对于大火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
作为主要注解,并在其中设置 @RequiredArgsConstructor
的 staticName
属性。例如:
java
java@AllArgsConstructor(onConstructor = @__(@Autowired), staticName = "of")
@RequiredArgsConstructor
public class MyClass {
// ...
}
这将保留 @AllArgsConstructor
的 onConstructor
参数,同时在 @RequiredArgsConstructor
中使用默认设置。同时,您可以使用 MyClass.of(...)
作为静态构造函数来创建对象。
问:听不懂
答:让我们先来看一个例子:
java
javapublic 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 类,它有两个构造方法,一个是按照 name
和 age
的顺序初始化,另一个是按照 age
和 name
的顺序初始化。这两个构造方法的参数列表是不同的,但是它们的名字是相同的。这种情况下,如果我们使用 @RequiredArgsConstructor(onConstructor = @Autowired)
来生成构造方法,会发生什么呢?答案是,它会报错,因为 @Autowired
的作用是让 Spring 自动注入依赖,而它只能用在方法上,所以它和构造方法同名会发生冲突。
为了避免这种冲突,Lombok 使用了一个下划线的方式,让我们可以在注解上指定一个别名来避免和其他注解发生冲突。在上面的例子中,如果我们使用 @RequiredArgsConstructor(onConstructor_ = @Autowired)
,就不会发生冲突了。
问:@RequiredArgsConstructor的作用
答:@RequiredArgsConstructor
是 Lombok 提供的注解,它可以自动为类生成一个包含常量或 final 属性的构造方法,从而避免了手写构造方法的麻烦。
通常情况下,如果一个 Java 类中有很多的成员变量需要赋值,我们需要手动编写构造函数。而使用 @RequiredArgsConstructor
可以省去这些手动编写构造函数的过程。
例如,下面的代码是手动编写的构造函数:
java
javapublic 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
javaimport 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
类,它依赖于MyRepository
和MyHelper
两个类,这两个类都有无参构造器和有参构造器,而它们的有参构造器需要通过Spring容器注入相应的依赖。
使用@RequiredArgsConstructor(onConstructor_ = {@Autowired})
可以为MyService
类自动生成一个带有两个参数的构造器,并且使用@Autowired
注解来将依赖注入到构造器中。同时,由于MyRepository
和MyHelper
类已经有了自己的构造器,使用onConstructor_
可以避免命名冲突,确保注入的构造器和需要注入的构造器不会混淆。
如果使用onConstructor
,则可能会发生命名冲突,因为需要注入的构造器和MyRepository
和MyHelper
已经有的构造器都需要命名为MyRepository
和MyHelper
。
问:在你的示例中,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;
}
这个类有三个字段,其中 myHelper
和 anotherHelper
的类型相同,都是 MyHelper
。如果我们使用默认的构造器生成方式,会生成以下构造器:
java
javapublic 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版本会在编译时将注解信息丢失,因此使用onConstructor
或onConstructor_
指定注解的语法可能有所不同。
问: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给出的呀?看起来是我们自己发现的结果并告诉了他,他才像恍然大悟一样给出了一连串正确答案。。。
本文作者:DingDangDog
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!