最近,偶然看到东西——能够在开发的过程中用注解的方式,简化实体类。看起来还不错的样子,我挺喜欢,可以了解了解。说不定哪天就用上了。
于是我记下了学习笔记。上目录。
目录
- 什么是Lombok
- Lombok的作用——简化代码
- Lombok的优缺点
- Lombok原理
- Lombok的使用步骤(Idea)
- 正式使用Lombok
什么是Lombok
根据百度百科的解释。
Lombok项目是一个java库,它可以自动插入到编辑器和构建工具中,增强java的性能。不需要再写getter、setter或equals方法,只要有一个注解,你的类就有一个功能齐全的构建器、自动记录变量等等。
也就是说,Lombok是为了简化JavaBean代码。
Lombok的作用——简化代码
Lombok的作用是简化代码。
按照我们通常的方法,角色的实体类代码如下:
1 public class Role { 2 private int rid; 3 private String rname; 4 private String level; 5 6 public int getRid() { 7 return rid; 8 } 9 10 public void setRid(int rid) { 11 this.rid = rid; 12 } 13 14 public String getRname() { 15 return rname; 16 } 17 18 public void setRname(String rname) { 19 this.rname = rname; 20 } 21 22 public String getLevel() { 23 return level; 24 } 25 26 public void setLevel(String level) { 27 this.level = level; 28 } 29 30 @Override 31 public String toString() { 32 return "Role{" + 33 "rid=" + rid + 34 ", rname='" + rname + '\'' + 35 ", level='" + level + '\'' + 36 '}'; 37 } 38 }
如果使用lombok,只需要如下代码:
1 @Data 2 public class Role { 3 private int rid; 4 private String rname; 5 private String level; 6 }
Lombok的优缺点
Lombok的优点
- 代码简洁。
Lombok的缺点
- 强迫性。因为Lombok的使用要求开发者一定要在IDE中安装对应的插件。只要一个人安装了Lombok,其他人也需要安装,否则编译报错。
- 可读性和调试性降低。在代码中使用了Lombok,确实可以帮忙减少很多代码,因为Lombok会帮忙自动生成很多代码。我们不能直接知道类中的getter方法被哪些类引用。
- 不了解底层原理,容易出错。在使用Lombok过程中,如果对于各种注解的底层原理不理解的话,很容易产生意想不到的结果。
举一个简单的例子,我们知道,当我们使用@Data定义一个类的时候,会自动帮我们生成 equals() 方法 。但是如果只使用了 @Data ,而不使用 equalsAndHashCode(callSuper=true) 的话,会默认是 @EqualsAndHashCode(callSuper=false) ,这时候生成的 equals() 方法只会比较子类的属性,不会考虑从父类继承的属性,无论父类属性访问权限是否开放。
- 影响升级。如果我们需要升级到某个新版本的JDK的时候,若其中的特性在Lombok中不支持的话就会受到影响。
- 破坏抽象。如果我们在代码中直接使用Lombok,那么他会自动帮我们生成getter、setter 等方法,这就意味着,一个类中的所有参数都自动提供了设置和读取方法
对应Lombok缺点?
强迫性
对我来说,安装一个插件也不是很费事。当然,如果你要和人合作,他不愿意安装,那确实很难——相比起来,get、set、equals、toString等方法点几下鼠标和快捷键就生成了。所以,这个缺点主要是“势”。很多东西,用的人越多,维护他的人也越多,它的功能才能优化。
注:最新的IDEA似乎不需要安装插件,因为在IDEA中集成了。
可读性和调试性降低
我觉得可读性还是提高了,毕竟重复的代码减少了。其实通过IDEA可以很方便的找到某个属性的getter方法都被哪些类引用,这一点有待于进一步学习。
不了解底层原理,容易出错
每一个工具都有可能遇到坑,额,这个坑……很难理解吗?如果要开发越多人使用的东西,确实越需要谨慎——哪怕是看起来比较简单的新东西。所以,使用工具,确实要对它有所了解,否则容易出错。我们还是需要深度,更何况,Lombok没有特别深。
破坏抽象
这个要看使用场景。以我现在有限的代码来说,几乎所有参数都提供了get和set方法,并没有那么个性化的权限。如果真有个性化的权限,我希望使用注释的方式,这样更加一目了然。
很显然,类A聚集在一起的一行,或者两行代码,就能让我们知道属性的读取,或者写权限;类B,如果属性比较多,似乎相对看起来就没有那么方便了。需要个性化配置权限的时候,带有get和set的注解更一目了然。
Lombok中带有这样的注解:
@Setter/@Getter : 生成set和get方法。
影响升级
如果遇到这个问题,确实比较头疼,不知道发生它的概率。
不过,一般来说。以我有限的经验来说,同一个项目不会去随便升级JDK。难保一些插件和版本不对应。
个人看法
总之,lombok代码的思想还是很好的,它让代码的可读性提高了很多(写代码的时间倒是看不出节约,因为IDEA(或其他工具)都能快速生成get,set方法)。
对于他的缺点,大多数是因为固有的习惯,让很多人不适应;影响升级的问题,就像我们完成一件事情时,遇到的一些问题。如果更多的人接受这一思想,那么必定能够得到处理。
现实工作中,我们要人接受同一个工具、同一件事情不容易。可以提出自己的意见,但真正如果落实,要以实际的沟通为主。
Lombok原理
它的注解是在编译过程就生效的。其他的,我没关心……
不好意思,偶尔的不求甚解。
Lombok的使用准备(Idea)
使用准备包括两部,安装插件和添加项目依赖。
注:最新版的Ida似乎已经自带lombok插件(看来她是大势所趋)。
Lombok的安装
1. 使用快捷键【Crlt+Alt+S】(或点击菜单栏【File】—> 【Settings】)
2. 找到Lombox插件,并点击安装。等待安装完成。
安装完成后需要重启Idea。
添加项目依赖
打开pom.xml文件,加上项目依赖。
1 <dependency> 2 <groupId>org.projuectlombok</groupId> 3 <artifactId>lombok</artifactId> 4 <version>1.16.10</version> 5 </dependency>
正式使用Lombok
接下来,我们看看Lombox的常用注解有哪些。
写一个JavaBean
写一个简单的实体类。这也将是以后写实体类的常用注释。
1 @Data 2 public class User { 3 private Integer id; 4 private String username; 5 private String password; 6 private String phone; 7 private String email; 8 }
按【Alt + 7】,可以看到项目的结构如下。
可以知道,生成了一下类:
- 类的构造方法
- 所有属性的get和set方法
- equals(Object) 、 canEqul(Object) 和 hashCode() 的比较方法。
- toString() 输出打印方法。
我们能在项目的生成目录下查看对应JavaBean的生成文件。
常用注释——Getter和Setter
在字段中添加Getter注释
1 public class User { 2 3 @Getter 4 private Integer id; 5 private String username; 6 private String password; 7 private String phone; 8 private String email; 9 }
此时,id字段默认生成Getter()方法,可以看到它的权限 public 。
其实,Getter的属性可以设置生成权限的类型。
1 public class UserGetter { 2 3 @Getter(AccessLevel.PROTECTED) 4 private Integer id; 5 6 private String username; 7 private String password; 8 private String phone; 9 private String email; 10 }
这时候生成的Getter方法将改变权限。
我们可以设置的权限包括:
属性 | 权限 |
AccessLevel. PUBLIC | public |
AccessLevel. MODULE | |
AccessLevel. PROTECTED | protected |
AccessLevel. PACKAGE | |
AccessLevel. PRIVATE | private |
AccessLevel. NONE | 无权限修饰符 |
在字段中添加Setter注释
同理,可以添加Setter注解。
1 public class UserSetter { 2 3 @Setter 4 private Integer id; 5 private String username; 6 private String password; 7 private String phone; 8 private String email; 9 }
自动生成的Setter方法如下:
同理,可以设置对应Setter方法的权限属性。
为类添加Getter和Setter注释
1 @Getter 2 public class UserClassGetSet { 3 4 private Integer id; 5 private String username; 6 private String password; 7 private String phone; 8 private String email; 9 }
该类的所有字段,将会生成get方法。
类似的,在类上添加 @Setter ,则会为所有的属性添加set方法。
注:static的成员,作为静态类型,不会生成get和set函数。
final成员,只生成getter()方法,不会生成set函数。
常用注释——toString()
通常,实体类中还会有默认打印数据的方法。我们一般为函数写 toString() 方法。
默认toString 注释
1 @ToString 2 public class UsertoString { 3 4 private Integer id; 5 private String username; 6 private String password; 7 private String phone; 8 private String email; 9 }
可以看到,自动生成的toStirng方法为:
1 public String toString() { 2 return "UsertoString(id=" + this.id + ", username=" + this.username + ", password=" + this.password + ", phone=" + this.phone + ", email=" + this.email + ")"; 3 }
默认会打印所有成员变量的值。
排除某个成员变量——属性exclude
有些时候,某些变量可能不需要输出显示,这时候,可以使用 exclude 排除一些打印字段。
@ToString(exclude = {"id","email"}) public class UsertoString { private Integer id; private String username; private String password; private String phone; private String email; }
此时,id和email将不打印出来。
1 public String toString() { 2 return "UsertoString(username=" + this.username + ", password=" + this.password + ", phone=" + this.phone + ")"; 3 }
必须包含变量——属性of
同理, of 用来表示只使用的方法。
of 的优先级高于 exclude 。
常用注释——EqualsAndHashCode
EqualsAndHashCode用于生成函数相关比较方法。
默认EqualsAndHashCode
1 @EqualsAndHashCode 2 public class UserEqualsAndHasCode { 3 4 private Integer id; 5 private String username; 6 private String password; 7 private String phone; 8 private String email; 9 }
添加 @EqualsAndHashCode ,会默认生成三个方法 equals(Object) 、 canEquals(Object) 和 hashCode() 。
3个函数的作用如下:
函数 | 作用 |
equals | 成员变量的对比条件,用于判断两个对象是否相等 |
canEqual | equals引用。判断当前对象是否是定义类型的对象(例如User对象)。 |
hashCode | 对象的哈希值 |
对比条件不包含所有成员
同样,EqualsAndHashCode也有属性“exclude”表示不对某些属性进行比较,“of”表示只对某些属性进行比较。
1 @EqualsAndHashCode(exclude = {"password","phone","email"}) 2 public class UserEqualsAndHasCode { 3 4 private Integer id; 5 private String username; 6 private String password; 7 private String phone; 8 private String email; 9 }
常用注释——NotNull和Construct相关
空指针异常——NonNull注释
该方法可以用在函数的参数上,也可以用成员变量中。当值为空时,会报异常。
1 public class UserConstruct { 2 3 @NonNull 4 private Integer id; 5 private String username; 6 private String password; 7 private String phone; 8 private String email; 9 10 public void run1(@NonNull Integer id){ 11 System.out.println(id); 12 } 13 }
Construct相关
与构造函数相关的注解,加在类上。分别为:
注解 描述
@NoArgsConstructor 无参构造函数
@RequiredArgsConstructor 指定参数作为构造函数。
默认被标识的注解nonNull作为参数
默认还标识final成员变量(不指定值)
@AllArgsConstructor 所有成员的构造函数
注解 | 描述 |
@NoArgsConstructor | 无参构造函数 |
@RequiredArgsConstructor | 指定参数作为构造函数。
|
@AllArgsConstructor | 所有成员的构造函数 |
这里,方法示例如下:
1 @NoArgsConstructor 2 @RequiredArgsConstructor 3 @AllArgsConstructor 4 public class UserConstruct { 5 6 @NonNull 7 private Integer id; 8 @NonNull 9 private String username; 10 private String password; 11 private String phone; 12 private String email; 13 }
生成的构造函数如下:
常用注释——Data和Builder
可以知道。今晚夜色很美,代码很简洁。
注释的综合使用——Data
Lombok的有一个使命——简化代码。正如开头看到的,大多数情况下,我们要做的实体类要求是一致的。
- 包含getter和setter方法
- 包含toString()注释
- 包含对比方法。
- 包含构造函数。
这里使用到开始的时候提到的@Data注解。
可以知道。今晚夜色很美,代码很简洁。
内部类——Builder注释
1 @Data 2 @Builder 3 public class UserData { 4 5 private Integer id; 6 private String username; 7 private String password; 8 private String phone; 9 private String email; 10 }
此时生成了UserBataBuilder内部类。每个对象返回UserDataBuilder的构造对象。
它有什么作用呢?我们可以换一种方式设置值—建造者模式,链式编程。
1 UserData userData = UserData.builder().id(5).username("lilei").password("123456").username("122@qq.com").build();
常用注释——log、Var和Cleanup
日志——log
1 @Log 2 public class UserLog { 3 4 private Integer id; 5 private String username; 6 private String password; 7 private String phone; 8 private String email; 9 10 public void run(String msg){ 11 log.info(msg); 12 } 13 }
可以看到,引用了log日志。
测试运行代码:
1 UserLog userLog = new UserLog(); 2 userLog.run("ssdf");
Log运行结果:
定义变量——val
val关键字的作用约等于我们JavaScript的var。不确定数据类型,运行时再根据赋值确定编译类型。
1 val map = new HashMap<String, String>();
自动清理——cleanup
加上注释可以自动清理文件流、数据流,专注于查询本身,不需要写关闭。
1 @Cleanup FileInputStream in = new FileInputStream("filepath");
完结,撒花。