2023-03-29
温故知新
00
请注意,本文编写于 560 天前,最后修改于 276 天前,其中某些信息可能已经过时。

目录

简介
全部讨论记录
总结

简介

对于大火ChatGPT,个人感觉在有些问题上,他看起来很厉害,但在另一些问题上,他好像总是给出一些看起来很对的错误结论。

本文以ChatGPT来讨论@RequiredArgsConstructor(onConstructor_ = @Autowired)为例,看看他是有多么的 能说会道

全部讨论记录

  • 其中可能有部分错别字出现,但并未影响讨论结果。

问:@RequiredArgsConstructor(onConstructor_ = @Autowired) 为什么onConstructor后边要下_

答:onConstructor_ 是 Lombok 提供的一个注解属性,用于指定构造函数参数的赋值顺序。@RequiredArgsConstructor 注解会自动生成一个包含所有 @NonNull 注解的成员变量的构造函数,但这个构造函数参数的顺序是按照成员变量在类中声明的顺序。如果需要自定义构造函数参数的赋值顺序,则可以使用 onConstructor_ 属性来指定。

@RequiredArgsConstructor 中使用 onConstructor_ 属性时,需要在属性名称后面加上下划线,以避免与 Java 关键字冲突。所以,onConstructor_ 后面要加下划线。


问:java中由onConstructor关键字吗?

答: Java中并没有onConstructor这个关键字。

@RequiredArgsConstructor(onConstructor_ = @Autowired)这行代码是使用了Lombok的注解处理器。其中@RequiredArgsConstructor是Lombok中的一个注解,可以自动为类中的非空final字段生成对应的构造方法。在这里,onConstructor_是一个Lombok注解参数,用来表示对构造方法进行自定义处理,其中@Autowired表示自动注入。通过这种方式,可以简化Java代码,减少一些重复性的劳动。


问:@RequiredArgsConstructor(onConstructor_ = @Autowired) 和 @RequiredArgsConstructor(onConstructor = @Autowired) 哪个是正确的写法?为什么?

答:在 Lombok 中,使用 onConstructor_onConstructor 是等效的,都是用来指定构造函数上的注解,用于注入依赖。不同之处在于,onConstructor_ 是一个废弃的 API,Lombok 推荐使用 onConstructor 来指定构造函数上的注解。

所以,在使用 Lombok 时,建议使用 @RequiredArgsConstructor(onConstructor = @Autowired) 的写法。


问:@RequiredArgsConstructor(onConstructor = @Autowired)写法会报错:Incompatible types. Found: 'org.springframework.beans.factory.annotation.Autowired', required: 'lombok.RequiredArgsConstructor.AnyAnnotation[]'

答:@RequiredArgsConstructor(onConstructor = @Autowired)写法会报错,是因为 @RequiredArgsConstructor 注解中的 onConstructor 属性需要传入 lombok.RequiredArgsConstructor.AnyAnnotation[] 类型的参数,而 @Autowired 注解并不是该类型。

正确的写法应该是 @RequiredArgsConstructor(onConstructor_ = @Autowired.class),其中的 onConstructor_ 属性用于指定构造函数注入依赖时使用的注解。同时要注意加上 .class 来指定注解类型。


问:@RequiredArgsConstructor(onConstructor_ = @Autowired)才可以

答:是的,正确的写法是 @RequiredArgsConstructor(onConstructor_ = @Autowired),其中 onConstructor_ 表示使用下划线语法来避免与 lombok 中的其他注解发生冲突。


问:举个例子,onConstructor和谁有可能冲突

答:onConstructor 可能会与 Lombok 中的 @AllArgsConstructor(onConstructor = @__({@Deprecated})) 冲突。这是因为它们都使用了 onConstructor 参数。

如果您同时使用了 @AllArgsConstructor@RequiredArgsConstructor,并想在 @AllArgsConstructor 中使用 onConstructor 参数,那么您可以将 @AllArgsConstructor 作为主要注解,并在其中设置 @RequiredArgsConstructorstaticName 属性。例如:

java

java
@AllArgsConstructor(onConstructor = @__(@Autowired), staticName = "of") @RequiredArgsConstructor public class MyClass { // ... }

这将保留 @AllArgsConstructoronConstructor 参数,同时在 @RequiredArgsConstructor 中使用默认设置。同时,您可以使用 MyClass.of(...) 作为静态构造函数来创建对象。


问:听不懂

答:让我们先来看一个例子:

java

java
public class MyClass { private String name; private int age; public MyClass(String name, int age) { this.name = name; this.age = age; } public MyClass(int age, String name) { this.name = name; this.age = age; } }

这是一个简单的 Java 类,它有两个构造方法,一个是按照 nameage 的顺序初始化,另一个是按照 agename 的顺序初始化。这两个构造方法的参数列表是不同的,但是它们的名字是相同的。这种情况下,如果我们使用 @RequiredArgsConstructor(onConstructor = @Autowired) 来生成构造方法,会发生什么呢?答案是,它会报错,因为 @Autowired 的作用是让 Spring 自动注入依赖,而它只能用在方法上,所以它和构造方法同名会发生冲突。

为了避免这种冲突,Lombok 使用了一个下划线的方式,让我们可以在注解上指定一个别名来避免和其他注解发生冲突。在上面的例子中,如果我们使用 @RequiredArgsConstructor(onConstructor_ = @Autowired),就不会发生冲突了。


问:@RequiredArgsConstructor的作用

答:@RequiredArgsConstructor 是 Lombok 提供的注解,它可以自动为类生成一个包含常量或 final 属性的构造方法,从而避免了手写构造方法的麻烦。

通常情况下,如果一个 Java 类中有很多的成员变量需要赋值,我们需要手动编写构造函数。而使用 @RequiredArgsConstructor 可以省去这些手动编写构造函数的过程。

例如,下面的代码是手动编写的构造函数:

java

java
public class MyClass { private final String field1; private final int field2; private final double field3; public MyClass(String field1, int field2, double field3) { this.field1 = field1; this.field2 = field2; this.field3 = field3; } }

使用 @RequiredArgsConstructor 后,可以省去构造函数的编写:

java

java
@RequiredArgsConstructor public class MyClass { private final String field1; private final int field2; private final double field3; }

Lombok 会在编译期自动生成构造函数的代码。

@RequiredArgsConstructor 还有一些其他的参数,可以控制自动生成的构造函数的一些特性,比如是否包含某些字段、是否加上访问修饰符、是否生成一个带参数的 static 构造函数等。


问:我还是没有明白使用onConstructor_和onConstructor的区别

答:好的,让我再进一步解释一下。

@RequiredArgsConstructor(onConstructor = @Autowired)@RequiredArgsConstructor(onConstructor_ = @Autowired) 在实现上没有什么区别,它们都会自动生成一个带有 @Autowired 注解的构造函数。

区别在于,@RequiredArgsConstructor(onConstructor_ = @Autowired) 基于 Lombok 的一个特性:允许使用 _ 来避免在生成的构造函数中使用参数名作为变量名,因为参数名可能会与字段名冲突。这种情况下,使用 onConstructor_ 可以避免命名冲突。

这个 _ 只是一个惯例,实际上你可以用任何不会和合法的 Java 变量名冲突的字符来代替它,比如 onConstructorX

因此,如果你的代码中不需要使用 _ 来避免命名冲突,那么你可以使用 @RequiredArgsConstructor(onConstructor = @Autowired),否则你需要使用 @RequiredArgsConstructor(onConstructor_ = @Autowired)

希望这次解释更加清晰明了。


问:能给我个示例说明吗?它避免了谁和谁的命名冲突?

答:可以看下面这个示例:

java

java
import lombok.AllArgsConstructor; import lombok.NoArgsConstructor; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component @RequiredArgsConstructor(onConstructor_ = {@Autowired}) public class MyService { private final MyRepository myRepository; private final MyHelper myHelper; } @NoArgsConstructor @AllArgsConstructor class MyRepository { private String name; } @NoArgsConstructor @AllArgsConstructor class MyHelper { private String name; }

这里定义了一个MyService类,它依赖于MyRepositoryMyHelper两个类,这两个类都有无参构造器和有参构造器,而它们的有参构造器需要通过Spring容器注入相应的依赖。

使用@RequiredArgsConstructor(onConstructor_ = {@Autowired})可以为MyService类自动生成一个带有两个参数的构造器,并且使用@Autowired注解来将依赖注入到构造器中。同时,由于MyRepositoryMyHelper类已经有了自己的构造器,使用onConstructor_可以避免命名冲突,确保注入的构造器和需要注入的构造器不会混淆。

如果使用onConstructor,则可能会发生命名冲突,因为需要注入的构造器和MyRepositoryMyHelper已经有的构造器都需要命名为MyRepositoryMyHelper


问:在你的示例中,MyService 类中的构造器名字不应该是`MyService`吗?为什么可能与 MyRepository和MyHelper 冲突

答:非常抱歉,是我的笔误。MyService 的构造器名字应该是 MyService,因为它与类名相同。下面我将进行更正和说明:

@RequiredArgsConstructor 注解生成的构造器参数名默认是根据字段名生成的,如果有多个字段的类型相同,参数名也就相同了。这就有可能导致在构造器中命名冲突,因为不能有重复的参数名。

例如,假设我们有以下类:

java

java
@RequiredArgsConstructor(onConstructor_ = {@Autowired}) public class MyService { private final MyRepository myRepository; private final MyHelper myHelper; private final MyHelper anotherHelper; }

这个类有三个字段,其中 myHelperanotherHelper 的类型相同,都是 MyHelper。如果我们使用默认的构造器生成方式,会生成以下构造器:

java

java
public MyService(MyRepository myRepository, MyHelper myHelper, MyHelper myHelper1) { this.myRepository = myRepository; this.myHelper = myHelper; this.anotherHelper = myHelper1; }

这里就会有命名冲突,因为两个参数都叫做 myHelper。但是,如果我们使用 onConstructor_ 属性和 @Autowired 注解,就可以给这些参数指定不同的名字,避免命名冲突:

java

java
@RequiredArgsConstructor(onConstructor_ = {@Autowired}) public class MyService { private final MyRepository myRepository; private final MyHelper myHelper; private final MyHelper anotherHelper; public MyService(MyRepository myRepository, @Qualifier("myHelper") MyHelper myHelper, @Qualifier("anotherHelper") MyHelper anotherHelper) { this.myRepository = myRepository; this.myHelper = myHelper; this.anotherHelper = anotherHelper; } }

这里使用了 @Qualifier 注解来为参数指定了不同的名字,从而避免了命名冲突。


问:我觉得你说的不对,起码没有说服我

答:非常抱歉我的解释没有让您明白。我再次尝试解释一下。

在Java中,我们可以使用多个构造函数来创建类的对象。如果我们使用@RequiredArgsConstructor注解来自动生成一个构造函数,那么这个构造函数的名称将与类名相同。但是,如果我们在同一个类中使用多个@RequiredArgsConstructor注解来自动生成多个构造函数,那么所有这些构造函数的名称将相同,这就可能导致冲突。

为了避免这种冲突,Lombok允许我们使用onConstructor_参数来指定一个前缀,它将与构造函数名称组合在一起。例如,如果我们在MyService类中使用@RequiredArgsConstructor(onConstructor_=@Autowired)注解来生成一个构造函数,那么它的名称将是MyService_autowired,这将避免与其他使用@RequiredArgsConstructor注解生成的构造函数名称相冲突。

相反,如果我们使用onConstructor参数而不是onConstructor_,那么Lombok将使用与类名相同的名称作为构造函数名称,这将可能导致命名冲突。

希望这次解释能够让您理解。如果您还有任何疑问,请随时询问。


问:在源码注释中有说明:Any annotations listed here are put on the generated constructor. The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs). 这是什么意思?

答:这段注释的意思是,@RequiredArgsConstructor注解会将指定的注解放在生成的构造函数上。不同的JDK版本可能有不同的语法要求,这是为了解决javac的一些bug而采取的措施。具体而言,这是因为不同的JDK版本对于注解处理器有不同的实现方式,有些JDK版本会在编译时将注解信息丢失,因此使用onConstructoronConstructor_指定注解的语法可能有所不同。


问:from JDK8: @RequiredArgsConstructor(onConstructor_={@AnnotationsGohere}) // note the underscore after onConstructor. 这是不是说明JDK语法规定必须这么写,而不是你说的规避命名冲突?

答:您说得对,我之前的解释不是很准确。实际上,@RequiredArgsConstructor(onConstructor_ = @Autowired) 中的下划线是 Lombok 的语法规则,而不是 JDK 的规定,是用来规避命名冲突的。因此,在使用 onConstructor_ 时,必须在参数列表中加上下划线,而不能直接使用 onConstructor

对于 Lombok 的语法规则,它们的语法和行为可能会受到 JDK 版本的影响,具体可以参考 Lombok 的文档。


问:注释中不是说这是由于javac的bug才必须这么写吗

答:是的,Lombok 官方文档中的注释说明了这个问题是由于 javac 的 bug 引起的,所以才需要使用 onConstructor_ 而不是 onConstructor。在 JDK 8 及之前的版本中,当使用 onConstructor 时,由于 javac 的 bug,生成的代码中的注解可能会丢失,所以需要使用 onConstructor_ 来指定注解。而在 JDK 9 之后,这个问题已经被修复了,使用 onConstructor 也可以正确生成代码。

总结

经过漫长的讨论,终于得到了想要的答案,也就是最后一问ChatGPT给出的结果:

在 JDK 8 及之前的版本中,当使用 onConstructor 时,由于 javac 的 bug,生成的代码中的注解可能会丢失,所以需要使用 onConstructor_ 来指定注解。而在 JDK 9 之后,这个问题已经被修复了,使用 onConstructor 也可以正确生成代码。

但是好像并不是ChatGPT给出的呀?看起来是我们自己发现的结果并告诉了他,他才像恍然大悟一样给出了一连串正确答案。。。

如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:DingDangDog

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!