Lombok介绍、操作方法和归纳转载
原创同步启动: http://www.yuanrengu.com/index.php/20180324.html
1 Lombok背景介绍
官方介绍如下:
Project Lombok makes java a spicier language by adding handlers that know how to build and compile simple, boilerplate-free, not-quite-java code.
一般意义是Lombok通过添加一些“处理程序”,您可以。java变得简洁和快速。
2 Lombok使用方法
Lombok可以简化为简单注释的形式java提高开发人员开发效率的代码。例如,通常需要在开发中编写。javabean,添加相应的getter/setter,可能还必须编写构造函数,equals等等,并且需要维护,当有很多属性时会出现大量属性。getter/setter这些方法看起来很长,没有太多的技术含量。一旦修改了属性,就很容易犯忘记修改相应方法的错误。
Lombok构造函数可以在编译时通过注释为属性自动生成,getter/setter、equals、hashcode、toString方法神奇的是没有getter和setter方法,但在编译的字节码文件中有。getter和setter方法这避免了手动重新构建代码的麻烦,并使其看起来更加简洁。
Lombok使用和参考jar和套餐一样,可以在官方网站上找到( https://projectlombok.org/download )下载jar包装,也可以使用。maven添加依赖项:
接下来,让我们分析Lombok中注释的具体用法。
2.1 @Data
@Data为类上的类的所有属性自动生成注释。setter/getter、equals、canEqual、hashCode、toString方法,作为final属性,则不会为该属性生成。setter方法。
官方示例如下:
import lombok.AccessLevel; import lombok.Setter; import lombok.Data; import lombok.ToString;
@Data public class DataExample { private final String name; @Setter(AccessLevel.PACKAGE) private int age; private double score; private String[] tags;
@ToString(includeFieldNames=true)
@Data(staticConstructor="of")
public static class Exercise
如不使用Lombok,则实现如下:
import java.util.Arrays;
public class DataExample { private final String name; private int age; private double score; private String[] tags;
public DataExample(String name) { this.name = name; }
public String getName() { return this.name; }
void setAge(int age) { this.age = age; }
public int getAge() { return this.age; }
public void setScore(double score) { this.score = score; }
public double getScore() { return this.score; }
public String[] getTags() { return this.tags; }
public void setTags(String[] tags) { this.tags = tags; }
@Override public String toString() { return "DataExample(" + this.getName() + ", " + this.getAge() + ", " + this.getScore() + ", " + Arrays.deepToString(this.getTags()) + ")"; }
protected boolean canEqual(Object other) { return other instanceof DataExample; }
@Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof DataExample)) return false; DataExample other = (DataExample) o; if (!other.canEqual((Object)this)) return false; if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false; if (this.getAge() != other.getAge()) return false; if (Double.compare(this.getScore(), other.getScore()) != 0) return false; if (!Arrays.deepEquals(this.getTags(), other.getTags())) return false; return true; }
@Override public int hashCode() { final int PRIME = 59; int result = 1; final long temp1 = Double.doubleToLongBits(this.getScore()); result = (resultPRIME) + (this.getName() == null ? 43 : this.getName().hashCode()); result = (resultPRIME) + this.getAge(); result = (resultPRIME) + (int)(temp1 ^ (temp1 >>> 32)); result = (resultPRIME) + Arrays.deepHashCode(this.getTags()); return result; }
public static class Exercise
private Exercise(String name, T value) {
this.name = name;
this.value = value;
}
public static Exercise of(String name, T value) {
return new Exercise(name, value);
}
public String getName() {
return this.name;
}
public T getValue() {
return this.value;
}
@Override public String toString() {
return "Exercise(name=" + this.getName() + ", value=" + this.getValue() + ")";
}
protected boolean canEqual(Object other) {
return other instanceof Exercise;
}
@Override public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Exercise)) return false;
Exercise> other = (Exercise>) o;
if (!other.canEqual((Object)this)) return false;
if (this.getName() == null ? other.getValue() != null : !this.getName().equals(other.getName())) return false;
if (this.getValue() == null ? other.getValue() != null : !this.getValue().equals(other.getValue())) return false;
return true;
}
@Override public int hashCode() {
final int PRIME = 59;
int result = 1;
result = (result*PRIME) + (this.getName() == null ? 43 : this.getName().hashCode());
result = (result*PRIME) + (this.getValue() == null ? 43 : this.getValue().hashCode());
return result;
}
} }
2.2 @Getter/@Setter
如果觉得@Data太残忍了(因为@Data集合了@ToString、@EqualsAndHashCode、@Getter/@Setter、@RequiredArgsConstructor所有特征)都不够精细,无法使用。@Getter/@Setter注释,可以为属性上的相应属性自动生成Getter/Setter方法,示例如下:
import lombok.AccessLevel; import lombok.Getter; import lombok.Setter;
public class GetterSetterExample {
@Getter @Setter private int age = 10;
@Setter(AccessLevel.PROTECTED) private String name;
@Override public String toString() { return String.format("%s (age: %d)", name, age); } }
如果未使用Lombok:
public class GetterSetterExample {
private int age = 10;
private String name;
@Override public String toString() { return String.format("%s (age: %d)", name, age); }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
protected void setName(String name) { this.name = name; } }
2.3 @NonNull
此注释用于属性或构造函数,Lombok生成非空声明,可用于检查参数并帮助避免空指针。
示例如下:
import lombok.NonNull;
public class NonNullExample extends Something { private String name;
public NonNullExample(@NonNull Person person) { super("Hello"); this.name = person.getName(); } }
不使用Lombok:
import lombok.NonNull;
public class NonNullExample extends Something { private String name;
public NonNullExample(@NonNull Person person) { super("Hello"); if (person == null) { throw new NullPointerException("person"); } this.name = person.getName(); } }
2.4 @Cleanup
这个注释可以帮助我们自动调用它。close()方法,大大简化了代码。
示例如下:
import lombok.Cleanup; import java.io.*;
public class CleanupExample { 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,需要以下内容:
import java.io.*;
public class CleanupExample { public static void main(String[] args) throws IOException { InputStream in = new FileInputStream(args[0]); try { OutputStream out = new FileOutputStream(args[1]); try { byte[] b = new byte[10000]; while (true) { int r = in.read(b); if (r == -1) break; out.write(b, 0, r); } } finally { if (out != null) { out.close(); } } } finally { if (in != null) { in.close(); } } } }
2.5 @EqualsAndHashCode
默认情况下,所有非静态(non-static)和非瞬态(non-transient)要生成的属性equals和hasCode,也可以通过。exclude注释以排除某些属性。
示例如下:
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(exclude={"id", "shape"}) public class EqualsAndHashCodeExample { private transient int transientVar = 10; private String name; private double score; private Shape shape = new Square(5, 10); private String[] tags; private int id;
public String getName() { return this.name; }
@EqualsAndHashCode(callSuper=true) public static class Square extends Shape { private final int width, height;
public Square(int width, int height) {
this.width = width;
this.height = height;
}
} }
2.6 @ToString
类使用@ToString注解,Lombok将生成toString()方法,默认情况下,类名和所有属性(按定义属性的顺序)都将输出并用逗号分隔。
通过将 includeFieldNames
参数设为true,您可以清楚地输出。toString()属性这是不是有点绕道,通过代码会更清楚。
使用Lombok的示例:
import lombok.ToString;
@ToString(exclude="id") public class ToStringExample { private static final int STATIC_VAR = 10; private String name; private Shape shape = new Square(5, 10); private String[] tags; private int id;
public String getName() { return this.getName(); }
@ToString(callSuper=true, includeFieldNames=true) public static class Square extends Shape { private final int width, height;
public Square(int width, int height) {
this.width = width;
this.height = height;
}
} }
不使用Lombok的示例如下:
import java.util.Arrays;
public class ToStringExample { private static final int STATIC_VAR = 10; private String name; private Shape shape = new Square(5, 10); private String[] tags; private int id;
public String getName() { return this.getName(); }
public static class Square extends Shape { private final int width, height;
public Square(int width, int height) {
this.width = width;
this.height = height;
}
@Override public String toString() {
return "Square(super=" + super.toString() + ", width=" + this.width + ", height=" + this.height + ")";
}
}
@Override public String toString() { return "ToStringExample(" + this.getName() + ", " + this.shape + ", " + Arrays.deepToString(this.tags) + ")"; } }
2.7 @NoArgsConstructor, @RequiredArgsConstructor and @AllArgsConstructor
无参数构造函数、部分参数构造函数、完全参数构造函数。Lombok无法重载多个参数构造函数。
Lombok示例代码如下:
import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import lombok.AllArgsConstructor; import lombok.NonNull;
@RequiredArgsConstructor(staticName = "of")
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class ConstructorExample
@NoArgsConstructor public static class NoArgsExample { @NonNull private String field; } }
不使用Lombok的示例如下:
public class ConstructorExample
private ConstructorExample(T description) { if (description == null) throw new NullPointerException("description"); this.description = description; }
public static
@java.beans.ConstructorProperties({"x", "y", "description"}) protected ConstructorExample(int x, int y, T description) { if (description == null) throw new NullPointerException("description"); this.x = x; this.y = y; this.description = description; }
public static class NoArgsExample { @NonNull private String field;
public NoArgsExample() {
}
} }
3 Lombok工作原理分析
会发现在Lombok在使用过程中,只需要添加相应的注释,不需要为此编写更多的代码。自动生成的代码究竟是如何生成的?
核心是注释的解析。JDK5在介绍注释的同时,还提供了两种解析方法。
- 运行时分辨率
可以在运行时解析的注释必须是@Retention设置为RUNTIME,从而可以通过反射获得注释。java.lang,reflect反射包中提供了接口。AnnotatedElement该接口定义了几种用于获取注释信息的方法,Class、Constructor、Field、Method、Package接口已经实现,熟悉反射的朋友应该熟悉这种解析方法。
- 编译时分析
编译时分析有两种机制,分别简单描述下:
1)Annotation Processing Tool
apt自JDK5产生,JDK7标记为过期,不推荐,JDK8它已从中完全删除JDK6首先,您可以使用Pluggable Annotation Processing API为了替换它,apt主要更换2点原因:
- api都在com.sun.mirror非标准包装下
- 未集成到javac在中,需要其他操作。
2)Pluggable Annotation Processing API
JSR 269 自JDK6参加apt备选方案,解决了apt在两个问题中,javac在执行时,调用实现。API现在我们可以对编译器进行一些增强。javac执行的过程如下:
Lombok本质上是“ JSR 269 API “程序。使用中javac其工作的具体过程如下:
- javac分析源代码以生成抽象语法树(AST)
- 运行期间的调用实现“JSR 269 API”的Lombok程序
- 此时Lombok这是第一步。AST进程,查找@Data与注释所在的类相对应的语法树(AST),然后修改语法树(AST),增加getter和setter方法定义的相应树节点。
- javac使用修改后的抽象语法树(AST)生成给定的字节码文件。class添加新节点(代码块)
拜读了Lombok源代码,相应注释的实现在中。HandleXXX中,比如@Getter实现注释时HandleGetter.handle()。还有其他类库使用这种方法,例如 Google Auto 、 Dagger 等等。
-
Lombok的优缺点
优点:
- 构造函数可以以注释的形式自动生成,getter/setter、equals、hashcode、toString以及其他方法,在一定程度上提高了开发效率。
- 使代码简明扼要,不要过分关注相应的方法。
- 修改属性时,还可以简化为这些属性生成的的维护。getter/setter方法等
缺点:
-
不支持重载多参数构造函数。
-
尽管省略了手动创建getter/setter该方法很繁琐,但大大降低了源代码的可读性和完整性,降低了阅读源代码的舒适度。
-
总结
Lombok尽管有许多优点,Lombok更类似于IDE插件,项目也需要依赖相应的jar包。Lombok依赖jar包是用其注释编译的。为什么它类似于插件?因为在使用中,eclipse或IntelliJ IDEA所有这些都需要安装相应的插件,这些插件在编译器编译期间运行。AST(抽象语法树)改变字节码的生成,改变方向意味着它正在改变。java语法这不像spring依赖注入或mybatis的ORM这也是一个运行时特性,但却是一个编译时特性。在这里,我个人感觉最不舒服的地方是对插件的依赖!因为Lombok只需省去手动生成代码的麻烦,IDE有快捷键可帮助生成getter/setter它也很方便。
知乎上有一位大神表达了他的权利Lombok一些观点:
这是一个便宜的插件,不推荐使用。JAVA随着今天的发展,各种插件层出不穷。如何识别各种插件的优缺点?可以优化设计架构并提高应用程序性能 , 实现高度封装的可扩展性..., 像lombok这种插件,就像这种插件一样,不仅改变了你编写源代码的方式,事实上,如果你编写的代码少了怎么办? 如果JAVA家里到处都是这样的东西。它只是一块被金属色覆盖的粪便。它迟早会被其他语言取代。
虽然措辞粗暴,但原因并不粗暴。想象一下,一个项目中有许多类似的项目。Lombok我认为这样的插件确实会大大降低阅读源代码的舒适度。
虽然不建议在酒店使用。getter/setter编写一些业务代码,但在多年项目的现实世界中,有时通过提供getter/setter添加一点业务代码可以大大简化某些业务场景的代码。所谓的权衡可能是放弃某些规范和极大的便利。
我现在非常确信,任何编程语言或插件都只是一种工具。即使工具很强大,也取决于使用它的人,就像小米步枪仍然可以赢得飞机和大炮一样。结合具体业务场景和项目实际情况,不必盲目追求高科技,适合才是王道。
Lombok它有其独特的优点,但也有其回避的缺点,熟悉其优点和缺点,在实战中灵活运用才是王道。
参考:
https://projectlombok.org/features/
https://github.com/rzwitserloot/lombok?spm=a2c4e.11153940.blogcont59972.5.2aeb6d32hayLHv
https://www.zhihu.com/question/42348457
https://blog.csdn.net/ghsau/article/details/52334762
版权声明
所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除