大家好,今天小编来为大家解答Lombok介绍、使用及总结这个问题,很多人还不知道,现在让我们一起来看看吧!
1 Lombok背景介绍
官方介绍如下:
Lombok 项目通过添加知道如何构建和编译简单、无样板、不完全是Java 代码的“处理程序”,使Java 成为一种更有趣的语言。
大致意思是Lombok可以通过添加一些“处理程序”来让Java变得更简单、更快。
2 如何使用龙目岛
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添加依赖:
org.projectlomboklombok1.16.20提供
接下来我们来分析一下Lombok中注解的具体用法。
2.1 @数据
类上的@Data注解会自动为该类的所有属性生成setter/getter、equals、canEqual、hashCode和toString方法。如果是final属性,则不会为该属性生成setter方法。
官方给出的例子如下:
导入lombok.AccessLevel;导入lombok.Setter;导入lombok.Data;导入lombok.ToString;@Data 公共类DataExample { 私有最终字符串名称; @Setter(AccessLevel.PACKAGE) 私有int 年龄;私人双分;私有String[] 标签; @ToString(includeFieldNames=true) @Data(staticConstructor='of') 公共静态类练习{ 私有最终字符串名称;私人最终T 值; }}
如果不使用Lombok,实现如下:
导入java.util.Arrays;公共类DataExample { 私有最终字符串名称;私有整数年龄;私人双分;私有String[] 标签;公共DataExample(String name) { this.name=name; } public String getName() { return this.name; } } 无效setAge(int 年龄) { this.age=年龄; } public int getAge() { return this.age; } } public void setScore(double Score) { this.score=分数; } public double getScore () { return this.score; } } public String[] getTags() { return this.tags; } } public void setTags(String[] Tags) { this.tags=标签; } @Override public String toString() { return ' DataExample(' + this.getName() + ', ' + this.getAge() + ', ' + this.getScore() + ', ' + Arrays.deepToString(this .getTags()) + ')'; } protected boolean canEqual(Object other) { return other实例的DataExample; } @Override public boolean equals(Object o) { if (o==this) return true; } if (!(o instanceof DataExample)) 返回false;数据示例其他=( 数据示例) o; if (!other.canEqual((Object)this)) 返回false; if (this.getName()==null ? other.getName() !=null :this.getName().equals(other .getName())) 返回false; if (this.getAge() !=other.getAge()) 返回false; if (Double.compare(this.getScore(), other.getScore()) !=0) 返回false; if (!Arrays.deepEquals(this.getTags(), other.getTags())) 返回false;返回真; } @Override public int hashCode() { 最终int PRIME=59;整数结果=1;最终长temp1=Double.doubleToLongBits(this.getScore());结果=(结果*PRIME) + (this.getName()==null ? 43 : this.getName().hashCode());结果=(结果*PRIME) + this.getAge();结果=(结果*PRIME) + (int)(temp1 ^ (temp1 32));结果=(结果*PRIME) + Arrays.deepHashCode(this.getTags());返回结果; } 公共静态类练习{ 私有最终字符串名称;私人最终T 值;私人练习(字符串名称,T值){ this.name=name; this.value=值; } public staticExerciseof(String name, T value) { return newExercise(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 练习; } @Override public boolean equals(Object o) { if (o==this) return true; } if (!(o instanceof 练习)) 返回false;锻炼?其他=(锻炼?)o; if (!other.canEqual((Object)this)) 返回false; if (this.getName()==null ? other.getValue() !=null :this.getName().equals(other.getName())) 返回false; if (this.getValue()==null ? other.getValue() !=null :this.getValue().equals(other.getValue())) 返回false;返回真; } @Override public int hashCode() { 最终int PRIME=59;整数结果=1;结果=(结果* PRIME) + (this.getName()==null ? 43 : this.getName().hashCode());结果=(结果*PRIME) + (this.getValue()==null ? 43 : this.getValue( ).hashCode());返回结果; } }}
2.2 @Getter/@Setter
如果你觉得@Data太残酷了(因为@Data集成了@ToString、@EqualsAndHashCode、@Getter/@Setter和@RequiredArgsConstructor的所有功能),你可以使用@Getter/@Setter注解。该注释位于属性上。会自动为相应的属性生成Getter/Setter 方法。示例如下:
导入lombok.AccessLevel;导入lombok.Getter;导入lombok.Setter;公共类GetterSetterExample { @Getter @Setter private intage=10; @Setter(AccessLevel.PROTECTED) 私有字符串名称; @Override public String toString() { return String .format('%s (age: %d)', 姓名, 年龄); }}
如果不使用龙目岛:
公共类GetterSetterExample { 私有int 年龄=10;私有字符串名称; @Override public String toString() { return String.format('%s (age: %d)', 姓名, 年龄); } public int getAge() { 返回年龄; } 公共无效setAge(int 年龄) { this.age=年龄; } protected void setName(String name) { this.name=name; }}
2.3 @非空
示例如下:
导入lombok.NonNull;公共类NonNullExample 扩展了Something { 私有字符串名称;公共NonNullExample(@NonNull Person person) { super('Hello'); this.name=person.getName(); }}
不使用龙目岛:
导入lombok.NonNull;公共类NonNullExample 扩展了Something { 私有字符串名称;公共NonNullExample(@NonNull Person person) { super('Hello'); if (person==null) { throw new NullPointerException('person'); }这个.name=person.getName(); }}
2.4 @清理
这个注解可以帮助我们自动调用close()方法,大大简化了代码。
示例如下:
导入lombok.Cleanup;导入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=新字节[10000]; while (true) { int r=in.read(b); if (r==-1) 中断; out.write(b, 0 , r); } }}
如果不使用Lombok,则需要以下内容:
导入java.io.*;public class CleanupExample { public static void main(String[] args) throws IOException { InputStream in=new FileInputStream(args[0]); }尝试{ OutputStream out=new FileOutputStream(args[1]);尝试{ byte[] b=新字节[10000]; while (true) { int r=in.read(b); if (r==-1) 中断;输出.write(b, 0, r); } } 最后{ if (out !=null) { out.close(); } } } 最后{ if (in !=null) { in.close(); } } }}
2.5 @EqualsAndHashCode
默认情况下,所有非静态和非瞬态属性都用于生成equals 和hasCode。还可以通过排除注释排除某些属性。
示例如下:
导入lombok.EqualsAndHashCode;@EqualsAndHashCode(exclude={'id', 'shape'})public class EqualsAndHashCodeExample { 私有瞬态inttransientVar=10;私有字符串名称;私人双分; private Shape 形状=new Square(5, 10) ;私有String[] 标签;私有int id;公共字符串getName() { 返回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=高度; } }}
2.6 @ToString
该类用@ToString注释,Lombok将生成一个toString()方法。默认情况下,将输出类名和所有属性(按照属性定义的顺序),以逗号分隔。
通过将includeFieldNames 参数设置为true,可以显式输出toString() 属性。这是不是有点绕?通过代码来看就更清楚了。
使用龙目岛的示例:
导入lombok.ToString;@ToString(exclude='id')public class ToStringExample { private static final int STATIC_VAR=10;私有字符串名称; private Shape 形状=new Square(5, 10);私有String[] 标签;私有int id;公共字符串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=高度; } }}
不使用Lombok的示例如下:
导入java.util.Arrays;公共类ToStringExample { 私有静态最终int STATIC_VAR=10;私有字符串名称; private Shape 形状=new Square(5, 10);私有String[] 标签;私有int id;公共字符串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=高度; } @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 和@AllArgsConstructor
无参数构造函数、部分参数构造函数、全参数构造函数。 Lombok 无法实现多参数构造函数的重载。
Lombok示例代码如下:
导入lombok.AccessLevel;导入lombok.RequiredArgsConstructor;导入lombok.AllArgsConstructor;导入lombok.NonNull;@RequiredArgsConstructor(staticName='of')@AllArgsConstructor(access=AccessLevel.PROTECTED)public class ConstructorExample{ private int x, y; @NonNull 私有T 描述; @NoArgsConstructor 公共静态类NoArgsExample { @NonNull 私有字符串字段; }}
不使用Lombok的示例如下:
公共类ConstructorExample{ 私有int x, y; @NonNull 私有T 描述; private ConstructorExample(T description) { if (description==null) throw new NullPointerException('description'); } this.description=描述; } public staticConstructorExampleof(T 描述) { return new ConstructorExample(描述); } @java.beans.ConstructorProperties({'x', 'y', 'description'}) protected ConstructorExample(int x, int y, T description) { if (description==null) throw new NullPointerException('description') ;这个.x=x;这个.y=y; this.description=描述; } public static class NoArgsExample { @NonNull 私有字符串字段;公共NoArgsExample() { } }}
3 Lombok工作原理分析
你会发现,使用Lombok时,只需要添加相应的注解即可,不需要为此编写任何代码。自动生成的代码是如何生成的?
核心点是注释的分析。 JDK5在引入注解的同时,也提供了两种解析方式。
运行时解析
对于运行时可以解析的注解,@Retention必须设置为RUNTIME,这样才能通过反射获取注解。 java.lang,reflect反射包提供了一个接口AnnotatedElement,它定义了几种获取注解信息的方法。 Class、Constructor、Field、Method、Package等都实现了这个接口。熟悉反射的朋友应该非常熟悉。熟悉这种解析方法。
编译时分析
编译时解析有两种机制,下面简单介绍一下:
1)注释处理工具
apt 是从JDK5 生成的。 JDK7已被标记为过期,不建议使用。在JDK8中已经被彻底删除了。从JDK6开始,可以使用Pluggable Annotation Processing API来替代它。 apt被替换的主要原因有两个:
api均在com.sun.mirror非标准包下
没有集成到javac中,需要额外运行
2)可插拔注释处理API
自JDK6 以来添加了https://jcp.org/en/jsr/detail?id=269。作为apt的替代方案,它解决了apt的两个问题。当javac执行时,它会调用实现API的程序,这样我们就可以对编译器进行一些增强。此时javac的执行流程如下:
Lombok本质上是一个实现“https://www.jcp.org/en/jsr/detail?id=269”的程序。在使用javac的过程中,其作用的具体过程如下:
javac分析源代码并生成抽象语法树(AST)
运行过程中调用实现“JSR 269 API”的Lombok程序
此时Lombok对第一步得到的AST进行处理,找到@Data注解所在类对应的语法树(AST),然后修改语法树(AST),添加由getter 和setter 方法。
javac 使用修改后的抽象语法树(AST)生成字节码文件,即向类添加新节点(代码块)
看完Lombok源码,对应注解的实现在HandleXXX中。例如,@Getter注解的实现是HandleGetter.handle()。还有一些其他的类库也是通过这种方式实现的,比如https://github.com/google/auto、http://square.github.io/dagger/等。
4.Lombok的优缺点
优势:
可以以注解的形式自动生成构造函数、getters/setters、equals、hashcode、toString等方法,一定程度上提高了开发效率。
使代码简洁,无需过多关注相应的方法。
修改属性时,还简化了为这些属性生成的getter/setter 方法的维护。
缺点:
不支持重载多个参数构造函数
虽然省去了手动创建getter/setter方法的麻烦,但是却大大降低了源码的可读性和完整性,降低了阅读源码的舒适度。
5. 总结
虽然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
用户评论
终于找到了关于 Lombok 的详细介绍!我之前想尝试用它简化代码,但一直没找到合适教程。你的博客讲得非常清晰,已经迫不及待想实践一下了。
有17位网友表示赞同!
Lombok 确实可以提高开发效率,但我感觉它可能会导致代码的可读性降低。尤其是对于不熟悉 Lombok 的团队来说,阅读代码会比较费力。
有8位网友表示赞同!
这篇博客写的很不错,介绍了 Lombok 的基本概念和常用的注解。我之前用过一些 Lombok 的功能,比如 @Data 和 @Setter,但还有很多新的特性还没有了解清楚。我会仔细阅读你的文章,希望能深入理解它。
有17位网友表示赞同!
我一直觉得 IDE 自动生成 Getters/Setters 太繁琐了,Lombok 能帮我节省不少时间和精力!不过文档上说 Lombok 会增加运行时的开销,这让我有点担忧,不知道实际影响有多大?
有6位网友表示赞同!
我是 Java 新手,之前一直在探索项目开发过程中如何提高效率。看到这个标题就觉得很有兴趣,希望博客能详细讲解一下 Lombok 的使用方法和优缺点,以便我更好地理解它的应用场景。
有15位网友表示赞同!
Lombok 的一个比较好的地方就是它能够让代码更加简洁,尤其是对于那些重复性的代码块来说。不过要记住,过度的使用注解可能会使代码难以理解,所以需要适度使用,才能发挥其最大的作用。
有11位网友表示赞同!
Lombok 确实可以提高开发效率,但是我比较担心它会降低代码的可维护性和可读性。如果一个项目有多个开发者协同工作,缺乏对 Lombok 的统一理解可能会导致沟通困难,增加代码调试的难度。
有9位网友表示赞同!
非常感谢博主分享这篇深入浅出的文章!我的 Java 生涯已经走了一些年头了,Lombok 虽然不是第一次接触,但博主的讲解仍然让我受益匪浅,尤其是在使用 Lombok 构建 API 和数据结构方面给出了很多实用建议,我打算尽快将这些知识应用到我的项目中去。
有9位网友表示赞同!
总觉得 Java 代码写起来有时候会比较繁琐,很高兴看到有人介绍了 Lombok 这类工具。期待下期博主能分享一下Lombok 与某些主流框架的兼容性以及一些实际案例分析,以便更全面地了解它的应用场景。
有11位网友表示赞同!
学习完了这篇博客后对 Lombok 有了一个初步的理解。主要想问一下使用 Lombok 之后测试代码的方式有什么改变吗?
有17位网友表示赞同!
lombok 一直在想用这个强大的框架简化代码,但还没找到合适的时机去应用它!这篇文章讲解得很到位,尤其是使用方法部分非常实用,我已经着手在我的项目中尝试使用了Lombok。
有8位网友表示赞同!
我之前也在研究 Lombok, 看了你的博客对它的总结有更深入的了解了,特别是关于使用场景的选择,需要更加慎重, 才能发挥Lombok的最大效用。我会继续学习更多相关的资料!
有16位网友表示赞同!
这篇文章写的太好了!终于可以对lombok有一个清楚的认识了,之前一直在做 Java 开发却不知道这个框架居然能带来如此巨大的方便!我马上就把它应用到我的项目中去试试!
有12位网友表示赞同!
Lombok 确实厉害,可以让代码简洁很多,不过对于初学 Java 的同学来说,可能会比较难以理解 Lombok 的语法和注解的使用。建议博主可以加入一些图文并茂的示例和案例分析,帮助读者更直观地理解它的使用方法。
有12位网友表示赞同!
我主要关注的是 Kotlin 开发,这个 Lombok 介绍是否能适用于 Kotlin?
有19位网友表示赞同!
我一直在寻找高效提高 Java 开发效率的方式,Lombok 作为一种受欢迎的框架,我一直想尝试使用它。这篇博文介绍得非常清晰,我已经开始学习使用它的注解,相信在以后开发中可以大大减少重复代码的书写工作。
有17位网友表示赞同!
这篇博客讲解了 Lombok 的基本概念和用法,让我对这个工具有了初步的了解。不过我想要知道,Lombok 是否支持所有 Java 类型以及不同版本的兼容性如何?
有20位网友表示赞同!