lombok
#介绍
lombok项目通过增加处理程序使java语言更加简洁和快速,帮助开发人员消除 Java 的冗长,尤其是对于简单的 Java 对象,在源码中不需要写一些通用的方法,但是在编译生成的字节码文件中会生成这些方法
github项目地址
#安装
下载 lombok.jar 它包含用于开发的API,作为IDE集成的安装程序
##Eclipse
打开cmd控制台,运行lombok.jar
D:\下载>java -jar lombok.jar
如果无法正确确定IDE的安装位置,则可以手动指定位置
手工配置eclipse.ini可以能达到相同目的:在eclipse.ini中配置lombok.jar路径
##idea
打开IDEA的Setting –> 选择Plugins选项 –> 选择Browse repositories –> 搜索lombok –> 点击安装 –> 安装完成重启IDEA –> 安装成功
#注解
参考:https://projectlombok.org/features/all
注解 | 描述 |
---|---|
val | 变量类型自动推算,用在局部变量前面,相当于将变量声明为final,只能用在局部变量或者foreach中,不能用于类变量 |
var | 变量类型自动推算,用在局部变量前面,功能类似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,有六种可选实现类 |
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); |
更多实现可以参考源码:git源码
#使用示例
非静态:静态是指由static关键字修饰的变量或方法;反之就是非静态。在java中,静态变量优于非静态量加载,所以如果静态代码块中不能引用非静态成员的变量,会报错。
非瞬态:瞬态是指关键字“transient”修饰的变量;反之为非瞬态。瞬态变量的生命周期仅存于调用者的内存中。瞬态变量不可被序列化。
以maven工程为例
引入依赖
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.2</version>
<scope>provided</scope>
</dependency>
多数例子都是以官网的例子为原型加工修改
如果还不熟悉lombok.config的使用,请先异步至 lombok.config----lombok配置系统 因为下面的各注解有关介绍可能会涉及配置项,不再单独说明
##val
类型推导:val注解变量申明是final类型,根据类型自动推导出变量类型,只能用在方法或者构造函数的参数前面,不能用于类变量
package com.demo.lombok;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import lombok.val;
import lombok.var;
public class DemoValVar {
/*
* //下面的定义语句会有异常:Change type of "name" to "String"
* val name="xiaoming";
*/
public void lomboValMethod() {
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());
}
/*
* //定义下面这条语句会有异常:The final local variable map cannot be assigned. It must be blank and not using a compound assignment
* //说明是定义成final类型的变量
* map=new HashMap<Integer, String>();
*/
}
public void lomboVarMethod() {
var map = new HashMap<Integer, String>();
map.put(0, "zero");
map.put(5, "five");
for (var entry : map.entrySet()) {
System.out.printf("%d: %s\n", entry.getKey(), entry.getValue());
}
//定义下面的语句没有异常,说明是非final的
map=new HashMap<Integer, String>();
}
public static String getType(Object o){ //获取变量类型方法
return o.getClass().toString(); //使用类型的getClass()方法
}
public static String getModifier(Object o){ //获取变量类型方法
return Modifier.toString(o.getClass().getModifiers()); //使用类型的getClass()方法
}
public static void main(String[] args) {
DemoValVar demoValVar = new DemoValVar();
demoValVar.lomboValMethod();
demoValVar.lomboVarMethod();
}
}
可以结合lombok.config配置文件进行喜好设置:禁用、使用警告、允许(默认not set即允许)
lombok.val.flagUsage = [warning | ERROR] (default: not set)
对于符合类型,通常都是推导出来父类,不是接口,例如表达式
bool ? new HashSet() : new ArrayList()
结果既是AbstractCollection 也是serializable,结果推导是AbstractCollection,因为AbstractCollection是一个类,而Serializable是一个接口。当然也有歧义的时候,如当表达式推导结果是null或者Object的时候
##var
var是在lombok1.16.12引入的扩展功能,在2.0.0晋升到主包。
var的用法类似val,区别在于var定义的变量不是final的
可以结合lombok.config配置文件进行喜好设置:禁用、使用警告、允许(默认not set即允许)
lombok.var.flagUsage = [warning | error] (default: not set)
##NonNull
@NonNull注释用于指示对相应成员进行快速失败null-check的需要
- 在方法或构造函数的参数上使用@NonNull,lombok将生成一个null-check语句。
- 在字段上使用@NonNull,为字段赋值时(即调用字段的setter方法时),如果传的参数为null,则会抛出空异常NullPointerException,生成setter方法时会对参数是否为空检查
- 执行顺序在方法比较靠前的地方执行,比如构造函数,null-check会在显式声明this()或supper()的后面立即执行
- 如果已经有null-check,则不会有额外的null-check生成
package com.demo.lombok;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
@Getter
@Setter
public class DemoNonNull {
static final Logger logger = LoggerFactory.getLogger(DemoNonNull.class);
@NonNull
private String id;
public DemoNonNull(@NonNull String id) {
this.id = id;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
DemoNonNull demoNonNull = null;
/*
* 下面的语句会抛出异常
* //demoNonNull= new DemoNonNull(null);
*/
demoNonNull = new DemoNonNull("1");
logger.info("demoNonNull.getId() :{}", demoNonNull.getId());
/*
* 下面的语句会抛出异常 //demoNonNull.setId(null);
*/
}
}
可以结合lombok.config配置文件进行喜好设置:禁用、使用警告、允许(默认not set即允许)
lombok.nonNull.flagUsage = [warning | error] (default: not set)
另外还可以设置抛出异常的类型
lombok.nonNull.exceptionType = [NullPointerException | illegalargumentException] (default: NullPointerException).
##@Cleanup
该注解使用在属性前,该注解是用来保证分配的资源被释放。在本地变量上使用该注解,任何后续代码都将封装在try/finally中,确保当前作用于中的资源被释放。默认@Cleanup清理的方法为"close()",可以使用注解的value指定不同的无参方法名称@Cleanup(“xxxx”)
- @Cleanup 不能调用有参清理函数,需要调用有参清理函数需要手工操作
- @Cleanup 如果清理方法抛出异常,它将抢占抛出方法体内的任何异常。这可能导致问题的实际原因被掩埋,因此在选择使用Project Lombok的资源管理时应该考虑这个问题
package com.demo.lombok;
import lombok.Cleanup;
import java.io.*;
public class CleanupExample {
public static void main(String[] args) throws IOException {
@Cleanup
InputStream in = new fileinputstream("c:\\test\\a.txt");
@Cleanup
outputstream out = new FileOutputStream("b.txt");
byte[] b = new byte[10000];
while (true) {
int r = in.read(b);
if (r == -1)
break;
out.write(b, 0, r);
}
}
}
可以结合lombok.config配置文件进行喜好设置:禁用、使用警告、允许(默认not set即允许)
lombok.cleanup.flagUsage = [warning | error] (default: not set)
##@Getter和@Setter
该注解使用在类或者属性上,为字段生成Getter和Setter方法,可以注解到字段或者类上,默认是public类型的,如果需要的话可以修改方法的访问级别,使用AccessLevel.xxxxxx
AccessLevel.PUBLIC
AccessLevel.MODULE
AccessLevel.protected
AccessLevel.PACKAGE
AccessLevel.PRIVATE
AccessLevel.NONE
- 注解在类上会为类中的所有非静态成员变量生成Getter和Setter方法
- 非boolean类型:生成getXxxxx和setXxxx
- boolean类型:setXxxx和isXxxx
- 如果类和字段上均使用相同的注解,字段上的注解会覆盖相同的注解,以字段上的注解为准
- 如果注释字段所属的类包含与要生成的getter或setter相同名称的方法,则不管参数或返回类型如何,都不会生成相应的方法
- 可以通过AccessLevel.NONE禁用某个字段上的@Setter、@Getter、@Data
下面的截图中m_noneSetter字段没有生成对应的getter和setter(另外这个例子也演示了覆盖相同的注解)
package com.demo.lombok;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.java.Log;
@AllArgsConstructor
@Log
@Getter
@Setter
public class Tester {
private String m_test;
@Setter(AccessLevel.NONE)
@Getter(AccessLevel.NONE)
private String m_noneSetter;
}
##@ToString
生成toString()方法,默认情况下,它会以逗号分隔按顺序打印类名称以及每个字段(只会使用类中的非静态和非transient成员变量)。
设置lombok.toString.includeFieldNames =true 就可以控制字段的使用,默认是true,即开启状态
- 如果想从生成的方法中排除特定字段使用@ToString.Exclude注解该字段;当然也可以只使用特定的字段使用onlyexplicitlyIncluded = true和@ToString.Include注解
- 如果继承的有父类的话,可以设置callSuper 让其调用父类的toString()方法,例如:@ToString(callSuper = true)
package com.demo.lombok;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.ToString;
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Father {
private String m_father_first;
private String m_father_second;
}
package com.demo.lombok;
import lombok.AllArgsConstructor;
import lombok.ToString;
import lombok.extern.java.Log;
@AllArgsConstructor
@Log
@ToString(onlyExplicitlyIncluded = true,callSuper=true)
public class Tester extends Father{
Father father=new Father("father_first", "father_second");
private String m_first;
@ToString.Include
private String m_second;
@ToString.Include
private String m_third;
public static void main(String[] args) {
mylog.info(new Tester(new Father("father_first", "father_second"),"m_first","m_second","m_third").toString());
}
}
八月 14, 2018 6:04:47 下午 com.demo.lombok.Tester main
信息: Tester(super=Father(m_father_first=null, m_father_second=null), m_second=m_second, m_third=m_third)
父类也要有toString方法,不然打印的是对象内存地址
对于数组,在写 toString 方法时使用了 Arrays类的 静态方法 deepToString------这两个方法的区别是这样的,对于多维数组,使用 toString 只能打印出内部数组的名字,这时需要使用 deepToString 方法,它能将内部数组的内容全部打印出来
再来看看静态变量
package com.demo.lombok;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.ToString;
@ToString
@Log
public class ToStringExample {
private static final int STATIC_VAR = 10;
private String name;
private Shape shape = new Square(5, 10);
private String[] tags;
@ToString.Exclude
private int id;
static String spec="hide me";
String comm="show me";
public String getName() {
return this.name;
}
@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;
}
}
public static void main(String[] args) {
ToStringExample toStringExample = new ToStringExample();
mylog.info("toStringExample.toString() :"+toStringExample.toString());
}
}
class Shape {
}
lombok.toString.includeFieldNames=true:默认的情况下lombok为每一个字段生成一对key=value返回给toString。如果这个设置被设置为false,lombok将省略该字段的名称,并简单地展开一个逗号分隔的所有字段值列表。如果显式指定includeFieldNames的话,优先级高于此设置。
lombok.toString.includeFieldNames = [true | false] (default: true)
修改lombok.config配置文件,增加如下设置
lombok.toString.includeFieldNames = false
package com.demo.lombok;
import lombok.AllArgsConstructor;
import lombok.ToString;
import lombok.extern.java.Log;
@AllArgsConstructor
@Log
@ToString
public class Tester extends Father{
private String m_first;
private String m_second;
private String m_third;
public static void main(String[] args) {
mylog.info(new Tester("m_first","m_second","m_third").toString());
}
}
八月 14, 2018 6:30:56 下午 com.demo.lombok.Tester main
信息: Tester(m_first, m_second, m_third)
开启includeFieldNames=true
@AllArgsConstructor
@Log
@ToString(includeFieldNames=true)
public class Tester extends Father{
private String m_first;
private String m_second;
private String m_third;
public static void main(String[] args) {
mylog.info(new Tester("m_first","m_second","m_third").toString());
}
}
八月 14, 2018 6:33:00 下午 com.demo.lombok.Tester main
信息: Tester(m_first=m_first, m_second=m_second, m_third=m_third)
在研究参数lombok.toString.doNotUseGetters的时候,发现无论如何设置,好像都不走getter,有哪位高手指点下,我在继续看看,若能解决我会更新本参数
lombok.toString.doNotUseGetters如果设置成true,lombok生成toString时直接访问字段而不是使用getter。如果注解doNotUseGetters显式声明,优先级高于该设置
lombok.toString.doNotUseGetters = [true | false] (default: false)
修改lombok.config,添加两行
clear lombok.toString.doNotUseGetters
lombok.toString.doNotUseGetters = true
@AllArgsConstructor
@Log
@ToString(onlyExplicitlyIncluded=true)
public class Tester extends Father{
private String m_first;
@ToString.Include
private String m_second;
private String m_third;
public void setM_second(String xString) {
System.err.println("doing");
this.m_second=xString;
}
public static void main(String[] args) {
mylog.info(new Tester("m_first","m_second","m_third").toString());
}
}
八月 14, 2018 6:44:43 下午 com.demo.lombok.Tester main
信息: Tester(m_second)
可以结合lombok.config配置文件进行喜好设置:禁用、使用警告、允许(默认not set即允许)
lombok.toString.flagUsage = [warning | error] (default: not set)
未完待续……
参考 lombokfeatures
lombok.config配置系统
lombok特性(一)
lombok特性(二)