简化Java代码,拥抱Lombok

从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地址

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值