lombok使用
lombok是一个Java库,官方网址是 https://projectlombok.org。它通过对Java字节码进行修改,简化Java开发。这边介绍可以看作是对官方文档的简单介绍,不少例子都是官方文档上,另外官方文档上还有使用lombok和不使用lombok代码的对比,可以加深理解。如果需要更加详细的介绍或者配置,建议大家去官方网站看下。
val
可以使用val作为本地变量声明的类型,替代变量的实际类型。当你使用val的时候,变量的类型会通过初始化表达式推断出。本地变量也可以是final的。
限制条件:
- val只作用于本地变量和foreach循环,不能作用于类字段
- 必须有初始化表达式,以便于lombok进行推断
举例:
import lombok.val;
public void example{
// 作用于本地变量
val a = "123";
val b = 123;
System.out.println(a + b);
// 作用于foreach循环
val list = new int[]{1, 2, 3, 4, 5, 6};
for (val i : list){
System.out.println(i);
}
}
@NonNull
作用于方法或构造器的参数上,自动生成null-check语句。
null-check语句看起来像:
if (param == null) throw new NullPointerException("param")
这条语句会插入方法的的顶部。对构造器来说,会被插入任何显示的this()或者super()调用之后。
如果null-check语句已经在方法或者构造器内部有了,那么不会生成多余的null-check语句。
举例:
public class NonNull {
private String name;
public NonNull(@lombok.NonNull String name){
this.name = name;
}
public static void main(String[] args) {
NonNull notNull = new NonNull(null);// java.lang.NullPointerException: name
}
}
@Cleanup
可以使用Cleanup来确保资源在退出当前作用域的时候自动清理。你只需要在本地变量加上@Cleanup注解就行。
@Cleanup InputStream in = new FileInputStream("some/file");
这样,当你退出当前作用域的时候,in.close()方法就会被调用。这个方法的调用是用 try/finally 来确保执行的。
如果资源对象没有close()方法,但是有其他的无参方法来释放资源,那么可以在@Cleanup注解中指定方法名:
@Cleanup("dispose") org.eclipse.swt.widgets.CoolBar bar = new CoolBar(parent, 0);
这样,就会调用bar.dispose()方法了。只得注意的是,默认的cleanup方法是close()。如果cleanup方法有参数,那么不能使用@Cleanup。
@Getter / @Setter
这个应该是最常用的功能了。为类属性生成getter/setter方法。
默认的getter/setter方法是public的,除非你显示的指定AccessLevel,这个是@Getter/@Setter的参数。AccessLevel有public,protected,package,private四种。
在类上也可以使用@Getter/@Setter注解,这样该类内的所有非静态属性生成getter/setter方法。
你也可以手动的关闭getter/setter方法的生成,通过指定AccessLevel.NONE,这个会覆盖类上的@Getter、@Setter和@Data注解。
举例:
public class GetterSetter {
@Getter
@Setter
private int age = 10;
public static void main(String[] args) {
GetterSetter getterSetter = new GetterSetter();
getterSetter.setAge(20);
System.out.println(getterSetter.getAge());
}
}
@ToString
这个注解作用在类上,生成toString()方法。默认,它会返回类名和每个属性。
可以通过指定includeFieldNames参数来设置是否返回属性的名字,该参数默认是true。
所有的非static属性默认都会返回。如果你还想跳过某些属性,可以指定exclude参数来讲不想输出的参数放在里面。另外一个选择是使用of参数,将想输出的参数放在里面。
将callSuper参数设置成true,就可以包含父类的toString返回值了。
举例:
@lombok.ToString(includeFieldNames = true,callSuper = false,exclude = {"address"})
public class ToString {
private String name;
private int age;
private String address;
public ToString(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
public static void main(String[] args) {
ToString toString = new ToString("chengjf", 20, "China");
System.out.println(toString.toString());
}
}
@EqualsAndHashCode
该注解作用在类上,用来生成equals(Object other)和hashCode()方法的实现。默认的,会使用所有的非静态,非瞬时的类属性。同样的,你可以使用exclude参数排除你不想使用的类属性,或者使用of参数指定向使用的属性。
当你的类继承自其他的类时,需要特别注意。
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;
}
}
}
@NoArgsConstructor, @RequiredArgsConstructor and @AllArgsConstructor
这三个注解都是作用在类上的,用来生成合适的构造函数。
@NoArgsContructor生成无参数的构造函数。如果类中有final类属性,构造函数必须要初始化该属性,那么会抛出一个编译错误。但是如果你使用@NoArgsConstructor(force = true),那么所有的final类属性会被初始化成0/false/null。
@RequiredArgsConstructor生成包含所有非final类变量的构造函数。参数的顺序和它们在类中出现的顺序一样。
@AllArgsConstructor生成所有类变量的构造函数。
使用staticName参数,指定一个方法名字,可以为该类生成一个静态方法工厂。然后可以通过该静态方法构造对象,形如SomeClass.of(someargs)。
@RequiredArgsConstructor(staticName = "of")
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class ConstructorExample<T> {
private int x, y;
@NonNull private T description;
@NoArgsConstructor
public static class NoArgsExample {
@NonNull private String field;
}
}
@Data
该注解是一个快捷方式,包含了@ToString,@EqualsAndHashCode,@Getter/Setter和@RequiredArgsConstructor注解。
如果某个注解有自己的特殊处理,那么需要进行单独的设置。
举例:
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<T> {
private final String name;
private final T value;
}
}
@Value
@Value注解是@Data注解的不可变的版本。所有的类属性默认都是private和final的,setter方法不会生成。类本身也被标记成了final。toString()、equals()和hashCode()方法也会生成。每个类属性都有getter方法。然后,还有一个包含所有类属性字段的构造方法(除了已经初始化的final类属性)。
实际上,@Value=final @ToString @EqualsAndHashCode @AllArgsConstructor @FieldDefault(makeFinal = true, level = AcessLevel.PRIVATE) @Getter。
举例:
@Value
public class ValueExample {
String name;
@Wither(AccessLevel.PACKAGE)
@NonFinal
int age;
double score;
protected String[] tags;
@ToString(includeFieldNames = true)
@Value(staticConstructor = "of")
public static class Exercise<T> {
String name;
T value;
}
}
@Builder
该注解为类生成builder API。
形如:
Person.builder().name("Adam Savage").city("San Francisco").job("Mythbusters").job("Unchained Reaction").build();
举例:
@Builder
public class BuilderExample {
private String name;
private int age;
@Singular private Set<String> occupations;
}
@SneakyThrows
该注解会为你偷偷的抛出检查异常(checkd exceptions),代替你手动在你的方法里写throws语句。
该英爱只用在下面这两种情况下:
- 一些非必要的接口中,比如Runnable接口
- 一些不可能的异常,比如 new String(someByteArray, "UTF-8")
举例:
public class SneakyThrowsExample implements Runnable {
@SneakyThrows(UnsupportedEncodingException.class)
public String utf8ToString(byte[] bytes) {
return new String(bytes, "UTF-8");
}
@SneakyThrows
public void run() {
throw new Throwable();
}
}
@Synchronized
类似于synchronized关键字,作用于方法上。同synchronized不同的是,它锁的对象不是this,而是一个类属性lock对象。
如果该变量不存在,会生成一个。如果该注解作用在static方法上,锁对象会替换成静态的LOCK对象。
如果自己定义了锁对象,那么可以在@Synchronized的参数里指定。
举例:
public class SynchronizedExample {
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");
}
}
@Getter(lazy=true)
生成一个getter方法,只求一次值。第一个getter访问的时候,计算出该值,然后将该值缓存起来,后面在调用该方法的时候,直接从缓存中取值返回即可。这个对于占用大量CPU或者占用很多内存的方法来说非常有用。
使用该特性,需要创建一个private final的变量,通过表达式进行初始化,然后给该变量加上@Getter(lazy = true)注解。
举例:
public class GetterLazyExample {
@Getter(lazy = true)
private final double[] cached = expensive();
private double[] expensive() {
double[] result = new double[1000000];
for (int i = 0; i < result.length; i++) {
result[i] = Math.asin(i);
}
return result;
}
}
@Log
这个也是我常用的注解。该注解有下面这几种:
- @CommonsLog
private static final org.apche.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class); - @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);
举例
@Log
public class LogExample {
public static void main(String... args) {
log.error("Something's wrong here");
}
}