什么是Lombok

lombok是一个简化Java代码的插件,原理是在java类编译时增加通用代码,通常是 equals、get、set、tosting、loging等通用代码,尤其是Java领域模型(即提倡JavaBean)编程中解放大量生产力。

装个插件,可以节省10%的代码量,很划算。
官方解释:

Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java. Never write another getter or equals method again, with one annotation your class has a fully featured builder, Automate your logging variables, and much more. 项目龙目岛是一个java库,它会自动插入编辑器并构建工具,从而加快你的java。 永远不要再写另一个 getter 或等值方法,一个注释,您的类具有一个功能齐全的生成器,自动执行日志记录变量,等等。


安装

1. 开发工具安装lombok插件

比如idea安装:file—settings—plugins—搜插件lombok —安装
image.png

2. 引入lombok 的jar,比如maven可以

最新版:https://mvnrepository.com/artifact/org.projectlombok/lombok

  1. <dependency>
  2. <groupId>org.projectlombok</groupId>
  3. <artifactId>lombok</artifactId>
  4. <version>1.18.10</version>
  5. <scope>provided</scope>
  6. </dependency>

使用日志还需要引入日志依赖
最新版:https://mvnrepository.com/artifact/org.slf4j/slf4j-api

  1. <dependency>
  2. <groupId>org.slf4j</groupId>
  3. <artifactId>slf4j-api</artifactId>
  4. <version>2.0.0-alpha1</version>
  5. </dependency>

3.解决编译时出错问题

编译时出错,可能是没有enable注解处理器。Annotation Processors > Enable annotation processing。设置完成之后程序正常运行。

原理讲解

准备一个User类

  1. @Data
  2. public class User {
  3. private Long id;
  4. private String msg;
  5. private Date sendTime;
  6. }

以@Data注解为例

Lombok - 图2

常用注解

image.png

import lombok.*; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test;

/**

  • 测试lombok */ public class LomBokTest {

// @Getter //生成get方法 // @Setter //生成set方法 // @ToString //生成toString方法 // @EqualsAndHashCode //生成equals和hashCode方法 // @RequiredArgsConstructor //public User(@NonNull String name) 构造参数需要参数

  1. /**
  2. * 因为以上注解常用,故多个注解合并为一个注解@Data
  3. * 等同于以下几个注解的集合
  4. * * @see Getter
  5. * * @see Setter
  6. * * @see RequiredArgsConstructor
  7. * * @see ToString
  8. * * @see EqualsAndHashCode
  9. * * @see lombok.Value
  10. *
  11. * @see Data
  12. */
  13. @Data
  14. @AllArgsConstructor //全参构造方法
  15. @NoArgsConstructor //无参构造方法
  16. /**
  17. * 日志支持
  18. * 支持如下日志
  19. * @see Slf4j
  20. * * @see lombok.extern.apachecommons.CommonsLog CommonsLog
  21. * * @see lombok.extern.java.Log Log
  22. * * @see lombok.extern.log4j.Log4j Log4j
  23. * * @see lombok.extern.log4j.Log4j2 Log4j2
  24. * * @see lombok.extern.slf4j.XSlf4j XSlf4j
  25. * * @see lombok.extern.jbosslog.JBossLog JBossLog
  26. * * @see lombok.extern.flogger.Flogger Flogger
  27. * * @see lombok.CustomLog CustomLog
  28. */
  29. @Slf4j
  30. static class User {
  31. private static int i = 1;
  32. private Integer id;
  33. private String name;
  34. private void log() {
  35. log.info("打印日志");
  36. }
  37. @Synchronized //线程安全 等同意 synchronized关键字
  38. @SneakyThrows //不抛出异常
  39. private void synchronizedTest() {
  40. Thread.sleep(100);
  41. i++; //多线程共享数据i
  42. log.info("线程安全,i={}", i);
  43. }
  44. private void threadTest() {
  45. //启动两个线程 模拟多线程操作
  46. new Thread(this::synchronizedTest).start();
  47. new Thread(this::synchronizedTest).start();
  48. }
  49. }
  50. @Test
  51. void t1() {
  52. User user = new User();
  53. user.setName("编程指南");
  54. System.out.println(user.getName());
  55. System.out.println(user.toString());
  56. User u1 = new User(1, "张三");
  57. User u2 = new User(1, "张三");
  58. System.out.println(u1.equals(u2));
  59. }

}

  1. <a name="5h0Zi"></a>
  2. # 最新特性解读
  3. 官方文档:[https://projectlombok.org/features/all](https://projectlombok.org/features/all)
  4. <a name="ir80J"></a>
  5. ## @val
  6. 自动推断类型,并声明为 final 。<br />`_val a ="str"_` 等同于 `final String a = "str"` <br />lombok代码:
  7. ```java
  8. import java.util.ArrayList;
  9. import java.util.HashMap;
  10. import lombok.val;
  11. public class ValExample {
  12. public String example() {
  13. val example = new ArrayList<String>();
  14. example.add("Hello, World!");
  15. val foo = example.get(0);
  16. return foo.toLowerCase();
  17. }
  18. public void example2() {
  19. val map = new HashMap<Integer, String>();
  20. map.put(0, "zero");
  21. map.put(5, "five");
  22. for (val entry : map.entrySet()) {
  23. System.out.printf("%d: %s\n", entry.getKey(), entry.getValue());
  24. }
  25. }
  26. }

转为java代码:

  1. import java.util.ArrayList;
  2. import java.util.HashMap;
  3. import java.util.Map;
  4. public class ValExample {
  5. public String example() {
  6. final ArrayList<String> example = new ArrayList<String>();
  7. example.add("Hello, World!");
  8. final String foo = example.get(0); //自动推断为 final String类型
  9. return foo.toLowerCase();
  10. }
  11. public void example2() {
  12. final HashMap<Integer, String> map = new HashMap<Integer, String>();
  13. map.put(0, "zero");
  14. map.put(5, "five");
  15. for (final Map.Entry<Integer, String> entry : map.entrySet()) {
  16. System.out.printf("%d: %s\n", entry.getKey(), entry.getValue());
  17. }
  18. }
  19. }

@var

与val意义,自动推断类型,只不过不是final
如: var a =2 等同于 Integer a =2

@Nonall

生成空指针检查语句,如果为null则抛出异常

lombok代码:

  1. import lombok.NonNull;
  2. public class NonNullExample extends Something {
  3. private String name;
  4. public NonNullExample(@NonNull Person person) {
  5. super("Hello");
  6. this.name = person.getName();
  7. }
  8. }

转为java代码:

  1. import lombok.NonNull;
  2. public class NonNullExample extends Something {
  3. private String name;
  4. public NonNullExample(@NonNull Person person) {
  5. super("Hello");
  6. //生成空指针检查语句,如果为null则抛出异常
  7. if (person == null) {
  8. throw new NullPointerException("person is marked @NonNull but is null");
  9. }
  10. this.name = person.getName();
  11. }
  12. }

@ClearUp

自动资源管理:close()轻松安全地调用方法。
声明进行@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()。不能通过调用带有1个或多个参数的清理方法@Cleanup

lombok代码:

  1. import lombok.Cleanup;
  2. import java.io.*;
  3. public class CleanupExample {
  4. public static void main(String[] args) throws IOException {
  5. @Cleanup InputStream in = new FileInputStream(args[0]);
  6. @Cleanup OutputStream out = new FileOutputStream(args[1]);
  7. byte[] b = new byte[10000];
  8. while (true) {
  9. int r = in.read(b);
  10. if (r == -1) break;
  11. out.write(b, 0, r);
  12. }
  13. }
  14. }

转为java代码:

  1. import java.io.*;
  2. public class CleanupExample {
  3. public static void main(String[] args) throws IOException {
  4. InputStream in = new FileInputStream(args[0]);
  5. try {
  6. OutputStream out = new FileOutputStream(args[1]);
  7. try {
  8. byte[] b = new byte[10000];
  9. while (true) {
  10. int r = in.read(b);
  11. if (r == -1) break;
  12. out.write(b, 0, r);
  13. }
  14. } finally {
  15. if (out != null) {
  16. out.close();
  17. }
  18. }
  19. } finally {
  20. if (in != null) {
  21. in.close();
  22. }
  23. }
  24. }
  25. }

@Getter/@Setter

生成getter和setter代码

lombok代码

  1. import lombok.AccessLevel;
  2. import lombok.Getter;
  3. import lombok.Setter;
  4. public class GetterSetterExample {
  5. /**
  6. * Age of the person. Water is wet.
  7. *
  8. * @param age New value for this person's age. Sky is blue.
  9. * @return The current value of this person's age. Circles are round.
  10. */
  11. @Getter @Setter private int age = 10;
  12. /**
  13. * Name of the person.
  14. * -- SETTER --
  15. * Changes the name of this person.
  16. *
  17. * @param name The new value.
  18. */
  19. @Setter(AccessLevel.PROTECTED) private String name;
  20. @Override public String toString() {
  21. return String.format("%s (age: %d)", name, age);
  22. }
  23. }

转为java代码:

  1. public class GetterSetterExample {
  2. /**
  3. * Age of the person. Water is wet.
  4. */
  5. private int age = 10;
  6. /**
  7. * Name of the person.
  8. */
  9. private String name;
  10. @Override public String toString() {
  11. return String.format("%s (age: %d)", name, age);
  12. }
  13. /**
  14. * Age of the person. Water is wet.
  15. *
  16. * @return The current value of this person's age. Circles are round.
  17. */
  18. public int getAge() {
  19. return age;
  20. }
  21. /**
  22. * Age of the person. Water is wet.
  23. *
  24. * @param age New value for this person's age. Sky is blue.
  25. */
  26. public void setAge(int age) {
  27. this.age = age;
  28. }
  29. /**
  30. * Changes the name of this person.
  31. *
  32. * @param name The new value.
  33. */
  34. protected void setName(String name) {
  35. this.name = name;
  36. }
  37. }

@Getter(lazy = true)

懒加载方式getter。
您可以让lombok生成一个将在第一次调用此getter时计算一次值的getter,然后将其缓存。如果计算该值占用大量CPU或该值占用大量内存,这将很有用。要使用此功能,请创建一个private final变量,使用运行起来很昂贵的表达式对其进行初始化,然后使用注释您的字段@Getter(lazy=true)。首次调用getter时,该字段将对代码的其余部分隐藏,并且对该表达式的求值不会超过一次。没有魔术标记值(即,即使您的昂贵计算null结果为,结果也将被缓存),并且您昂贵的计算不必是线程安全的,因为lombok负责锁定。
您将的变体@Log放在您的类上(适用于您所使用的日志记录系统的任何一种);然后,您将拥有一个静态的final log字段,该字段按照使用的日志记录框架通常规定的方式进行初始化,然后可以使用该方法编写日志语句。
有几种选择:
@CommonsLog创造 private static final [org.apache.commons.logging.Log](https://commons.apache.org/logging/apidocs/org/apache/commons/logging/Log.html) log = [org.apache.commons.logging.LogFactory.getLog](https://commons.apache.org/logging/apidocs/org/apache/commons/logging/LogFactory.html#getLog(java.lang.Class))(LogExample.class);``@Flogger创造 private static final [com.google.common.flogger.FluentLogger](https://google.github.io/flogger/) log = com.google.common.flogger.FluentLogger.forEnclosingClass();``@JBossLog创造 private static final [org.jboss.logging.Logger](https://docs.jboss.org/jbosslogging/latest/org/jboss/logging/Logger.html) log = [org.jboss.logging.Logger.getLogger](https://docs.jboss.org/jbosslogging/latest/org/jboss/logging/Logger.html#getLogger(java.lang.Class))(LogExample.class);``@Log创造 private static final [java.util.logging.Logger](https://docs.oracle.com/javase/6/docs/api/java/util/logging/Logger.html) log = [java.util.logging.Logger.getLogger](https://docs.oracle.com/javase/6/docs/api/java/util/logging/Logger.html#getLogger(java.lang.String))(LogExample.class.getName());``@Log4j创造 private static final [org.apache.log4j.Logger](https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/Logger.html) log = [org.apache.log4j.Logger.getLogger](https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/Logger.html#getLogger(java.lang.Class))(LogExample.class);``@Log4j2创造 private static final [org.apache.logging.log4j.Logger](https://logging.apache.org/log4j/2.0/log4j-api/apidocs/org/apache/logging/log4j/Logger.html) log = [org.apache.logging.log4j.LogManager.getLogger](https://logging.apache.org/log4j/2.0/log4j-api/apidocs/org/apache/logging/log4j/LogManager.html#getLogger(java.lang.Class))(LogExample.class);``@Slf4j创造 private static final [org.slf4j.Logger](https://www.slf4j.org/api/org/slf4j/Logger.html) log = [org.slf4j.LoggerFactory.getLogger](https://www.slf4j.org/api/org/slf4j/LoggerFactory.html#getLogger(java.lang.Class))(LogExample.class);``@XSlf4j创造 private static final [org.slf4j.ext.XLogger](https://www.slf4j.org/api/org/slf4j/ext/XLogger.html) log = [org.slf4j.ext.XLoggerFactory.getXLogger](https://www.slf4j.org/api/org/slf4j/ext/XLoggerFactory.html#getXLogger(java.lang.Class))(LogExample.class);``@CustomLog创造 private static final _com.foo.your.Logger_ log = _com.foo.your.LoggerFactory.createYourLogger_(LogExample.class);
此选项要求您将配置添加到lombok.config文件中以指定@CustomLog应执行的操作。
例如:lombok.log.custom.declaration = com.foo.your.Logger com.foo.your.LoggerFactory.createYourLog(TYPE)(TOPIC)这将产生上面的陈述。首先是一个类型,它是记录器的类型,然后是一个空格,然后是记录器工厂的类型,然后是一个点,然后是记录器工厂方法的名称,然后是1或2个参数定义;最多有一个定义TOPIC,最多有一个没有定义TOPIC。每个参数定义都指定为带括号的逗号分隔参数类型列表。选项有:TYPE(通过这个@Log装饰类型,作为一个阶级), NAME(通过这个@Log装饰类的全名), TOPIC(通过在显式选择的主题串集@CustomLog注释),和NULL(通行证null)。
记录器类型是可选的;如果省略,则使用记录器出厂类型。(因此,如果记录器类具有创建记录器的静态方法,则可以缩短记录器定义)。
如果有一个公共的,开源的,有些常用的日志记录框架,但我们还没有明确的注释,请与我们联系。的主要目的@CustomLog是支持您内部的私有日志记录框架。
默认情况下,记录器的主题(或名称)将是带有@Log注释的类的(名称)。可以通过指定topic参数来自定义。例如:@XSlf4j(topic="reporting")。lombok代码:

  1. import lombok.Getter;
  2. public class GetterLazyExample {
  3. @Getter(lazy=true) private final double[] cached = expensive();
  4. private double[] expensive() {
  5. double[] result = new double[1000000];
  6. for (int i = 0; i < result.length; i++) {
  7. result[i] = Math.asin(i);
  8. }
  9. return result;
  10. }
  11. }

转换为java代码:

  1. public class GetterLazyExample {
  2. private final java.util.concurrent.AtomicReference<java.lang.Object> cached = new java.util.concurrent.AtomicReference<java.lang.Object>();
  3. public double[] getCached() {
  4. java.lang.Object value = this.cached.get();
  5. if (value == null) {
  6. synchronized(this.cached) {
  7. value = this.cached.get();
  8. if (value == null) {
  9. final double[] actualValue = expensive();
  10. value = actualValue == null ? this.cached : actualValue;
  11. this.cached.set(value);
  12. }
  13. }
  14. }
  15. return (double[])(value == this.cached ? null : value);
  16. }
  17. private double[] expensive() {
  18. double[] result = new double[1000000];
  19. for (int i = 0; i < result.length; i++) {
  20. result[i] = Math.asin(i);
  21. }
  22. return result;
  23. }
  24. }

@ToString

生成toString()方法

lombok代码:

  1. import lombok.ToString;
  2. @ToString
  3. public class ToStringExample {
  4. private static final int STATIC_VAR = 10;
  5. private String name;
  6. private Shape shape = new Square(5, 10);
  7. private String[] tags;
  8. @ToString.Exclude private int id;
  9. public String getName() {
  10. return this.name;
  11. }
  12. @ToString(callSuper=true, includeFieldNames=true)
  13. public static class Square extends Shape {
  14. private final int width, height;
  15. public Square(int width, int height) {
  16. this.width = width;
  17. this.height = height;
  18. }
  19. }
  20. }

转为java代码:

  1. import java.util.Arrays;
  2. public class ToStringExample {
  3. private static final int STATIC_VAR = 10;
  4. private String name;
  5. private Shape shape = new Square(5, 10);
  6. private String[] tags;
  7. private int id;
  8. public String getName() {
  9. return this.name;
  10. }
  11. public static class Square extends Shape {
  12. private final int width, height;
  13. public Square(int width, int height) {
  14. this.width = width;
  15. this.height = height;
  16. }
  17. @Override public String toString() {
  18. return "Square(super=" + super.toString() + ", width=" + this.width + ", height=" + this.height + ")";
  19. }
  20. }
  21. @Override public String toString() {
  22. return "ToStringExample(" + this.getName() + ", " + this.shape + ", " + Arrays.deepToString(this.tags) + ")";
  23. }
  24. }

@EqualsAndHashCode

生成equals(Object other)hashCode()方法的实现。默认情况下,它将使用所有非静态,非瞬态字段,但是您可以通过使用@EqualsAndHashCode.Include或标记类型成员来修改要使用的字段(甚至指定要使用各种方法的输出)@EqualsAndHashCode.Exclude。另外,您可以通过标记@EqualsAndHashCode.Include并使用来确切指定要使用的字段或方法@EqualsAndHashCode(onlyExplicitlyIncluded = true)
如果将其应用于@EqualsAndHashCode扩展另一个类的类,则此功能会有些棘手。通常,为此类自动生成equalsand hashCode方法是一个坏主意,因为超类还定义了字段,该字段也需要equals / hashCode代码,但不会生成此代码。通过设置callSuper真正的,可以包括equalshashCode你的超类中生成的方法的方法。对于hashCode,结果super.hashCode()包含在哈希算法中,对于equals,如果超级实现认为它与传入的对象不相等,则生成的方法将返回false。请注意,并非所有的equals实现都能正确处理这种情况。但是,龙目岛生成的equals实现可以正确处理这种情况,因此,如果它也具有lombok生成的equals方法,则可以安全地调用您的超类equals 。如果您有显式的超类,则必须提供一些价值callSuper以确认您已经考虑了它。否则将导致警告。
当您不扩展任何内容时, 将其设置callSupertruejava.lang.Object是一个编译时错误,因为它将使生成的equals()hashCode()实现具有与仅继承这些方法的行为相同的行为java.lang.Object:只有相同的对象才等于每个对象其他,并将具有相同的hashCode。扩展另一个类时未将其设置callSupertrue会生成警告,因为除非超类没有(相等的重要)字段,否则lombok无法为您生成一个实现,该实现将您的超类声明的字段考虑在内。您需要编写自己的实现,或依靠callSuper链接工具。您也可以使用lombok.equalsAndHashCode.callSuperconfig键。
Lombok 0.10中的新增内容:除非您的类是final并且java.lang.Objectextended,否则lombok会生成一个canEqual方法,这意味着JPA代理仍然可以与其基类相等,但是添加新状态的子类不会破坏等号契约。本文解释了为什么需要这种方法的复杂原因:如何用Java编写平等方法。如果层次结构中的所有类都是Scala案例类和具有lombok生成的equals方法的类的混合,则所有等式都将“正常工作”。如果您需要编写自己的equals方法,canEqual则更改equals和时应始终覆盖hashCode
Lombok 1.14.0中的新增功能:要将注释放在(和相关的)方法的other参数上,可以使用。不过要小心!这是一项实验功能。有关更多详细信息,请参阅onX功能的文档。 equals``canEqual``onParam=@__({@AnnotationsHere})

lombok代码:

  1. import lombok.EqualsAndHashCode;
  2. @EqualsAndHashCode
  3. public class EqualsAndHashCodeExample {
  4. private transient int transientVar = 10;
  5. private String name;
  6. private double score;
  7. @EqualsAndHashCode.Exclude private Shape shape = new Square(5, 10);
  8. private String[] tags;
  9. @EqualsAndHashCode.Exclude private int id;
  10. public String getName() {
  11. return this.name;
  12. }
  13. @EqualsAndHashCode(callSuper=true)
  14. public static class Square extends Shape {
  15. private final int width, height;
  16. public Square(int width, int height) {
  17. this.width = width;
  18. this.height = height;
  19. }
  20. }
  21. }

转为java代码:

  1. import java.util.Arrays;
  2. public class EqualsAndHashCodeExample {
  3. private transient int transientVar = 10;
  4. private String name;
  5. private double score;
  6. private Shape shape = new Square(5, 10);
  7. private String[] tags;
  8. private int id;
  9. public String getName() {
  10. return this.name;
  11. }
  12. @Override public boolean equals(Object o) {
  13. if (o == this) return true;
  14. if (!(o instanceof EqualsAndHashCodeExample)) return false;
  15. EqualsAndHashCodeExample other = (EqualsAndHashCodeExample) o;
  16. if (!other.canEqual((Object)this)) return false;
  17. if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
  18. if (Double.compare(this.score, other.score) != 0) return false;
  19. if (!Arrays.deepEquals(this.tags, other.tags)) return false;
  20. return true;
  21. }
  22. @Override public int hashCode() {
  23. final int PRIME = 59;
  24. int result = 1;
  25. final long temp1 = Double.doubleToLongBits(this.score);
  26. result = (result*PRIME) + (this.name == null ? 43 : this.name.hashCode());
  27. result = (result*PRIME) + (int)(temp1 ^ (temp1 >>> 32));
  28. result = (result*PRIME) + Arrays.deepHashCode(this.tags);
  29. return result;
  30. }
  31. protected boolean canEqual(Object other) {
  32. return other instanceof EqualsAndHashCodeExample;
  33. }
  34. public static class Square extends Shape {
  35. private final int width, height;
  36. public Square(int width, int height) {
  37. this.width = width;
  38. this.height = height;
  39. }
  40. @Override public boolean equals(Object o) {
  41. if (o == this) return true;
  42. if (!(o instanceof Square)) return false;
  43. Square other = (Square) o;
  44. if (!other.canEqual((Object)this)) return false;
  45. if (!super.equals(o)) return false;
  46. if (this.width != other.width) return false;
  47. if (this.height != other.height) return false;
  48. return true;
  49. }
  50. @Override public int hashCode() {
  51. final int PRIME = 59;
  52. int result = 1;
  53. result = (result*PRIME) + super.hashCode();
  54. result = (result*PRIME) + this.width;
  55. result = (result*PRIME) + this.height;
  56. return result;
  57. }
  58. protected boolean canEqual(Object other) {
  59. return other instanceof Square;
  60. }
  61. }
  62. }

@ NoArgsConstructor,@ RequiredArgsConstructor,@ AllArgsConstructor

  • NoArgsConstructor将生成没有参数的构造函数。如果这是不可能的(由于final字段),则将导致编译器错误,除非@NoArgsConstructor(force = true)使用,否则将使用0/ false/ 初始化所有final字段null。对于具有约束的字段(例如,@NonNull字段),不会生成检查,因此请注意,直到稍后对这些字段进行适当初始化之前,通常不会满足这些约束。某些Java构造(例如,休眠和服务提供者接口)需要无参数构造函数。此注释主要与@Data生成注释的其他构造函数之一或其中一个结合使用。
  • @RequiredArgsConstructor为每个需要特殊处理的字段生成一个带有1个参数的构造函数。所有未初始化的final字段都将获得参数,以及任何标记为@NonNull声明为未初始化的字段。对于标记为的字段@NonNull,还将生成一个显式的null检查。构造函数将抛出一个NullPointerException用于@NonNull包含contains的字段的参数(如果有)null。参数的顺序与字段在类中出现的顺序匹配。
  • @AllArgsConstructor为类中的每个字段生成一个带有1个参数的构造函数。标记为的字段@NonNull将对这些参数进行空检查。

这些注释中的每一个都允许使用备用格式,其中生成的构造函数始终是私有的,并且会生成环绕私有构造函数的其他静态工厂方法。通过提供staticName注释的值来启用此模式,如下所示:@RequiredArgsConstructor(staticName="of")。与常规构造函数不同,这种静态工厂方法将推断泛型。这意味着您的API用户可以写MapEntry.of("foo", 5)而不是写更长的时间new MapEntry<String, Integer>("foo", 5)

lombok代码:

  1. import lombok.AccessLevel;
  2. import lombok.RequiredArgsConstructor;
  3. import lombok.AllArgsConstructor;
  4. import lombok.NonNull;
  5. @RequiredArgsConstructor(staticName = "of")
  6. @AllArgsConstructor(access = AccessLevel.PROTECTED)
  7. public class ConstructorExample<T> {
  8. private int x, y;
  9. @NonNull private T description;
  10. @NoArgsConstructor
  11. public static class NoArgsExample {
  12. @NonNull private String field;
  13. }
  14. }

转为java代码:

  1. public class ConstructorExample<T> {
  2. private int x, y;
  3. @NonNull private T description;
  4. private ConstructorExample(T description) {
  5. if (description == null) throw new NullPointerException("description");
  6. this.description = description;
  7. }
  8. public static <T> ConstructorExample<T> of(T description) {
  9. return new ConstructorExample<T>(description);
  10. }
  11. @java.beans.ConstructorProperties({"x", "y", "description"})
  12. protected ConstructorExample(int x, int y, T description) {
  13. if (description == null) throw new NullPointerException("description");
  14. this.x = x;
  15. this.y = y;
  16. this.description = description;
  17. }
  18. public static class NoArgsExample {
  19. @NonNull private String field;
  20. public NoArgsExample() {
  21. }
  22. }
  23. }

@Dataa

@Data是捆绑的特征的方便快捷方式注释@ToString@EqualsAndHashCode@Getter/@Setter@RequiredArgsConstructor在一起:即,@Data产生所有其通常与简单的POJO(普通旧式Java对象)和豆类相关的样板:吸气剂对于所有字段,设置器为所有非final字段和适当的toStringequals以及hashCode涉及该类字段的实现,以及一个构造函数,该构造函数初始化所有final字段以及所有未标记有初始化程序的非final字段,@NonNull以确保该字段永远不会空值。
@Data就像具有隐式@Getter@Setter@ToString@EqualsAndHashCode@RequiredArgsConstructor在类注解(不同之处在于没有构造将生成如果已经存在任何明确写入构造函数)。但是,这些注释的参数(如callSuperincludeFieldNamesexclude)不能与设置@Data。如果您需要为这些参数中的任何一个设置非默认值,只需显式添加这些注释即可;@Data非常聪明,可以遵循这些注释。
所有生成的getter和setter将为public。要覆盖访问级别,请使用显式@Setter和/或@Getter注释对字段或类进行注释。您也可以使用此批注(通过将其与结合使用AccessLevel.NONE)来完全禁止生成getter和/或setter。
标记为的所有字段transient都不会考虑hashCodeequals。所有静态字段都将被完全跳过(任何生成的方法都不会考虑,并且不会为它们创建setter / getter)。
如果该类已经包含与通常会生成的任何方法具有相同名称和参数计数的方法,则不会生成该方法,并且不会发出警告或错误。例如,如果您已经有一个带有签名的方法,则不会生成equals(AnyType param)任何equals方法,即使从技术上讲,由于具有不同的参数类型,它可能是完全不同的方法。该规则同样适用于构造函数(任何显式构造将防止@Data从生成一个),以及toStringequals和所有的getter和setter。您可以标记任何构造函数或方法@lombok.experimental.Tolerate以将其从lombok中隐藏。
@Data可以处理字段的泛型参数。为了减少使用泛型为类构造对象时的样板,可以使用staticConstructor参数生成私有构造函数,以及返回新实例的静态方法。这样,javac将推断变量名称。因此,通过这样声明:@Data(staticConstructor="of") class Foo<T> { private T x;}您可以Foo通过编写:创建新实例,Foo.of(5);而不必编写:new Foo<Integer>(5);

lombok代码:

  1. import lombok.AccessLevel;
  2. import lombok.Setter;
  3. import lombok.Data;
  4. import lombok.ToString;
  5. @Data public class DataExample {
  6. private final String name;
  7. @Setter(AccessLevel.PACKAGE) private int age;
  8. private double score;
  9. private String[] tags;
  10. @ToString(includeFieldNames=true)
  11. @Data(staticConstructor="of")
  12. public static class Exercise<T> {
  13. private final String name;
  14. private final T value;
  15. }
  16. }

转为java代码:

  1. import java.util.Arrays;
  2. public class DataExample {
  3. private final String name;
  4. private int age;
  5. private double score;
  6. private String[] tags;
  7. public DataExample(String name) {
  8. this.name = name;
  9. }
  10. public String getName() {
  11. return this.name;
  12. }
  13. void setAge(int age) {
  14. this.age = age;
  15. }
  16. public int getAge() {
  17. return this.age;
  18. }
  19. public void setScore(double score) {
  20. this.score = score;
  21. }
  22. public double getScore() {
  23. return this.score;
  24. }
  25. public String[] getTags() {
  26. return this.tags;
  27. }
  28. public void setTags(String[] tags) {
  29. this.tags = tags;
  30. }
  31. @Override public String toString() {
  32. return "DataExample(" + this.getName() + ", " + this.getAge() + ", " + this.getScore() + ", " + Arrays.deepToString(this.getTags()) + ")";
  33. }
  34. protected boolean canEqual(Object other) {
  35. return other instanceof DataExample;
  36. }
  37. @Override public boolean equals(Object o) {
  38. if (o == this) return true;
  39. if (!(o instanceof DataExample)) return false;
  40. DataExample other = (DataExample) o;
  41. if (!other.canEqual((Object)this)) return false;
  42. if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
  43. if (this.getAge() != other.getAge()) return false;
  44. if (Double.compare(this.getScore(), other.getScore()) != 0) return false;
  45. if (!Arrays.deepEquals(this.getTags(), other.getTags())) return false;
  46. return true;
  47. }
  48. @Override public int hashCode() {
  49. final int PRIME = 59;
  50. int result = 1;
  51. final long temp1 = Double.doubleToLongBits(this.getScore());
  52. result = (result*PRIME) + (this.getName() == null ? 43 : this.getName().hashCode());
  53. result = (result*PRIME) + this.getAge();
  54. result = (result*PRIME) + (int)(temp1 ^ (temp1 >>> 32));
  55. result = (result*PRIME) + Arrays.deepHashCode(this.getTags());
  56. return result;
  57. }
  58. public static class Exercise<T> {
  59. private final String name;
  60. private final T value;
  61. private Exercise(String name, T value) {
  62. this.name = name;
  63. this.value = value;
  64. }
  65. public static <T> Exercise<T> of(String name, T value) {
  66. return new Exercise<T>(name, value);
  67. }
  68. public String getName() {
  69. return this.name;
  70. }
  71. public T getValue() {
  72. return this.value;
  73. }
  74. @Override public String toString() {
  75. return "Exercise(name=" + this.getName() + ", value=" + this.getValue() + ")";
  76. }
  77. protected boolean canEqual(Object other) {
  78. return other instanceof Exercise;
  79. }
  80. @Override public boolean equals(Object o) {
  81. if (o == this) return true;
  82. if (!(o instanceof Exercise)) return false;
  83. Exercise<?> other = (Exercise<?>) o;
  84. if (!other.canEqual((Object)this)) return false;
  85. if (this.getName() == null ? other.getValue() != null : !this.getName().equals(other.getName())) return false;
  86. if (this.getValue() == null ? other.getValue() != null : !this.getValue().equals(other.getValue())) return false;
  87. return true;
  88. }
  89. @Override public int hashCode() {
  90. final int PRIME = 59;
  91. int result = 1;
  92. result = (result*PRIME) + (this.getName() == null ? 43 : this.getName().hashCode());
  93. result = (result*PRIME) + (this.getValue() == null ? 43 : this.getValue().hashCode());
  94. return result;
  95. }
  96. }
  97. }

@Value

@Value是的不变变体@Data; 所有字段由privatefinal默认情况下,也不会产生setter方法。该类本身也是final默认设置的,因为不可改变性不能强加到子类上。像@Data,有用的toString()equals()并且hashCode()也被生成的方法,每个字段得到的吸气剂的方法,以及覆盖每个参数(除了一个构造final了在字段声明初始化字段)也被产生。
实际上,@Value是的简写:final @ToString @EqualsAndHashCode @AllArgsConstructor @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) @Getter,除了明确包括任何相关方法的实现以外,仅意味着不会生成部分并且不会发出警告。例如,如果您自己编写toString,则不会发生错误,并且lombok不会生成toString。同样,任何显式构造函数,无论参数列表如何,都意味着lombok不会生成构造函数。如果您确实希望lombok生成all-args构造函数,请添加@AllArgsConstructor到该类中。请注意,如果@Builder和@Value都在一个类上,则@Builder要使“ @Value”要使之成为公共对象的包私有allargs构造函数。您可以标记任何构造函数或方法@lombok.experimental.Tolerate以将其从lombok中隐藏。
使用字段上的显式访问级别或使用@NonFinal@PackagePrivate批注,可以覆盖默认的最终行为和默认的私有行为。@NonFinal也可以用于类中以删除最终关键字。通过显式使用该批注,
可以覆盖组成任何“零件”的任何默认行为@Value

lombok代码:

  1. import lombok.AccessLevel;
  2. import lombok.experimental.NonFinal;
  3. import lombok.experimental.Value;
  4. import lombok.experimental.Wither;
  5. import lombok.ToString;
  6. @Value public class ValueExample {
  7. String name;
  8. @Wither(AccessLevel.PACKAGE) @NonFinal int age;
  9. double score;
  10. protected String[] tags;
  11. @ToString(includeFieldNames=true)
  12. @Value(staticConstructor="of")
  13. public static class Exercise<T> {
  14. String name;
  15. T value;
  16. }
  17. }

转为java代码:

  1. import java.util.Arrays;
  2. public final class ValueExample {
  3. private final String name;
  4. private int age;
  5. private final double score;
  6. protected final String[] tags;
  7. @java.beans.ConstructorProperties({"name", "age", "score", "tags"})
  8. public ValueExample(String name, int age, double score, String[] tags) {
  9. this.name = name;
  10. this.age = age;
  11. this.score = score;
  12. this.tags = tags;
  13. }
  14. public String getName() {
  15. return this.name;
  16. }
  17. public int getAge() {
  18. return this.age;
  19. }
  20. public double getScore() {
  21. return this.score;
  22. }
  23. public String[] getTags() {
  24. return this.tags;
  25. }
  26. @java.lang.Override
  27. public boolean equals(Object o) {
  28. if (o == this) return true;
  29. if (!(o instanceof ValueExample)) return false;
  30. final ValueExample other = (ValueExample)o;
  31. final Object this$name = this.getName();
  32. final Object other$name = other.getName();
  33. if (this$name == null ? other$name != null : !this$name.equals(other$name)) return false;
  34. if (this.getAge() != other.getAge()) return false;
  35. if (Double.compare(this.getScore(), other.getScore()) != 0) return false;
  36. if (!Arrays.deepEquals(this.getTags(), other.getTags())) return false;
  37. return true;
  38. }
  39. @java.lang.Override
  40. public int hashCode() {
  41. final int PRIME = 59;
  42. int result = 1;
  43. final Object $name = this.getName();
  44. result = result * PRIME + ($name == null ? 43 : $name.hashCode());
  45. result = result * PRIME + this.getAge();
  46. final long $score = Double.doubleToLongBits(this.getScore());
  47. result = result * PRIME + (int)($score >>> 32 ^ $score);
  48. result = result * PRIME + Arrays.deepHashCode(this.getTags());
  49. return result;
  50. }
  51. @java.lang.Override
  52. public String toString() {
  53. return "ValueExample(name=" + getName() + ", age=" + getAge() + ", score=" + getScore() + ", tags=" + Arrays.deepToString(getTags()) + ")";
  54. }
  55. ValueExample withAge(int age) {
  56. return this.age == age ? this : new ValueExample(name, age, score, tags);
  57. }
  58. public static final class Exercise<T> {
  59. private final String name;
  60. private final T value;
  61. private Exercise(String name, T value) {
  62. this.name = name;
  63. this.value = value;
  64. }
  65. public static <T> Exercise<T> of(String name, T value) {
  66. return new Exercise<T>(name, value);
  67. }
  68. public String getName() {
  69. return this.name;
  70. }
  71. public T getValue() {
  72. return this.value;
  73. }
  74. @java.lang.Override
  75. public boolean equals(Object o) {
  76. if (o == this) return true;
  77. if (!(o instanceof ValueExample.Exercise)) return false;
  78. final Exercise<?> other = (Exercise<?>)o;
  79. final Object this$name = this.getName();
  80. final Object other$name = other.getName();
  81. if (this$name == null ? other$name != null : !this$name.equals(other$name)) return false;
  82. final Object this$value = this.getValue();
  83. final Object other$value = other.getValue();
  84. if (this$value == null ? other$value != null : !this$value.equals(other$value)) return false;
  85. return true;
  86. }
  87. @java.lang.Override
  88. public int hashCode() {
  89. final int PRIME = 59;
  90. int result = 1;
  91. final Object $name = this.getName();
  92. result = result * PRIME + ($name == null ? 43 : $name.hashCode());
  93. final Object $value = this.getValue();
  94. result = result * PRIME + ($value == null ? 43 : $value.hashCode());
  95. return result;
  96. }
  97. @java.lang.Override
  98. public String toString() {
  99. return "ValueExample.Exercise(name=" + getName() + ", value=" + getValue() + ")";
  100. }
  101. }
  102. }

@Builder

建造者模式,生成对象。
@Builder 使您可以自动生成使您的类可实例化的代码,例如:
Person.builder().name("Adam Savage").city("San Francisco").job("Mythbusters").job("Unchained Reaction").build();
@Builder可以放在类,构造函数或方法上。虽然“基于类”和“基于构造函数”模式是最常见的用例,但是@Builder使用“方法”用例最容易解释。
用注释的方法@Builder(从现在开始称为target)将生成以下7件事:

  • 一个内部静态类,名为_Foo_Builder,具有与静态方法相同的类型参数(称为builder)。
  • 构建器中目标的每个参数有一个私有的非静态非最终字段。
  • builder中:包私有的无参数空构造函数。
  • 构建器中:对目标的每个参数使用类似于“ setter”的方法:与该参数具有相同的类型和相同的名称。如上例所示,它返回构建器本身,以便可以将setter调用链接起来。
  • 构建器中build()调用该方法的方法,并在每个字段中传递。它返回与目标返回类型相同的类型。
  • 构建器中:明智的toString()实现。
  • 在包含target的类中:一个builder()方法,该方法创建builder的新实例。

如果该元素已存在,则每个列出的生成元素都将被静默跳过(忽略参数计数,仅查看名称)。这包括构建器本身:如果该类已经存在,则lombok会简单地开始在此现有类中注入字段和方法,除非要注入的字段/方法当然已经存在。但是,您不能在生成器类上放置其他任何生成lombok批注的方法(或构造函数);例如,您不能放入@EqualsAndHashCodebuilder类。
@Builder可以为收集参数/字段生成所谓的“奇异”方法。它们采用1个元素而不是整个列表,然后将该元素添加到列表中。例如:Person.builder().job("Mythbusters").job("Unchained Reaction").build();将导致该List<String> jobs字段中包含2个字符串。要获得此行为,必须使用注释字段/参数@Singular。该功能具有其自己的文档
现在,“方法”模式已经很清楚了,@Builder在构造函数上添加注释的功能类似。实际上,构造函数只是具有特殊语法以调用它们的静态方法:它们的“返回类型”是它们构造的类,并且它们的类型参数与类本身的类型参数相同。
最后,应用于@Builder类就像是将其添加@AllArgsConstructor(access = AccessLevel.PACKAGE)到该类并将@Builder注释应用于此all-args-constructor一样。仅当您自己未编写任何显式构造函数时,此方法才有效。如果确实有显式构造函数,则将@Builder注释放在构造函数上而不是在类上。请注意,如果将@Value和@Builder都放在一个类上,则@Builder要生成“ wins”的程序包私有构造函数,而禁止@Value要生成的构造函数。
如果@Builder用于生成生成器以生成您自己的类的实例(除非添加@Builder到不返回您自己的类型的方法中,否则通常都是这种情况),您还可以@Builder(toBuilder = true)在类中使用生成实例方法toBuilder();它创建一个新的构建器,该构建器以该实例的所有值开始。您可以将@Builder.ObtainVia注释放在参数(对于构造函数或方法的情况)或字段(对于@Builder类型的情况)上,以指示从该实例获取该字段/参数的值的替代方法。例如,您可以指定要调用的方法:@Builder.ObtainVia(method = "calculateFoo")
构建器类的名称为_Foobar_Builder,其中Foobar目标的返回类型的简化的,以标题区分大小写的形式-即,@Builderon构造函数和类型的类型名称,以及@Builderon方法的返回类型的名称。。例如,如果@Builder应用于名为的类com.yoyodyne.FancyList<T>,则构建器名称将为FancyListBuilder<T>。如果@Builder将应用于返回的方法,void则将命名构建器VoidBuilder
构建器的可配置方面包括:

  • 生成器的类名(默认:返回类型+“生成器”)
  • 版本()方法的名称(默认:"build"
  • 生成器()方法的名称(默认:"builder"
  • 如果需要toBuilder()(默认值:否)
  • 所有生成的元素的访问级别(默认值:)public
  • (不推荐使用)如果您希望构建器的“ set”方法具有前缀,即Person.builder().setName("Jane").build()而不是前缀,Person.builder().name("Jane").build()则应为前缀。

用法示例,其中所有选项均从其默认值更改:
@Builder(builderClassName = "HelloWorldBuilder", buildMethodName = "execute", builderMethodName = "helloWorld", toBuilder = true, access = AccessLevel.PRIVATE, setterPrefix = "set")

@Builder.Default

如果在构建会话期间从未设置过某个字段/参数,则它始终为0 null// false。如果您@Builder使用的是类(而不是方法或构造函数),则可以直接在字段上指定默认值,并使用@Builder.Default
@Builder.Default private final long created = System.currentTimeMillis();

@Singular

通过使用注释对参数之一(如果使用注释方法或构造函数@Builder)或字段(如果使用注释字段@Builder@Singular,lombok会将该生成器节点视为一个集合,并生成2个“ adder”方法而不是“设置方法。一个将单个元素添加到集合中,另一个将另一个集合的所有元素添加到集合中。不会生成仅设置集合(替换已添加的内容)的设置器。还生成“清除”方法。这些“单一”生成器非常复杂,以保证以下属性:

  • 调用时build(),产生的集合将是不可变的。
  • 调用后调用“ adder”方法或“ clear”方法之一build()不会修改任何已生成的对象,并且,如果build()稍后再次调用,则会生成自创建器创建以来添加了所有元素的另一个集合。
  • 产生的集合将压缩为最小的可行格式,同时保持有效。

@Singular只能应用于lombok已知的集合类型。当前,支持的类型是:

  • java.util
    • IterableCollectionListArrayList通常情况下以不可修改的压缩为后盾)。
    • SetSortedSetNavigableSet(由大小适中的不可修改文件HashSetTreeSet一般情况下的文件支持)。
    • MapSortedMapNavigableMap(由大小适中的不可修改文件HashMapTreeMap一般情况下的文件支持)。
  • Guavacom.google.common.collect
    • ImmutableCollectionImmutableList(由的构建器功能支持ImmutableList)。
    • ImmutableSetImmutableSortedSet(由这些类型的构建器功能支持)。
    • ImmutableMapImmutableBiMapImmutableSortedMap(由这些类型的构建器功能支持)。
    • ImmutableTable(由的构建器功能支持ImmutableTable)。

如果您的标识符是用普通英语书写的,则lombok假定上面带有任何集合的名称@Singular都是英语的复数形式,并将尝试自动将该名称单数化。如果可能的话,附加方法将使用此名称。例如,如果您的集合被调用statuses,那么add-one方法将被自动调用status。您还可以通过将奇异形式作为参数传递给注释,从而明确指定标识符的奇异形式,例如:@Singular("axis") List<Line> axes;
如果lombok无法单数化您的标识符,或者模棱两可,则lombok会生成错误并迫使您明确指定单数名称。
下面的代码片段没有显示lombok对于@Singular字段/参数产生的内容,因为它相当复杂。您可以在此处查看摘要。
如果还使用setterPrefix = "with",则生成的名称例如是withName(添加1个名称),withNames(添加许多名称)和clearNames(重置所有名称)。
通常,生成的“复数形式”方法(接收一个集合,并添加此集合中的每个元素)将检查是否以null相同的方式传递了a @NonNull(默认情况下,抛出NullPointerException带有适当消息的a)。但是,你也可以告诉龙目忽略这样的集合(因此,加什么,立即返回)@Singular(ignoreNullCollections = true

与Jackson

您可以自定义构建器的各个部分,例如,通过自己构建构建器类,在构建器类中添加其他方法,或在构建器类中注释方法。Lombok将生成您不手动添加的所有内容,并将其放入此构建器类。例如,如果您尝试将杰克逊配置为对集合使用特定的子类型,则可以编写如下内容:

  1. @Value @Builder
  2. @JsonDeserialize(builder = JacksonExample.JacksonExampleBuilder.class)
  3. public class JacksonExample {
  4. @Singular(nullBehavior = NullCollectionBehavior.IGNORE) private List<Foo> foos;
  5. @JsonPOJOBuilder(withPrefix = "")
  6. public static class JacksonExampleBuilder implements JacksonExampleBuilderMeta {
  7. }
  8. private interface JacksonExampleBuilderMeta {
  9. @JsonDeserialize(contentAs = FooImpl.class) JacksonExampleBuilder foos(List<? extends Foo> foos)
  10. }
  11. }

lombok代码:

  1. import lombok.Builder;
  2. import lombok.Singular;
  3. import java.util.Set;
  4. @Builder
  5. public class BuilderExample {
  6. @Builder.Default private long created = System.currentTimeMillis();
  7. private String name;
  8. private int age;
  9. @Singular private Set<String> occupations;
  10. }

转换为java代码:

  1. import java.util.Set;
  2. public class BuilderExample {
  3. private long created;
  4. private String name;
  5. private int age;
  6. private Set<String> occupations;
  7. BuilderExample(String name, int age, Set<String> occupations) {
  8. this.name = name;
  9. this.age = age;
  10. this.occupations = occupations;
  11. }
  12. private static long $default$created() {
  13. return System.currentTimeMillis();
  14. }
  15. public static BuilderExampleBuilder builder() {
  16. return new BuilderExampleBuilder();
  17. }
  18. public static class BuilderExampleBuilder {
  19. private long created;
  20. private boolean created$set;
  21. private String name;
  22. private int age;
  23. private java.util.ArrayList<String> occupations;
  24. BuilderExampleBuilder() {
  25. }
  26. public BuilderExampleBuilder created(long created) {
  27. this.created = created;
  28. this.created$set = true;
  29. return this;
  30. }
  31. public BuilderExampleBuilder name(String name) {
  32. this.name = name;
  33. return this;
  34. }
  35. public BuilderExampleBuilder age(int age) {
  36. this.age = age;
  37. return this;
  38. }
  39. public BuilderExampleBuilder occupation(String occupation) {
  40. if (this.occupations == null) {
  41. this.occupations = new java.util.ArrayList<String>();
  42. }
  43. this.occupations.add(occupation);
  44. return this;
  45. }
  46. public BuilderExampleBuilder occupations(Collection<? extends String> occupations) {
  47. if (this.occupations == null) {
  48. this.occupations = new java.util.ArrayList<String>();
  49. }
  50. this.occupations.addAll(occupations);
  51. return this;
  52. }
  53. public BuilderExampleBuilder clearOccupations() {
  54. if (this.occupations != null) {
  55. this.occupations.clear();
  56. }
  57. return this;
  58. }
  59. public BuilderExample build() {
  60. // complicated switch statement to produce a compact properly sized immutable set omitted.
  61. Set<String> occupations = ...;
  62. return new BuilderExample(created$set ? created : BuilderExample.$default$created(), name, age, occupations);
  63. }
  64. @java.lang.Override
  65. public String toString() {
  66. return "BuilderExample.BuilderExampleBuilder(created = " + this.created + ", name = " + this.name + ", age = " + this.age + ", occupations = " + this.occupations + ")";
  67. }
  68. }
  69. }

@SneakyThrows

@SneakyThrows可以用于偷偷地抛出已检查的异常,而无需在方法的throws子句中实际声明。当然,应该谨慎使用这种有争议的功能。lombok生成的代码不会忽略,包装,替换或以其他方式修改引发的检查异常;它只是伪造编译器。在JVM(类文件)级别上,无论方法的throws子句如何,都可以引发所有已检查或未检查的异常,这就是为什么这样做的原因。
当您想退出检查异常机制时,常见的用例围绕两种情况:

  • 不必要的严格接口,例如Runnable-无论异常是否从您的run()方法中传播出去(是否经过检查),都会将其传递给Thread的未处理异常处理程序。捕获已检查的异常并将其包装为某种形式RuntimeException只会掩盖问题的真正原因。
  • “不可能”例外。例如,new String(someByteArray, "UTF-8");声明可以抛出一个,UnsupportedEncodingException但是根据JVM规范,UTF-8 必须始终可用。一个UnsupportedEncodingException在这里大约是有可能的ClassNotFoundError,当你使用一个String对象,而你没有赶上那些要么!

使用lambda语法(arg -> action)时,受到不必要的严格接口的约束尤为常见。但是,lambda无法注释,这意味着将其@SneakyThrows与lambda组合使用并不容易。
请注意,直接捕获偷偷地抛出的检查类型是不可能的,因为javac不允许您为try主体中没有方法调用声明为抛出的异常类型编写catch块。在上面列出的两个用例中,此问题均无关紧要,因此,应将此作为警告,如果您不进行@SneakyThrows任何深思熟虑,就不要使用该机制!
您可以将任意数量的异常传递给@SneakyThrows注释。如果没有通过任何异常,则可以偷偷地抛出任何异常。

lombok代码:

  1. import lombok.SneakyThrows;
  2. public class SneakyThrowsExample implements Runnable {
  3. @SneakyThrows(UnsupportedEncodingException.class)
  4. public String utf8ToString(byte[] bytes) {
  5. return new String(bytes, "UTF-8");
  6. }
  7. @SneakyThrows
  8. public void run() {
  9. throw new Throwable();
  10. }
  11. }

转换为java代码:

  1. import lombok.Lombok;
  2. public class SneakyThrowsExample implements Runnable {
  3. public String utf8ToString(byte[] bytes) {
  4. try {
  5. return new String(bytes, "UTF-8");
  6. } catch (UnsupportedEncodingException e) {
  7. throw Lombok.sneakyThrow(e);
  8. }
  9. }
  10. public void run() {
  11. try {
  12. throw new Throwable();
  13. } catch (Throwable t) {
  14. throw Lombok.sneakyThrow(t);
  15. }
  16. }
  17. }

@With

不变属性的二传手的第二个最佳选择是构造对象的副本,但为该字段添加一个新值。生成此克隆的方法正是@With生成的withFieldName(newValue)方法:一种生成克隆的方法,但关联字段的新值除外。
例如,如果您创建public class Point { private final int x, y; },则setter没有意义,因为这些字段是最终字段。@With可以withX(int newXValue)为您生成一个方法,该方法将返回一个新点,其中包含的提供的值x和的相同的值y
@With为了完成它的工作依赖于所有领域的构造函数。如果此构造函数不存在,则@With注释将导致编译时错误消息。您可以使用Lombok自己的@AllArgsConstructor,也Value可以自动生成所有args构造函数,也可以使用它。如果您手动编写此构造函数,当然也可以接受。它必须包含相同词法顺序的所有非静态字段。
就像@Setter,您可以指定访问级别,以防生成的with方法不同于public
@With(level = AccessLevel.PROTECTED)。与一样@Setter,您还可以@With在类型上添加注释,这意味着with将为每个字段(甚至非最终字段)生成一个方法。
要将注释放在生成的方法上,可以使用onMethod=@__({@AnnotationsHere})。不过要小心!这是一项实验功能。有关更多详细信息,请参阅onX功能的文档。
字段上的javadoc将被复制为使用方法生成。通常,将复制所有文本,并将@param移至 with方法,而@return从with方法的javadoc中删除行。移动的意思是:从字段的javadoc中删除。也可以为with方法的javadoc定义唯一的文本。为此,您创建一个名为的“部分” WITH。一节是Javadoc中的一行,其中包含2个或更多的破折号,然后是文本“ WITH”,后跟2个或更多的破折号,而该行上没有其他内容。如果您使用的部分,@return@param剥离/复制该节不再做(移动@param进线部分)。

lombok代码:

  1. import lombok.AccessLevel;
  2. import lombok.NonNull;
  3. import lombok.With;
  4. public class WithExample {
  5. @With(AccessLevel.PROTECTED) @NonNull private final String name;
  6. @With private final int age;
  7. public WithExample(String name, int age) {
  8. if (name == null) throw new NullPointerException();
  9. this.name = name;
  10. this.age = age;
  11. }
  12. }

转换为java代码:

  1. import lombok.NonNull;
  2. public class WithExample {
  3. private @NonNull final String name;
  4. private final int age;
  5. public WithExample(String name, int age) {
  6. if (name == null) throw new NullPointerException();
  7. this.name = name;
  8. this.age = age;
  9. }
  10. protected WithExample withName(@NonNull String name) {
  11. if (name == null) throw new java.lang.NullPointerException("name");
  12. return this.name == name ? this : new WithExample(name, age);
  13. }
  14. public WithExample withAge(int age) {
  15. return this.age == age ? this : new WithExample(name, age);
  16. }
  17. }

@Log

@CommonsLog

@Flogger

@JBossLog

@Log

@Log4j

@Log4j2

@Slf4j

@XSlf4j

@CustomLog

生成日志工具。
您将的变体@Log放在您的类上(适用于您所使用的日志记录系统的任何一种);然后,您将拥有一个静态的final log字段,该字段按照使用的日志记录框架通常规定的方式进行初始化,然后可以使用该方法编写日志语句。
有几种选择:

  • @CommonsLog创造 private static final [org.apache.commons.logging.Log](https://commons.apache.org/logging/apidocs/org/apache/commons/logging/Log.html) log = [org.apache.commons.logging.LogFactory.getLog](https://commons.apache.org/logging/apidocs/org/apache/commons/logging/LogFactory.html#getLog(java.lang.Class))(LogExample.class);
  • @Flogger创造 private static final [com.google.common.flogger.FluentLogger](https://google.github.io/flogger/) log = com.google.common.flogger.FluentLogger.forEnclosingClass();
  • @JBossLog创造 private static final [org.jboss.logging.Logger](https://docs.jboss.org/jbosslogging/latest/org/jboss/logging/Logger.html) log = [org.jboss.logging.Logger.getLogger](https://docs.jboss.org/jbosslogging/latest/org/jboss/logging/Logger.html#getLogger(java.lang.Class))(LogExample.class);
  • @Log创造 private static final [java.util.logging.Logger](https://docs.oracle.com/javase/6/docs/api/java/util/logging/Logger.html) log = [java.util.logging.Logger.getLogger](https://docs.oracle.com/javase/6/docs/api/java/util/logging/Logger.html#getLogger(java.lang.String))(LogExample.class.getName());
  • @Log4j创造 private static final [org.apache.log4j.Logger](https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/Logger.html) log = [org.apache.log4j.Logger.getLogger](https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/Logger.html#getLogger(java.lang.Class))(LogExample.class);
  • @Log4j2创造 private static final [org.apache.logging.log4j.Logger](https://logging.apache.org/log4j/2.0/log4j-api/apidocs/org/apache/logging/log4j/Logger.html) log = [org.apache.logging.log4j.LogManager.getLogger](https://logging.apache.org/log4j/2.0/log4j-api/apidocs/org/apache/logging/log4j/LogManager.html#getLogger(java.lang.Class))(LogExample.class);
  • @Slf4j创造 private static final [org.slf4j.Logger](https://www.slf4j.org/api/org/slf4j/Logger.html) log = [org.slf4j.LoggerFactory.getLogger](https://www.slf4j.org/api/org/slf4j/LoggerFactory.html#getLogger(java.lang.Class))(LogExample.class);
  • @XSlf4j创造 private static final [org.slf4j.ext.XLogger](https://www.slf4j.org/api/org/slf4j/ext/XLogger.html) log = [org.slf4j.ext.XLoggerFactory.getXLogger](https://www.slf4j.org/api/org/slf4j/ext/XLoggerFactory.html#getXLogger(java.lang.Class))(LogExample.class);
  • @CustomLog创造 private static final _com.foo.your.Logger_ log = _com.foo.your.LoggerFactory.createYourLogger_(LogExample.class);

此选项要求您将配置添加到lombok.config文件中以指定@CustomLog应执行的操作。
例如:lombok.log.custom.declaration = com.foo.your.Logger com.foo.your.LoggerFactory.createYourLog(TYPE)(TOPIC)这将产生上面的陈述。首先是一个类型,它是记录器的类型,然后是一个空格,然后是记录器工厂的类型,然后是一个点,然后是记录器工厂方法的名称,然后是1或2个参数定义;最多有一个定义TOPIC,最多有一个没有定义TOPIC。每个参数定义都指定为带括号的逗号分隔参数类型列表。选项有:TYPE(通过这个@Log装饰类型,作为一个阶级), NAME(通过这个@Log装饰类的全名), TOPIC(通过在显式选择的主题串集@CustomLog注释),和NULL(通行证null)。
记录器类型是可选的;如果省略,则使用记录器出厂类型。(因此,如果记录器类具有创建记录器的静态方法,则可以缩短记录器定义)。
如果有一个公共的,开源的,有些常用的日志记录框架,但我们还没有明确的注释,请与我们联系。的主要目的@CustomLog是支持您内部的私有日志记录框架。
默认情况下,记录器的主题(或名称)将是带有@Log注释的类的(名称)。可以通过指定topic参数来自定义。例如:@XSlf4j(topic="reporting")

lombok代码:

  1. import lombok.extern.java.Log;
  2. import lombok.extern.slf4j.Slf4j;
  3. @Log
  4. public class LogExample {
  5. public static void main(String... args) {
  6. log.severe("Something's wrong here");
  7. }
  8. }
  9. @Slf4j
  10. public class LogExampleOther {
  11. public static void main(String... args) {
  12. log.error("Something else is wrong here");
  13. }
  14. }
  15. @CommonsLog(topic="CounterLog")
  16. public class LogExampleCategory {
  17. public static void main(String... args) {
  18. log.error("Calling the 'CounterLog' with a message");
  19. }
  20. }

转换为java代码:

  1. public class LogExample {
  2. private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());
  3. public static void main(String... args) {
  4. log.severe("Something's wrong here");
  5. }
  6. }
  7. public class LogExampleOther {
  8. private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExampleOther.class);
  9. public static void main(String... args) {
  10. log.error("Something else is wrong here");
  11. }
  12. }
  13. public class LogExampleCategory {
  14. private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog("CounterLog");
  15. public static void main(String... args) {
  16. log.error("Calling the 'CounterLog' with a message");
  17. }
  18. }

实验性注解

见文档,这里不多说了: https://projectlombok.org/features/experimental/all

var

可修改的局部变量,其类型通过分配值来推断。

@Accessors

面向getter和setter的更流畅的API。

@ExtensionMethod

烦人的API?自己修复:将新方法添加到现有类型中!

@FieldDefaults

21世纪的新默认字段修饰符。

@Delegate

不要失去你的组成。

onMethod= / onConstructor= / onParam=

抱歉,我们听说您喜欢批注,因此我们将批注放入批注中,以便您可以在批注时进行批注。

@UtilityClass

效用,适度,潮湿!大众的实用程序类。

@Helper

在朋友的帮助下… Java的辅助方法。

@FieldNameConstants

名称…那个字段!字段名称的字符串常量。

@SuperBuilder

鲍勃现在也认识他的祖先:也拥有超类字段的建造者。

@Tolerate

跳过,跳跃和忘记!使lombok忽略现有的方法或构造函数。