Lombok使用指南 lombok@builder
qiyuwang 2024-10-13 07:27 15 浏览 0 评论
作者 | Louis码农工具人
来源 | urlify.cn/fEJzUv
一、Lombok 简介
Lombok 是一款 Java 开发插件,使得 Java 开发者可以通过其定义的一些注解来消除业务工程中冗长和繁琐的代码,尤其对于简单的 Java 模型对象(POJO)。在开发环境中使用 Lombok 插件后,Java 开发人员可以节省出重复构建,诸如 hashCode 和 equals 这样的方法以及各种业务对象模型的 accessor 和 toString 等方法的大量时间。对于这些方法,Lombok 能够在编译源代码期间自动帮我们生成这些方法,但并不会像反射那样降低程序的性能。
二、Lombok 安装
2.1 构建工具
- Maven
在 Maven 项目的 pom.xml 文件中添加 Lombok 依赖:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
- Gradle
在 build.gradle 文件中添加 Lombok 依赖:
dependencies {
compileOnly 'org.projectlombok:lombok:1.18.10'
annotationProcessor 'org.projectlombok:lombok:1.18.10'
}
- Ant
假设在 lib 目录中已经存在 lombok.jar,然后设置 javac 任务:
<javac srcdir="src" destdir="build" source="1.8">
<classpath location="lib/lombok.jar" />
</javac>
2.2 IDE
由于 Lombok 仅在编译阶段生成代码,所以使用 Lombok 注解的源代码,在 IDE 中会被高亮显示错误,针对这个问题可以通过安装 IDE 对应的插件来解决。具体的安装方式可以参考:https://www.baeldung.com/lombok-ide。
Lombok in IntelliJ IDEA
Lombok in Eclipse
三、Lombok 详解
注解说明
- val:用在局部变量前面,相当于将变量声明为final
- @NonNull:给方法参数增加这个注解会自动在方法内对该参数进行是否为空的校验,如果为空,则抛出NPE(NullPointerException)
- @Cleanup:自动管理资源,用在局部变量之前,在当前变量范围内即将执行完毕退出之前会自动清理资源,自动生成try-finally这样的代码来关闭流
- @Getter/@Setter:用在属性上,再也不用自己手写setter和getter方法了,还可以指定访问范围
- @ToString:用在类上,可以自动覆写toString方法,当然还可以加其他参数,例如@ToString(exclude=”id”)排除id属性,或者@ToString(callSuper=true, includeFieldNames=true)调用父类的toString方法,包含所有属性
- @EqualsAndHashCode:用在类上,自动生成equals方法和hashCode方法
- @NoArgsConstructor, @RequiredArgsConstructor and @AllArgsConstructor:用在类上,自动生成无参构造和使用所有参数的构造函数以及把所有@NonNull属性作为参数的构造函数,如果指定staticName = “of”参数,同时还会生成一个返回类对象的静态工厂方法,比使用构造函数方便很多
- @Data:注解在类上,相当于同时使用了@ToString、@EqualsAndHashCode、@Getter、@Setter和@RequiredArgsConstrutor这些注解,对于POJO类十分有用
- @Value:用在类上,是@Data的不可变形式,相当于为属性添加final声明,只提供getter方法,而不提供setter方法
- @Builder:用在类、构造器、方法上,为你提供复杂的builder APIs,让你可以像如下方式一样调用Person.builder().name("Adam Savage").city("San Francisco").job("Mythbusters").job("Unchained Reaction").build();更多说明参考Builder
- @SneakyThrows:自动抛受检异常,而无需显式在方法上使用throws语句
- @Synchronized:用在方法上,将方法声明为同步的,并自动加锁,而锁对象是一个私有的属性$lock或$LOCK,而java中的synchronized关键字锁对象是this,锁在this或者自己的类对象上存在副作用,就是你不能阻止非受控代码去锁this或者类对象,这可能会导致竞争条件或者其它线程错误
- @Getter(lazy=true):可以替代经典的Double Check Lock样板代码
- @Log:根据不同的注解生成不同类型的log对象,但是实例名称都是log,有六种可选实现类
- @CommonsLog Creates log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);
- @Log Creates log = java.util.logging.Logger.getLogger(LogExample.class.getName());
- @Log4j Creates log = org.apache.log4j.Logger.getLogger(LogExample.class);
- @Log4j2 Creates log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
- @Slf4j Creates log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
- @XSlf4j Creates log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);
3.1 @Getter and @Setter
你可以使用 @Getter 或 @Setter 注释任何类或字段,Lombok 会自动生成默认的 getter/setter 方法。
@Getter
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface Getter {
// 若getter方法非public的话,可以设置可访问级别
lombok.AccessLevel value() default lombok.AccessLevel.PUBLIC;
AnyAnnotation[] onMethod() default {};
// 是否启用延迟初始化
boolean lazy() default false;
}
@Setter
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface Setter {
// 若setter方法非public的话,可以设置可访问级别
lombok.AccessLevel value() default lombok.AccessLevel.PUBLIC;
AnyAnnotation[] onMethod() default {};
AnyAnnotation[] onParam() default {};
}
使用示例
@Getter
@Setter
public class GetterAndSetterDemo {
String firstName;
String lastName;
LocalDate dateOfBirth;
}
以上代码经过 Lombok 编译后,会生成如下代码:
public class GetterAndSetterDemo {
String firstName;
String lastName;
LocalDate dateOfBirth;
public GetterAndSetterDemo() {
}
// 省略其它setter和getter方法
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
Lazy Getter
@Getter 注解支持一个 lazy 属性,该属性默认为 false。当设置为 true 时,会启用延迟初始化,即当首次调用 getter 方法时才进行初始化。
示例
public class LazyGetterDemo {
public static void main(String[] args) {
LazyGetterDemo m = new LazyGetterDemo();
System.out.println("Main instance is created");
m.getLazy();
}
@Getter
private final String notLazy = createValue("not lazy");
@Getter(lazy = true)
private final String lazy = createValue("lazy");
private String createValue(String name) {
System.out.println("createValue(" + name + ")");
return null;
}
}
以上代码经过 Lombok 编译后,会生成如下代码:
public class LazyGetterDemo {
private final String notLazy = this.createValue("not lazy");
private final AtomicReference<Object> lazy = new AtomicReference();
// 已省略部分代码
public String getNotLazy() {
return this.notLazy;
}
public String getLazy() {
Object value = this.lazy.get();
if (value == null) {
synchronized(this.lazy) {
value = this.lazy.get();
if (value == null) {
String actualValue = this.createValue("lazy");
value = actualValue == null ? this.lazy : actualValue;
this.lazy.set(value);
}
}
}
return (String)((String)(value == this.lazy ? null : value));
}
}
通过以上代码可知,调用 getLazy 方法时,若发现 value 为 null,则会在同步代码块中执行初始化操作。
3.2 Constructor Annotations
@NoArgsConstructor
使用 @NoArgsConstructor 注解可以为指定类,生成默认的构造函数,@NoArgsConstructor 注解的定义如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface NoArgsConstructor {
// 若设置该属性,将会生成一个私有的构造函数且生成一个staticName指定的静态方法
String staticName() default "";
AnyAnnotation[] onConstructor() default {};
// 设置生成构造函数的访问级别,默认是public
AccessLevel access() default lombok.AccessLevel.PUBLIC;
// 若设置为true,则初始化所有final的字段为0/null/false
boolean force() default false;
}
示例
@NoArgsConstructor(staticName = "getInstance")
public class NoArgsConstructorDemo {
private long id;
private String name;
private int age;
}
以上代码经过 Lombok 编译后,会生成如下代码:
public class NoArgsConstructorDemo {
private long id;
private String name;
private int age;
private NoArgsConstructorDemo() {
}
public static NoArgsConstructorDemo getInstance() {
return new NoArgsConstructorDemo();
}
}
@AllArgsConstructor
使用 @AllArgsConstructor 注解可以为指定类,生成包含所有成员的构造函数,@AllArgsConstructor 注解的定义如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface AllArgsConstructor {
// 若设置该属性,将会生成一个私有的构造函数且生成一个staticName指定的静态方法
String staticName() default "";
AnyAnnotation[] onConstructor() default {};
// 设置生成构造函数的访问级别,默认是public
AccessLevel access() default lombok.AccessLevel.PUBLIC;
}
示例
@AllArgsConstructor
public class AllArgsConstructorDemo {
private long id;
private String name;
private int age;
}
以上代码经过 Lombok 编译后,会生成如下代码:
public class AllArgsConstructorDemo {
private long id;
private String name;
private int age;
public AllArgsConstructorDemo(long id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
}
@RequiredArgsConstructor
使用 @RequiredArgsConstructor 注解可以为指定类必需初始化的成员变量,如 final 成员变量,生成对应的构造函数,@RequiredArgsConstructor 注解的定义如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface RequiredArgsConstructor {
// 若设置该属性,将会生成一个私有的构造函数且生成一个staticName指定的静态方法
String staticName() default "";
AnyAnnotation[] onConstructor() default {};
// 设置生成构造函数的访问级别,默认是public
AccessLevel access() default lombok.AccessLevel.PUBLIC;
}
示例
@RequiredArgsConstructor
public class RequiredArgsConstructorDemo {
private final long id;
private String name;
private int age;
}
以上代码经过 Lombok 编译后,会生成如下代码:
public class RequiredArgsConstructorDemo {
private final long id;
private String name;
private int age;
public RequiredArgsConstructorDemo(long id) {
this.id = id;
}
}
3.3 @EqualsAndHashCode
使用 @EqualsAndHashCode 注解可以为指定类生成 equals 和 hashCode 方法, @EqualsAndHashCode 注解的定义如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface EqualsAndHashCode {
// 指定在生成的equals和hashCode方法中需要排除的字段列表
String[] exclude() default {};
// 显式列出用于identity的字段,一般情况下non-static,non-transient字段会被用于identity
String[] of() default {};
// 标识在执行字段计算前,是否调用父类的equals和hashCode方法
boolean callSuper() default false;
boolean doNotUseGetters() default false;
AnyAnnotation[] onParam() default {};
@Deprecated
@Retention(RetentionPolicy.SOURCE)
@Target({})
@interface AnyAnnotation {}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface Exclude {}
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface Include {
String replaces() default "";
}
}
示例
public class EqualsAndHashCodeDemo {
String firstName;
String lastName;
LocalDate dateOfBirth;
}
以上代码经过 Lombok 编译后,会生成如下代码:
public class EqualsAndHashCodeDemo {
String firstName;
String lastName;
LocalDate dateOfBirth;
public EqualsAndHashCodeDemo() {
}
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof EqualsAndHashCodeDemo)) {
return false;
} else {
EqualsAndHashCodeDemo other = (EqualsAndHashCodeDemo)o;
if (!other.canEqual(this)) {
return false;
} else {
// 已省略大量代码
}
}
public int hashCode() {
int PRIME = true;
int result = 1;
Object $firstName = this.firstName;
int result = result * 59 + ($firstName == null ? 43 : $firstName.hashCode());
Object $lastName = this.lastName;
result = result * 59 + ($lastName == null ? 43 : $lastName.hashCode());
Object $dateOfBirth = this.dateOfBirth;
result = result * 59 + ($dateOfBirth == null ? 43 : $dateOfBirth.hashCode());
return result;
}
}
3.4 @ToString
使用 @ToString 注解可以为指定类生成 toString 方法, @ToString 注解的定义如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface ToString {
// 打印输出时是否包含字段的名称
boolean includeFieldNames() default true;
// 列出打印输出时,需要排除的字段列表
String[] exclude() default {};
// 显式的列出需要打印输出的字段列表
String[] of() default {};
// 打印输出的结果中是否包含父类的toString方法的返回结果
boolean callSuper() default false;
boolean doNotUseGetters() default false;
boolean onlyExplicitlyIncluded() default false;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface Exclude {}
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface Include {
int rank() default 0;
String name() default "";
}
}
示例
@ToString(exclude = {"dateOfBirth"})
public class ToStringDemo {
String firstName;
String lastName;
LocalDate dateOfBirth;
}
以上代码经过 Lombok 编译后,会生成如下代码:
public class ToStringDemo {
String firstName;
String lastName;
LocalDate dateOfBirth;
public ToStringDemo() {
}
public String toString() {
return "ToStringDemo(firstName=" + this.firstName + ", lastName=" + this.lastName + ")";
}
}
3.5 @Data
@Data 注解与同时使用以下的注解的效果是一样的:
@ToString
@Getter
@Setter
@RequiredArgsConstructor
@EqualsAndHashCode
@Data 注解的定义如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface Data {
String staticConstructor() default "";
}
示例
@Data
public class DataDemo {
private Long id;
private String summary;
private String description;
}
以上代码经过 Lombok 编译后,会生成如下代码:
public class DataDemo {
private Long id;
private String summary;
private String description;
public DataDemo() {
}
// 省略summary和description成员属性的setter和getter方法
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof DataDemo)) {
return false;
} else {
DataDemo other = (DataDemo)o;
if (!other.canEqual(this)) {
return false;
} else {
// 已省略大量代码
}
}
}
protected boolean canEqual(Object other) {
return other instanceof DataDemo;
}
public int hashCode() {
int PRIME = true;
int result = 1;
Object $id = this.getId();
int result = result * 59 + ($id == null ? 43 : $id.hashCode());
Object $summary = this.getSummary();
result = result * 59 + ($summary == null ? 43 : $summary.hashCode());
Object $description = this.getDescription();
result = result * 59 + ($description == null ? 43 : $description.hashCode());
return result;
}
public String toString() {
return "DataDemo(id=" + this.getId() + ", summary=" + this.getSummary() + ", description=" + this.getDescription() + ")";
}
}
3.6 @Log
若你将 @Log 的变体放在类上(适用于你所使用的日志记录系统的任何一种);之后,你将拥有一个静态的 final log 字段,然后你就可以使用该字段来输出日志。
@Log
private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());
@Log4j
private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class);
@Log4j2
private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
@Slf4j
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
@XSlf4j
private static final org.slf4j.ext.XLogger log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);
@CommonsLog
private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);
3.7 @Synchronized
@Synchronized 是同步方法修饰符的更安全的变体。与 synchronized 一样,该注解只能应用在静态和实例方法上。它的操作类似于 synchronized 关键字,但是它锁定在不同的对象上。synchronized 关键字应用在实例方法时,锁定的是 this 对象,而应用在静态方法上锁定的是类对象。对于 @Synchronized 注解声明的方法来说,它锁定的是
或lock。@Synchronized 注解的定义如下:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Synchronized {
// 指定锁定的字段名称
String value() default "";
}
示例
public class SynchronizedDemo {
private final Object readLock = new Object();
@Synchronized
public static void hello() {
System.out.println("world");
}
@Synchronized
public int answerToLife() {
return 42;
}
@Synchronized("readLock")
public void foo() {
System.out.println("bar");
}
}
以上代码经过 Lombok 编译后,会生成如下代码:
public class SynchronizedDemo {
private static final Object $LOCK = new Object[0];
private final Object $lock = new Object[0];
private final Object readLock = new Object();
public SynchronizedDemo() {
}
public static void hello() {
synchronized($LOCK) {
System.out.println("world");
}
}
public int answerToLife() {
synchronized(this.$lock) {
return 42;
}
}
public void foo() {
synchronized(this.readLock) {
System.out.println("bar");
}
}
}
3.8 @Builder
使用 @Builder 注解可以为指定类实现建造者模式,该注解可以放在类、构造函数或方法上。@Builder 注解的定义如下:
@Target({TYPE, METHOD, CONSTRUCTOR})
@Retention(SOURCE)
public @interface Builder {
@Target(FIELD)
@Retention(SOURCE)
public @interface Default {}
// 创建新的builder实例的方法名称
String builderMethodName() default "builder";
// 创建Builder注解类对应实例的方法名称
String buildMethodName() default "build";
// builder类的名称
String builderClassName() default "";
boolean toBuilder() default false;
AccessLevel access() default lombok.AccessLevel.PUBLIC;
@Target({FIELD, PARAMETER})
@Retention(SOURCE)
public @interface ObtainVia {
String field() default "";
String method() default "";
boolean isStatic() default false;
}
}
示例
@Builder
public class BuilderDemo {
private final String firstname;
private final String lastname;
private final String email;
}
以上代码经过 Lombok 编译后,会生成如下代码:
public class BuilderDemo {
private final String firstname;
private final String lastname;
private final String email;
BuilderDemo(String firstname, String lastname, String email) {
this.firstname = firstname;
this.lastname = lastname;
this.email = email;
}
public static BuilderDemo.BuilderDemoBuilder builder() {
return new BuilderDemo.BuilderDemoBuilder();
}
public static class BuilderDemoBuilder {
private String firstname;
private String lastname;
private String email;
BuilderDemoBuilder() {
}
public BuilderDemo.BuilderDemoBuilder firstname(String firstname) {
this.firstname = firstname;
return this;
}
public BuilderDemo.BuilderDemoBuilder lastname(String lastname) {
this.lastname = lastname;
return this;
}
public BuilderDemo.BuilderDemoBuilder email(String email) {
this.email = email;
return this;
}
public BuilderDemo build() {
return new BuilderDemo(this.firstname, this.lastname, this.email);
}
public String toString() {
return "BuilderDemo.BuilderDemoBuilder(firstname=" + this.firstname + ", lastname=" + this.lastname + ", email=" + this.email + ")";
}
}
}
3.9 @SneakyThrows
@SneakyThrows 注解用于自动抛出已检查的异常,而无需在方法中使用 throw 语句显式抛出。@SneakyThrows 注解的定义如下:
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.SOURCE)
public @interface SneakyThrows {
// 设置你希望向上抛的异常类
Class<? extends Throwable>[] value() default java.lang.Throwable.class;
}
示例
public class SneakyThrowsDemo {
@SneakyThrows
@Override
protected Object clone() {
return super.clone();
}
}
以上代码经过 Lombok 编译后,会生成如下代码:
public class SneakyThrowsDemo {
public SneakyThrowsDemo() {
}
protected Object clone() {
try {
return super.clone();
} catch (Throwable var2) {
throw var2;
}
}
}
3.10 @NonNull
你可以在方法或构造函数的参数上使用 @NonNull 注解,它将会为你自动生成非空校验语句。@NonNull 注解的定义如下:
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE, ElementType.TYPE_USE})
@Retention(RetentionPolicy.CLASS)
@Documented
public @interface NonNull {
}
示例
public class NonNullDemo {
@Getter
@Setter
@NonNull
private String name;
}
以上代码经过 Lombok 编译后,会生成如下代码:
public class NonNullDemo {
@NonNull
private String name;
public NonNullDemo() {
}
@NonNull
public String getName() {
return this.name;
}
public void setName(@NonNull String name) {
if (name == null) {
throw new NullPointerException("name is marked non-null but is null");
} else {
this.name = name;
}
}
}
3.11 @Clean
@Clean 注解用于自动管理资源,用在局部变量之前,在当前变量范围内即将执行完毕退出之前会自动清理资源,自动生成 try-finally 这样的代码来关闭流。
@Target(ElementType.LOCAL_VARIABLE)
@Retention(RetentionPolicy.SOURCE)
public @interface Cleanup {
// 设置用于执行资源清理/回收的方法名称,对应方法不能包含任何参数,默认名称为close。
String value() default "close";
}
示例
public class CleanupDemo {
public static void main(String[] args) throws IOException {
@Cleanup InputStream in = new FileInputStream(args[0]);
@Cleanup OutputStream out = new FileOutputStream(args[1]);
byte[] b = new byte[10000];
while (true) {
int r = in.read(b);
if (r == -1) break;
out.write(b, 0, r);
}
}
}
以上代码经过 Lombok 编译后,会生成如下代码:
public class CleanupDemo {
public CleanupDemo() {
}
public static void main(String[] args) throws IOException {
FileInputStream in = new FileInputStream(args[0]);
try {
FileOutputStream out = new FileOutputStream(args[1]);
try {
byte[] b = new byte[10000];
while(true) {
int r = in.read(b);
if (r == -1) {
return;
}
out.write(b, 0, r);
}
} finally {
if (Collections.singletonList(out).get(0) != null) {
out.close();
}
}
} finally {
if (Collections.singletonList(in).get(0) != null) {
in.close();
}
}
}
}
3.11 @With
在类的字段上应用 @With 注解之后,将会自动生成一个 withFieldName(newValue) 的方法,该方法会基于 newValue 调用相应构造函数,创建一个当前类对应的实例。@With 注解的定义如下:
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface With {
AccessLevel value() default AccessLevel.PUBLIC;
With.AnyAnnotation[] onMethod() default {};
With.AnyAnnotation[] onParam() default {};
@Deprecated
@Retention(RetentionPolicy.SOURCE)
@Target({})
public @interface AnyAnnotation {
}
}
示例
public class WithDemo {
@With(AccessLevel.PROTECTED)
@NonNull
private final String name;
@With
private final int age;
public WithDemo(String name, int age) {
if (name == null) throw new NullPointerException();
this.name = name;
this.age = age;
}
}
以上代码经过 Lombok 编译后,会生成如下代码:
public class WithDemo {
@NonNull
private final String name;
private final int age;
public WithDemo(String name, int age) {
if (name == null) {
throw new NullPointerException();
} else {
this.name = name;
this.age = age;
}
}
protected WithDemo withName(@NonNull String name) {
if (name == null) {
throw new NullPointerException("name is marked non-null but is null");
} else {
return this.name == name ? this : new WithDemo(name, this.age);
}
}
public WithDemo withAge(int age) {
return this.age == age ? this : new WithDemo(this.name, age);
}
}
3.12 其它特性
val
val 用在局部变量前面,相当于将变量声明为 final,此外 Lombok 在编译时还会自动进行类型推断。val 的使用示例:
public class ValExample {
public String example() {
val example = new ArrayList<String>();
example.add("Hello, World!");
val foo = example.get(0);
return foo.toLowerCase();
}
public void example2() {
val map = new HashMap<Integer, String>();
map.put(0, "zero");
map.put(5, "five");
for (val entry : map.entrySet()) {
System.out.printf("%d: %s\n", entry.getKey(), entry.getValue());
}
}
}
以上代码等价于:
public class ValExample {
public String example() {
final ArrayList<String> example = new ArrayList<String>();
example.add("Hello, World!");
final String foo = example.get(0);
return foo.toLowerCase();
}
public void example2() {
final HashMap<Integer, String> map = new HashMap<Integer, String>();
map.put(0, "zero");
map.put(5, "five");
for (final Map.Entry<Integer, String> entry : map.entrySet()) {
System.out.printf("%d: %s\n", entry.getKey(), entry.getValue());
}
}
}
四、Lombok注解原理
说到 Lombok,我们就得去提到 JSR 269: Pluggable Annotation Processing API (https://www.jcp.org/en/jsr/detail?id=269) 。JSR 269 之前我们也有注解这样的神器,可是我们比如想要做什么必须使用反射,反射的方法局限性较大。首先,它必须定义@Retention为RetentionPolicy.RUNTIME,只能在运行时通过反射来获取注解值,使得运行时代码效率降低。其次,如果想在编译阶段利用注解来进行一些检查,对用户的某些不合理代码给出错误报告,反射的使用方法就无能为力了。而 JSR 269 之后我们可以在 Javac的编译期利用注解做这些事情。所以我们发现核心的区分是在 运行期 还是 编译期。
从上图可知,Annotation Processing 是在解析和生成之间的一个步骤。具体详细步骤如下:
上图是 Lombok 处理流程,在Javac 解析成抽象语法树之后(AST), Lombok 根据自己的注解处理器,动态的修改 AST,增加新的节点(所谓代码),最终通过分析和生成字节码。
自从Java 6起,javac就支持“JSR 269 Pluggable Annotation Processing API”规范,只要程序实现了该API,就能在javac运行的时候得到调用。
常用的项目管理工具Maven所使用的java编译工具来源于配置的第三方工具,如果我们配置这个第三方工具为Oracle javac的话,那么Maven也就直接支持lombok了;
Intellij Idea配置的编译工具为Oracle javac的话,也就直接支持lombok了。
IDE工具问题解决:
现在有一个A类,其中有一些字段,没有创建它们的setter和getter方法,使用了lombok的@Data注解,另外有一个B类,它调用了A类实例的相应字段的setter和getter方法
编译A类和B类所在的项目,并不会报错,因为最终生成的A类字节码文件中存在相应字段的setter和getter方法
但是,IDE发现B类源代码中所使用的A类实例的setter和getter方法在A类源代码中找不到定义,IDE会认为这是错误
要解决以上这个不是真正错误的错误,可以下载安装Intellij Idea中的"Lombok plugin"。
相关推荐
- # 安装打开 ubuntu-22.04.3-LTS 报错 解决方案
-
#安装打开ubuntu-22.04.3-LTS报错解决方案WslRegisterDistributionfailedwitherror:0x800701bcError:0x80070...
- 利用阿里云镜像在ubuntu上安装Docker
-
简介:...
- 如何将Ubuntu Kylin(优麒麟)19.10系统升级到20.04版本
-
UbuntuKylin系统使用一段时间后,有新的版本发布,如何将现有的UbuntuKylin系统升级到最新版本?可以通过下面的方法进行升级。1.先查看相关的UbuntuKylin系统版本情况。使...
- Ubuntu 16.10内部代号确认为Yakkety Yak
-
在正式宣布Ubuntu16.04LTS(XenialXerus)的当天,Canonical创始人MarkShuttleworth还非常开心的在个人微博上宣布Ubuntu下个版本16.10的内...
- 如何在win11的wsl上装ubuntu(怎么在windows上安装ubuntu)
-
在Windows11的WSL(WindowsSubsystemforLinux)上安装Ubuntu非常简单。以下是详细的步骤:---...
- Win11学院:如何在Windows 11上使用WSL安装Ubuntu
-
IT之家2月18日消息,科技媒体pureinfotech昨日(2月17日)发布博文,介绍了3中简便的方法,让你轻松在Windows11系统中,使用WindowsSubs...
- 如何查看Linux的IP地址(如何查看Linux的ip地址)
-
本头条号每天坚持更新原创干货技术文章,欢迎关注本头条号"Linux学习教程",公众号名称“Linux入门学习教程"。...
- 怎么看电脑系统?(怎么看电脑系统配置)
-
要查看电脑的操作系统信息,可以按照以下步骤操作,根据不同的操作系统选择对应的方法:一、Windows系统通过系统属性查看右键点击桌面上的“此电脑”(或“我的电脑”)图标,选择“属性”。在打开的...
- 如何查询 Linux 内核版本?这些命令一定要会!
-
Linux内核是操作系统的核心,负责管理硬件资源、调度进程、处理系统调用等关键任务。不同的内核版本可能支持不同的硬件特性、提供新的功能,或者修复了已知的安全漏洞。以下是查询内核版本的几个常见场景:...
- 深度剖析:Linux下查看系统版本与CPU架构
-
在Linux系统管理、维护以及软件部署的过程中,精准掌握系统版本和CPU架构是极为关键的基础操作。这些信息不仅有助于我们深入了解系统特性、判断软件兼容性,还能为后续的软件安装、性能优化提供重要依据。接...
- 504 错误代码解析与应对策略(504错误咋解决)
-
在互联网的使用过程中,用户偶尔会遭遇各种错误提示,其中504错误代码是较为常见的一种。504错误并非意味着网站被屏蔽,它实际上是指服务器在规定时间内未能从上游服务器获取响应,专业术语称为“Ga...
- 猎聘APP和官网崩了?回应:正对部分职位整改,临时域名可登录
-
10月12日,有网友反映猎聘网无法打开,猎聘APP无法登录。截至10月14日,仍有网友不断向猎聘官方微博下反映该情况,而猎聘官方微博未发布相关情况说明,只是在微博内对反映该情况的用户进行回复,“抱歉,...
- 域名解析的原理是什么?域名解析的流程是怎样的?
-
域名解析是网站正常运行的关键因素,因此网站管理者了解域名解析的原理和流程对于做好域名管理、解决常见解析问题,保障网站的正常运转十分必要。那么域名解析的原理是什么?域名解析的流程是怎样的?接下来,中科三...
- Linux无法解析域名的解决办法(linux 不能解析域名)
-
如果由于误操作,删除了系统原有的dhcp相关设置就无法正常解析域名。 此时,需要手动修改配置文件: /etc/resolv.conf 将域名解析服务器手动添加到配置文件中 该文件是DNS域名解...
- 域名劫持是什么?(域名劫持是什么)
-
域名劫持是互联网攻击的一种方式,通过攻击域名解析服务器(DNS),或伪造域名解析服务器(DNS)的方法,把目标网站域名解析到错误的地址从而实现用户无法访问目标网站的目的。说的直白些,域名劫持,就是把互...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- # 安装打开 ubuntu-22.04.3-LTS 报错 解决方案
- 利用阿里云镜像在ubuntu上安装Docker
- 如何将Ubuntu Kylin(优麒麟)19.10系统升级到20.04版本
- Ubuntu 16.10内部代号确认为Yakkety Yak
- 如何在win11的wsl上装ubuntu(怎么在windows上安装ubuntu)
- Win11学院:如何在Windows 11上使用WSL安装Ubuntu
- 如何查看Linux的IP地址(如何查看Linux的ip地址)
- 怎么看电脑系统?(怎么看电脑系统配置)
- 如何查询 Linux 内核版本?这些命令一定要会!
- 深度剖析:Linux下查看系统版本与CPU架构
- 标签列表
-
- navicat无法连接mysql服务器 (65)
- 下横线怎么打 (71)
- flash插件怎么安装 (60)
- lol体验服怎么进 (66)
- ae插件怎么安装 (62)
- yum卸载 (75)
- .key文件 (63)
- cad一打开就致命错误是怎么回事 (61)
- rpm文件怎么安装 (66)
- linux取消挂载 (81)
- ie代理配置错误 (61)
- ajax error (67)
- centos7 重启网络 (67)
- centos6下载 (58)
- mysql 外网访问权限 (69)
- centos查看内核版本 (61)
- ps错误16 (66)
- nodejs读取json文件 (64)
- centos7 1810 (59)
- 加载com加载项时运行错误 (67)
- php打乱数组顺序 (68)
- cad安装失败怎么解决 (58)
- 因文件头错误而不能打开怎么解决 (68)
- js判断字符串为空 (62)
- centos查看端口 (64)