- 什么是Lombok
- 安装
- 原理讲解
- 常用注解
- 示例
- @var
- @Nonall
- @ClearUp
- @Getter/@Setter
- @Getter(lazy = true)
- @ToString
- @EqualsAndHashCode
- @ NoArgsConstructor,@ RequiredArgsConstructor,@ AllArgsConstructor
- @Dataa
- @Value
- @Builder
- @SneakyThrows
- @With
- @Log
- 实验性注解
var
">var
@Accessors
">@Accessors
@ExtensionMethod
">@ExtensionMethod
@FieldDefaults
">@FieldDefaults
@Delegate
">@Delegate
onMethod= / onConstructor= / onParam=
">onMethod= / onConstructor= / onParam=
@UtilityClass
">@UtilityClass
@Helper
">@Helper
@FieldNameConstants
">@FieldNameConstants
@SuperBuilder
">@SuperBuilder
@Tolerate
">@Tolerate
什么是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 或等值方法,一个注释,您的类具有一个功能齐全的生成器,自动执行日志记录变量,等等。
- 官网:https://projectlombok.org/
- 源码:https://github.com/rzwitserloot/lombok
- 英文文档:https://objectcomputing.com/resources/publications/sett/january-2010-reducing-boilerplate-code-with-project-lombok
安装
1. 开发工具安装lombok插件
比如idea安装:file—settings—plugins—搜插件lombok —安装
2. 引入lombok 的jar,比如maven可以
最新版:https://mvnrepository.com/artifact/org.projectlombok/lombok
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
使用日志还需要引入日志依赖
最新版:https://mvnrepository.com/artifact/org.slf4j/slf4j-api
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.0-alpha1</version>
</dependency>
3.解决编译时出错问题
编译时出错,可能是没有enable注解处理器。Annotation Processors > Enable annotation processing
。设置完成之后程序正常运行。
原理讲解
准备一个User类
@Data
public class User {
private Long id;
private String msg;
private Date sendTime;
}
以@Data注解为例
常用注解
- @Getter和@Setter:生成getter和getter方法
- @ToString:生成toString方法
- @NonNull:setter和构造函数时,增加判空的代码
- @EqualsAndHashCode:生成equals和hasCode方法代码
- @AllArgsConstructor: 生成全部参数的构造函数方法
- @NoArgsConstructor:无参构造代码
- @RequiredArgsConstructor: 必须有参数的构造方法(即public User(@NonNull String name) )
- @Data:效果达到以上所有注解的效果(Getter/Setter/RequiredArgsConstructor/ToString/EqualsAndHashCode/__Value)
- @Synchronized:锁代码,用于线程安全的场景
- @Slf4j:生成日志代码,以Slf4j为日志实现的
- @XSlf4j:与@Slf4j同理
- @Log4j或者@Log4j2:生成日志代码,分别由log4j和logback实现日志
- @Log:jdk默认日志实现
- @JbossLog:jboss日志实现
- @CommonsLog: Apache commons 日志实现
- 等等其他注解
示例
``` package com.initit.lombok;
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) 构造参数需要参数
/**
* 因为以上注解常用,故多个注解合并为一个注解@Data
* 等同于以下几个注解的集合
* * @see Getter
* * @see Setter
* * @see RequiredArgsConstructor
* * @see ToString
* * @see EqualsAndHashCode
* * @see lombok.Value
*
* @see Data
*/
@Data
@AllArgsConstructor //全参构造方法
@NoArgsConstructor //无参构造方法
/**
* 日志支持
* 支持如下日志
* @see Slf4j
* * @see lombok.extern.apachecommons.CommonsLog CommonsLog
* * @see lombok.extern.java.Log Log
* * @see lombok.extern.log4j.Log4j Log4j
* * @see lombok.extern.log4j.Log4j2 Log4j2
* * @see lombok.extern.slf4j.XSlf4j XSlf4j
* * @see lombok.extern.jbosslog.JBossLog JBossLog
* * @see lombok.extern.flogger.Flogger Flogger
* * @see lombok.CustomLog CustomLog
*/
@Slf4j
static class User {
private static int i = 1;
private Integer id;
private String name;
private void log() {
log.info("打印日志");
}
@Synchronized //线程安全 等同意 synchronized关键字
@SneakyThrows //不抛出异常
private void synchronizedTest() {
Thread.sleep(100);
i++; //多线程共享数据i
log.info("线程安全,i={}", i);
}
private void threadTest() {
//启动两个线程 模拟多线程操作
new Thread(this::synchronizedTest).start();
new Thread(this::synchronizedTest).start();
}
}
@Test
void t1() {
User user = new User();
user.setName("编程指南");
System.out.println(user.getName());
System.out.println(user.toString());
User u1 = new User(1, "张三");
User u2 = new User(1, "张三");
System.out.println(u1.equals(u2));
}
}
<a name="5h0Zi"></a>
# 最新特性解读
官方文档:[https://projectlombok.org/features/all](https://projectlombok.org/features/all)
<a name="ir80J"></a>
## @val
自动推断类型,并声明为 final 。<br />`_val a ="str"_` 等同于 `final String a = "str"` <br />lombok代码:
```java
import java.util.ArrayList;
import java.util.HashMap;
import lombok.val;
public class ValExample {
public String example() {
val example = new ArrayList<String>();
example.add("Hello, World!");
val foo = example.get(0);
return foo.toLowerCase();
}
public void example2() {
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());
}
}
}
转为java代码:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class ValExample {
public String example() {
final ArrayList<String> example = new ArrayList<String>();
example.add("Hello, World!");
final String foo = example.get(0); //自动推断为 final String类型
return foo.toLowerCase();
}
public void example2() {
final HashMap<Integer, String> map = new HashMap<Integer, String>();
map.put(0, "zero");
map.put(5, "five");
for (final Map.Entry<Integer, String> entry : map.entrySet()) {
System.out.printf("%d: %s\n", entry.getKey(), entry.getValue());
}
}
}
@var
与val意义,自动推断类型,只不过不是final
如: var a =2
等同于 Integer a =2
@Nonall
生成空指针检查语句,如果为null则抛出异常
lombok代码:
import lombok.NonNull;
public class NonNullExample extends Something {
private String name;
public NonNullExample(@NonNull Person person) {
super("Hello");
this.name = person.getName();
}
}
转为java代码:
import lombok.NonNull;
public class NonNullExample extends Something {
private String name;
public NonNullExample(@NonNull Person person) {
super("Hello");
//生成空指针检查语句,如果为null则抛出异常
if (person == null) {
throw new NullPointerException("person is marked @NonNull but is null");
}
this.name = person.getName();
}
}
@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代码:
import lombok.Cleanup;
import java.io.*;
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);
}
}
}
转为java代码:
import java.io.*;
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();
}
}
}
}
@Getter/@Setter
生成getter和setter代码
lombok代码
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
public class GetterSetterExample {
/**
* Age of the person. Water is wet.
*
* @param age New value for this person's age. Sky is blue.
* @return The current value of this person's age. Circles are round.
*/
@Getter @Setter private int age = 10;
/**
* Name of the person.
* -- SETTER --
* Changes the name of this person.
*
* @param name The new value.
*/
@Setter(AccessLevel.PROTECTED) private String name;
@Override public String toString() {
return String.format("%s (age: %d)", name, age);
}
}
转为java代码:
public class GetterSetterExample {
/**
* Age of the person. Water is wet.
*/
private int age = 10;
/**
* Name of the person.
*/
private String name;
@Override public String toString() {
return String.format("%s (age: %d)", name, age);
}
/**
* Age of the person. Water is wet.
*
* @return The current value of this person's age. Circles are round.
*/
public int getAge() {
return age;
}
/**
* Age of the person. Water is wet.
*
* @param age New value for this person's age. Sky is blue.
*/
public void setAge(int age) {
this.age = age;
}
/**
* Changes the name of this person.
*
* @param name The new value.
*/
protected void setName(String name) {
this.name = name;
}
}
@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代码:
import lombok.Getter;
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;
}
}
转换为java代码:
public class GetterLazyExample {
private final java.util.concurrent.AtomicReference<java.lang.Object> cached = new java.util.concurrent.AtomicReference<java.lang.Object>();
public double[] getCached() {
java.lang.Object value = this.cached.get();
if (value == null) {
synchronized(this.cached) {
value = this.cached.get();
if (value == null) {
final double[] actualValue = expensive();
value = actualValue == null ? this.cached : actualValue;
this.cached.set(value);
}
}
}
return (double[])(value == this.cached ? null : value);
}
private double[] expensive() {
double[] result = new double[1000000];
for (int i = 0; i < result.length; i++) {
result[i] = Math.asin(i);
}
return result;
}
}
@ToString
生成toString()方法
lombok代码:
import lombok.ToString;
@ToString
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;
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;
}
}
}
转为java代码:
import java.util.Arrays;
public class ToStringExample {
private static final int STATIC_VAR = 10;
private String name;
private Shape shape = new Square(5, 10);
private String[] tags;
private int id;
public String getName() {
return this.name;
}
public static class Square extends Shape {
private final int width, height;
public Square(int width, int height) {
this.width = width;
this.height = height;
}
@Override public String toString() {
return "Square(super=" + super.toString() + ", width=" + this.width + ", height=" + this.height + ")";
}
}
@Override public String toString() {
return "ToStringExample(" + this.getName() + ", " + this.shape + ", " + Arrays.deepToString(this.tags) + ")";
}
}
@EqualsAndHashCode
生成equals(Object other)
和hashCode()
方法的实现。默认情况下,它将使用所有非静态,非瞬态字段,但是您可以通过使用@EqualsAndHashCode.Include
或标记类型成员来修改要使用的字段(甚至指定要使用各种方法的输出)@EqualsAndHashCode.Exclude
。另外,您可以通过标记@EqualsAndHashCode.Include
并使用来确切指定要使用的字段或方法@EqualsAndHashCode(onlyExplicitlyIncluded = true)
。
如果将其应用于@EqualsAndHashCode
扩展另一个类的类,则此功能会有些棘手。通常,为此类自动生成equals
and hashCode
方法是一个坏主意,因为超类还定义了字段,该字段也需要equals / hashCode代码,但不会生成此代码。通过设置callSuper
到真正的,可以包括equals
和hashCode
你的超类中生成的方法的方法。对于hashCode
,结果super.hashCode()
包含在哈希算法中,对于equals
,如果超级实现认为它与传入的对象不相等,则生成的方法将返回false。请注意,并非所有的equals
实现都能正确处理这种情况。但是,龙目岛生成的equals
实现可以正确处理这种情况,因此,如果它也具有lombok生成的equals
方法,则可以安全地调用您的超类equals 。如果您有显式的超类,则必须提供一些价值callSuper
以确认您已经考虑了它。否则将导致警告。
当您不扩展任何内容时, 将其设置callSuper
为truejava.lang.Object
是一个编译时错误,因为它将使生成的equals()
和hashCode()
实现具有与仅继承这些方法的行为相同的行为java.lang.Object
:只有相同的对象才等于每个对象其他,并将具有相同的hashCode。扩展另一个类时未将其设置callSuper
为true会生成警告,因为除非超类没有(相等的重要)字段,否则lombok无法为您生成一个实现,该实现将您的超类声明的字段考虑在内。您需要编写自己的实现,或依靠callSuper
链接工具。您也可以使用lombok.equalsAndHashCode.callSuper
config键。
Lombok 0.10中的新增内容:除非您的类是final
并且java.lang.Object
extended,否则lombok会生成一个canEqual
方法,这意味着JPA代理仍然可以与其基类相等,但是添加新状态的子类不会破坏等号契约。本文解释了为什么需要这种方法的复杂原因:如何用Java编写平等方法。如果层次结构中的所有类都是Scala案例类和具有lombok生成的equals方法的类的混合,则所有等式都将“正常工作”。如果您需要编写自己的equals方法,canEqual
则更改equals
和时应始终覆盖hashCode
。
Lombok 1.14.0中的新增功能:要将注释放在(和相关的)方法的other
参数上,可以使用。不过要小心!这是一项实验功能。有关更多详细信息,请参阅onX功能的文档。 equals``canEqual``onParam=@__({@AnnotationsHere})
lombok代码:
import lombok.EqualsAndHashCode;
@EqualsAndHashCode
public class EqualsAndHashCodeExample {
private transient int transientVar = 10;
private String name;
private double score;
@EqualsAndHashCode.Exclude private Shape shape = new Square(5, 10);
private String[] tags;
@EqualsAndHashCode.Exclude 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;
}
}
}
转为java代码:
import java.util.Arrays;
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;
}
@Override public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof EqualsAndHashCodeExample)) return false;
EqualsAndHashCodeExample other = (EqualsAndHashCodeExample) o;
if (!other.canEqual((Object)this)) return false;
if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
if (Double.compare(this.score, other.score) != 0) return false;
if (!Arrays.deepEquals(this.tags, other.tags)) return false;
return true;
}
@Override public int hashCode() {
final int PRIME = 59;
int result = 1;
final long temp1 = Double.doubleToLongBits(this.score);
result = (result*PRIME) + (this.name == null ? 43 : this.name.hashCode());
result = (result*PRIME) + (int)(temp1 ^ (temp1 >>> 32));
result = (result*PRIME) + Arrays.deepHashCode(this.tags);
return result;
}
protected boolean canEqual(Object other) {
return other instanceof EqualsAndHashCodeExample;
}
public static class Square extends Shape {
private final int width, height;
public Square(int width, int height) {
this.width = width;
this.height = height;
}
@Override public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Square)) return false;
Square other = (Square) o;
if (!other.canEqual((Object)this)) return false;
if (!super.equals(o)) return false;
if (this.width != other.width) return false;
if (this.height != other.height) return false;
return true;
}
@Override public int hashCode() {
final int PRIME = 59;
int result = 1;
result = (result*PRIME) + super.hashCode();
result = (result*PRIME) + this.width;
result = (result*PRIME) + this.height;
return result;
}
protected boolean canEqual(Object other) {
return other instanceof Square;
}
}
}
@ 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代码:
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.NonNull;
@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;
}
}
转为java代码:
public class ConstructorExample<T> {
private int x, y;
@NonNull private T description;
private ConstructorExample(T description) {
if (description == null) throw new NullPointerException("description");
this.description = description;
}
public static <T> ConstructorExample<T> of(T description) {
return new ConstructorExample<T>(description);
}
@java.beans.ConstructorProperties({"x", "y", "description"})
protected ConstructorExample(int x, int y, T description) {
if (description == null) throw new NullPointerException("description");
this.x = x;
this.y = y;
this.description = description;
}
public static class NoArgsExample {
@NonNull private String field;
public NoArgsExample() {
}
}
}
@Dataa
@Data
是捆绑的特征的方便快捷方式注释@ToString
,@EqualsAndHashCode
,@Getter
/@Setter
和@RequiredArgsConstructor
在一起:即,@Data
产生所有其通常与简单的POJO(普通旧式Java对象)和豆类相关的样板:吸气剂对于所有字段,设置器为所有非final字段和适当的toString
,equals
以及hashCode
涉及该类字段的实现,以及一个构造函数,该构造函数初始化所有final字段以及所有未标记有初始化程序的非final字段,@NonNull
以确保该字段永远不会空值。@Data
就像具有隐式@Getter
,@Setter
,@ToString
,@EqualsAndHashCode
和@RequiredArgsConstructor
在类注解(不同之处在于没有构造将生成如果已经存在任何明确写入构造函数)。但是,这些注释的参数(如callSuper
,includeFieldNames
和exclude
)不能与设置@Data
。如果您需要为这些参数中的任何一个设置非默认值,只需显式添加这些注释即可;@Data
非常聪明,可以遵循这些注释。
所有生成的getter和setter将为public
。要覆盖访问级别,请使用显式@Setter
和/或@Getter
注释对字段或类进行注释。您也可以使用此批注(通过将其与结合使用AccessLevel.NONE
)来完全禁止生成getter和/或setter。
标记为的所有字段transient
都不会考虑hashCode
和equals
。所有静态字段都将被完全跳过(任何生成的方法都不会考虑,并且不会为它们创建setter / getter)。
如果该类已经包含与通常会生成的任何方法具有相同名称和参数计数的方法,则不会生成该方法,并且不会发出警告或错误。例如,如果您已经有一个带有签名的方法,则不会生成equals(AnyType param)
任何equals
方法,即使从技术上讲,由于具有不同的参数类型,它可能是完全不同的方法。该规则同样适用于构造函数(任何显式构造将防止@Data
从生成一个),以及toString
,equals
和所有的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代码:
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;
}
}
转为java代码:
import java.util.Arrays;
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;
}
}
}
@Value
@Value
是的不变变体@Data
; 所有字段由private
和final
默认情况下,也不会产生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代码:
import lombok.AccessLevel;
import lombok.experimental.NonFinal;
import lombok.experimental.Value;
import lombok.experimental.Wither;
import lombok.ToString;
@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;
}
}
转为java代码:
import java.util.Arrays;
public final class ValueExample {
private final String name;
private int age;
private final double score;
protected final String[] tags;
@java.beans.ConstructorProperties({"name", "age", "score", "tags"})
public ValueExample(String name, int age, double score, String[] tags) {
this.name = name;
this.age = age;
this.score = score;
this.tags = tags;
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
public double getScore() {
return this.score;
}
public String[] getTags() {
return this.tags;
}
@java.lang.Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof ValueExample)) return false;
final ValueExample other = (ValueExample)o;
final Object this$name = this.getName();
final Object other$name = other.getName();
if (this$name == null ? other$name != null : !this$name.equals(other$name)) 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;
}
@java.lang.Override
public int hashCode() {
final int PRIME = 59;
int result = 1;
final Object $name = this.getName();
result = result * PRIME + ($name == null ? 43 : $name.hashCode());
result = result * PRIME + this.getAge();
final long $score = Double.doubleToLongBits(this.getScore());
result = result * PRIME + (int)($score >>> 32 ^ $score);
result = result * PRIME + Arrays.deepHashCode(this.getTags());
return result;
}
@java.lang.Override
public String toString() {
return "ValueExample(name=" + getName() + ", age=" + getAge() + ", score=" + getScore() + ", tags=" + Arrays.deepToString(getTags()) + ")";
}
ValueExample withAge(int age) {
return this.age == age ? this : new ValueExample(name, age, score, tags);
}
public static final 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;
}
@java.lang.Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof ValueExample.Exercise)) return false;
final Exercise<?> other = (Exercise<?>)o;
final Object this$name = this.getName();
final Object other$name = other.getName();
if (this$name == null ? other$name != null : !this$name.equals(other$name)) return false;
final Object this$value = this.getValue();
final Object other$value = other.getValue();
if (this$value == null ? other$value != null : !this$value.equals(other$value)) return false;
return true;
}
@java.lang.Override
public int hashCode() {
final int PRIME = 59;
int result = 1;
final Object $name = this.getName();
result = result * PRIME + ($name == null ? 43 : $name.hashCode());
final Object $value = this.getValue();
result = result * PRIME + ($value == null ? 43 : $value.hashCode());
return result;
}
@java.lang.Override
public String toString() {
return "ValueExample.Exercise(name=" + getName() + ", value=" + getValue() + ")";
}
}
}
@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批注的方法(或构造函数);例如,您不能放入@EqualsAndHashCode
builder类。@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是目标的返回类型的简化的,以标题区分大小写的形式-即,@Builder
on构造函数和类型的类型名称,以及@Builder
on方法的返回类型的名称。。例如,如果@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
:Iterable
,Collection
和List
(ArrayList
通常情况下以不可修改的压缩为后盾)。Set
,SortedSet
和NavigableSet
(由大小适中的不可修改文件HashSet
或TreeSet
一般情况下的文件支持)。Map
,SortedMap
和NavigableMap
(由大小适中的不可修改文件HashMap
或TreeMap
一般情况下的文件支持)。
- Guava的
com.google.common.collect
:ImmutableCollection
和ImmutableList
(由的构建器功能支持ImmutableList
)。ImmutableSet
和ImmutableSortedSet
(由这些类型的构建器功能支持)。ImmutableMap
,ImmutableBiMap
和ImmutableSortedMap
(由这些类型的构建器功能支持)。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将生成您不手动添加的所有内容,并将其放入此构建器类。例如,如果您尝试将杰克逊配置为对集合使用特定的子类型,则可以编写如下内容:
@Value @Builder
@JsonDeserialize(builder = JacksonExample.JacksonExampleBuilder.class)
public class JacksonExample {
@Singular(nullBehavior = NullCollectionBehavior.IGNORE) private List<Foo> foos;
@JsonPOJOBuilder(withPrefix = "")
public static class JacksonExampleBuilder implements JacksonExampleBuilderMeta {
}
private interface JacksonExampleBuilderMeta {
@JsonDeserialize(contentAs = FooImpl.class) JacksonExampleBuilder foos(List<? extends Foo> foos)
}
}
lombok代码:
import lombok.Builder;
import lombok.Singular;
import java.util.Set;
@Builder
public class BuilderExample {
@Builder.Default private long created = System.currentTimeMillis();
private String name;
private int age;
@Singular private Set<String> occupations;
}
转换为java代码:
import java.util.Set;
public class BuilderExample {
private long created;
private String name;
private int age;
private Set<String> occupations;
BuilderExample(String name, int age, Set<String> occupations) {
this.name = name;
this.age = age;
this.occupations = occupations;
}
private static long $default$created() {
return System.currentTimeMillis();
}
public static BuilderExampleBuilder builder() {
return new BuilderExampleBuilder();
}
public static class BuilderExampleBuilder {
private long created;
private boolean created$set;
private String name;
private int age;
private java.util.ArrayList<String> occupations;
BuilderExampleBuilder() {
}
public BuilderExampleBuilder created(long created) {
this.created = created;
this.created$set = true;
return this;
}
public BuilderExampleBuilder name(String name) {
this.name = name;
return this;
}
public BuilderExampleBuilder age(int age) {
this.age = age;
return this;
}
public BuilderExampleBuilder occupation(String occupation) {
if (this.occupations == null) {
this.occupations = new java.util.ArrayList<String>();
}
this.occupations.add(occupation);
return this;
}
public BuilderExampleBuilder occupations(Collection<? extends String> occupations) {
if (this.occupations == null) {
this.occupations = new java.util.ArrayList<String>();
}
this.occupations.addAll(occupations);
return this;
}
public BuilderExampleBuilder clearOccupations() {
if (this.occupations != null) {
this.occupations.clear();
}
return this;
}
public BuilderExample build() {
// complicated switch statement to produce a compact properly sized immutable set omitted.
Set<String> occupations = ...;
return new BuilderExample(created$set ? created : BuilderExample.$default$created(), name, age, occupations);
}
@java.lang.Override
public String toString() {
return "BuilderExample.BuilderExampleBuilder(created = " + this.created + ", name = " + this.name + ", age = " + this.age + ", occupations = " + this.occupations + ")";
}
}
}
@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代码:
import lombok.SneakyThrows;
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();
}
}
转换为java代码:
import lombok.Lombok;
public class SneakyThrowsExample implements Runnable {
public String utf8ToString(byte[] bytes) {
try {
return new String(bytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw Lombok.sneakyThrow(e);
}
}
public void run() {
try {
throw new Throwable();
} catch (Throwable t) {
throw Lombok.sneakyThrow(t);
}
}
}
@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代码:
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.With;
public class WithExample {
@With(AccessLevel.PROTECTED) @NonNull private final String name;
@With private final int age;
public WithExample(String name, int age) {
if (name == null) throw new NullPointerException();
this.name = name;
this.age = age;
}
}
转换为java代码:
import lombok.NonNull;
public class WithExample {
private @NonNull final String name;
private final int age;
public WithExample(String name, int age) {
if (name == null) throw new NullPointerException();
this.name = name;
this.age = age;
}
protected WithExample withName(@NonNull String name) {
if (name == null) throw new java.lang.NullPointerException("name");
return this.name == name ? this : new WithExample(name, age);
}
public WithExample withAge(int age) {
return this.age == age ? this : new WithExample(name, age);
}
}
@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代码:
import lombok.extern.java.Log;
import lombok.extern.slf4j.Slf4j;
@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");
}
}
转换为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");
}
}
实验性注解
见文档,这里不多说了: https://projectlombok.org/features/experimental/all
var
@Accessors
@ExtensionMethod
@FieldDefaults
@Delegate
onMethod= / onConstructor= / onParam=
抱歉,我们听说您喜欢批注,因此我们将批注放入批注中,以便您可以在批注时进行批注。
@UtilityClass
@Helper
@FieldNameConstants
@SuperBuilder
@Tolerate
跳过,跳跃和忘记!使lombok忽略现有的方法或构造函数。