从getter和setter方法引入
在项目中,我们编写一个pojo类,都会有getter/setter,如果pojo的字段较多,那么这个类就会充斥大幅无意义但又不得不写的getter/setter。不仅不方便阅读,并且每当需要修改一些字段时,这些方法也必须进行修改。在我看来,这对于程序员的高效开发是一种阻碍。
那么,这时候,lombok可以很有效的解决这个问题。以下面的代码为例:
- 代码1:With Lombok
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);
}
}
- 代码2:Vanilla Java
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;
}
}
比较代码1和代码2,我们可以看到使用lombok后的代码非常简洁。
在代码中,使用@Getter和/或@Setter注解任何字段,lombok将会自动生成默认的getter/setter。默认getter只返回字段,如果字段名为foo(如果字段的类型是布尔型,则命名为isFoo),那么该getter将被命名为getFoo。如果字段名为foo,则默认setter名为setFoo,返回void,并接受与字段相同类型的1个参数。它只是将字段设置为这个值。生成的getter/setter方法将是公共的,除非显式地指定访问级别,例如代码1中设置setter方法级别为PROTECTED。
对于字段比较多的pojo,我们可以在类上添加@Getter和/或@Setter注解,为所有非静态字段添加getter/setter。对于特殊的字段,我们可以为其添加对应@Getter或者@Setter注解设置访问界别AccessLevel.NONE覆盖禁用对应getter/setter生成。
引入使用Lombok
IDE支持
使用Lombok需要ide提供相应的支持,不然会报错。这里以idea为例,进行插件的安装。
-
安装Lombok Pligin插件
-
在设置中启动annotation processors。
到这里,idea即可支持lombok的使用了。
在项目中使用
- 添加maven依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.16</version>
</dependency>
Lombok常用注解
- val: final 像动态语言一样,声明一个fianl的变量。
- var: 同JDK10
- @Data:注解在类上,将类提供的所有属性都添加get、set方法,并添加、equals、canEquals、hashCode、toString方法
- @Setter:注解在类上,为所有属性添加set方法、注解在属性上为该属性提供set方法
- @Getter:注解在类上,为所有的属性添加get方法、注解在属性上为该属性提供get方法
- @NotNull:在参数中使用时,如果调用时传了null值,就会抛出空指针异常
- @Synchronized 用于方法,可以锁定指定的对象,如果不指定,则默认创建一个对象锁定
- @Log作用于类,创建一个log属性
- @Builder:使用builder模式创建对象
- @NoArgsConstructor:创建一个无参构造函数
- @AllArgsConstructor:创建一个全参构造函数
- @ToString:创建一个toString方法
- @Accessors(chain = true)使用链式设置属性,set方法返回的是this对象。
- @RequiredArgsConstructor:创建对象, 例: 在class上添加@RequiredArgsConstructor(staticName = “of”)会创建生成一个静态方法
- @UtilityClass:工具类
- @ExtensionMethod:设置父类
- @FieldDefaults:设置属性的使用范围,如private、public等,也可以设置属性是否被final修饰。
- @Cleanup: 关闭流、连接点。
- @EqualsAndHashCode:重写equals和hashcode方法。
- @toString:创建toString方法。
- @Cleanup: 用于流等可以不需要关闭使用流对象.
下面我挑一些常用的给一些例子。
常用注解
以下对比代码,With Lombok和Vanilla Java只针对需要对比的注解。
@Accessors(chain = true):链式调用
如果设置为true,setter方法将返回this(而不是void)。
- With Lombok
@Setter
@Getter
@Accessors(chain = true)
public class User {
private String id;
private String name;
private Integer age;
}
public static void main(String[] args) {
//使用@Accessors(chain = true)
User userChain = new User();
userChain.setId("1").setName("chain").setAge(1);
}
- Vanilla Java
@Setter
@Getter
public class User {
private String id;
private String name;
private Integer age;
}
public static void main(String[] args) {
User userChain = new User();
user.setId("1");
user.setName("chain");
user.setAge(1);
}
@Builder:使用builder模式创建对象
- With Lombok
@Setter
@Getter
@Builder
public class User {
private String id;
private String name;
private Integer age;
}
public static void main(String[] args) {
User user = User.builder().id("1").name("builder").age(1).build();
System.out.println(user.getId());
}
- Vanilla Java
@Setter
@Getter
public class User {
private String id;
private String name;
private Integer age;
}
public static void main(String[] args) {
user user = new User();
user.setId("1");
user.setName("builder");
user.setAge(1);
System.out.println(user.getId());
}
这里也可以使用链式调用,但是还是不如使用Builder更简洁。
User user = new User();
user.setName("builder").setAge(1).setId("1");
System.out.println(user.getId());
注:Builder创建对象的使用的是".属性"链式调用,而不是不是用的setter/getter。Builder和链式调用根据具体情况使用。
@CleanUp: 清理流对象
我们可以使用@Cleanup确保在代码执行路径退出当前作用域之前自动清理给定的资源。我们可以通过使用@Cleanup来注解任何局部变量声明,如:@Cleanup InputStream in = new FileInputStream("some/file");
在我们所使用代码块的末尾,将自动调用in.close()。这个调用保证以try/finally构造的方式运行,如下面的代码所示。如果我们想要清除的对象类型没有close()方法,但是有其他一些无参数方法,那么我们可以像这样指定这个方法的名称:@Cleanup("dispose") org.eclipse.swt.widgets.CoolBar bar = new CoolBar(parent, 0);
默认情况下,清理方法被假定为close()。不能通过@Cleanup调用有参数的清除方法。
- With Lombok
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);
}
}
}
- Vanilla Java
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();
}
}
}
}
@Data:包含多个注解的快捷注解
@Data是一个方便的快捷注释,它将@ToString、@EqualsAndHashCode、@Getter / @Setter和@RequiredArgsConstructor的特性捆绑在一起。换句话说,@Data生成了通常与简单pojo和bean关联的所有样板:getter所有字段,setter不是final字段,和适当的toString, equals和hashCode实现涉及类的字段,和一个构造函数,初始化所有final字段,以及所有不是final、没有初始化、已标有@NonNull字段。
- With Lombok
@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;
}
}
- Vanilla Java
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 = (result*PRIME) + (this.getName() == null ? 43 : this.getName().hashCode());
result = (result*PRIME) + this.getAge();
result = (result*PRIME) + (int)(temp1 ^ (temp1 >>> 32));
result = (result*PRIME) + Arrays.deepHashCode(this.getTags());
return result;
}
public static class Exercise<T> {
private final String name;
private final T value;
private Exercise(String name, T value) {
this.name = name;
this.value = value;
}
public static <T> Exercise<T> of(String name, T value) {
return new Exercise<T>(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;
}
}
}
@Log:打印日志
我们可以用@Log来注解任何类,从而让lombok生成一个logger字段。日志记录器名为log,字段的类型取决于您选择的日志记录器。
- With Lombok
@Log
public class LogExample {
public static void main(String... args) {
log.severe("Something's wrong here");
}
}
@Slf4j
public class LogExampleOther {
public static void main(String... args) {
log.error("Something else is wrong here");
}
}
@CommonsLog(topic="CounterLog")
public class LogExampleCategory {
public static void main(String... args) {
log.error("Calling the 'CounterLog' with a message");
}
}
- Vanilla Java
public class LogExample {
private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());
public static void main(String... args) {
log.severe("Something's wrong here");
}
}
public class LogExampleOther {
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExampleOther.class);
public static void main(String... args) {
log.error("Something else is wrong here");
}
}
public class LogExampleCategory {
private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog("CounterLog");
public static void main(String... args) {
log.error("Calling the 'CounterLog' with a message");
}
}
结语
以上就是Lombok为我们提供的一些常用注解,使用这些工具方法,我们可以从重复繁杂的工作种解脱出来,专注于业务代码的设计和优化,同时在各个业务层代码种也提高了代码的可读性。
访问更多信息可以查看:lombok的官方地址和lombok的Github地址