草庐IT

Lombok工具 : 常用注解介绍 (全)

white camel 2023-06-01 原文

文章目录


介绍

官方网址 : https://projectlombok.org/

Lombok是一款Java开发插件,使得Java开发者可以通过其定义的一些注解来消除业务工程中冗长和繁琐的代码,尤其对于简单的Java模型对象(POJO)。

在开发环境中使用Lombok插件后,Java开发人员可以节省出重复构建,诸如hashCode和equals这样的方法以及各种业务对象模型的accessor和ToString等方法的大量时间。对于这些方法,它能够在编译源代码期间自动帮我们生成这些方法,并没有如反射那样降低程序的性能。

引入Maven依赖

采用最新版本 1.18.24

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <scope>provided</scope>
    <version>1.18.24</version>
</dependency>

常用的注解

@NoArgsConstructor/@AllArgsConstructor

  • @NoArgsConstructor/@AllArgsConstructor就是为该类产生无参的构造方法和包含所有参数的构造方法

测试案例1:

@AllArgsConstructor
public class Demo extends Parent{
    private String name;
    private int age;
}

@AllArgsConstructor
@NoArgsConstructor
class Parent {
    private Integer id;
}

编译后的两个class文件如下:

此注解并不会把父类的属性id拿到Demo的构造器里面去,这是需要注意的地方。

public class Demo {
    private String name;
    private int age;

    public Demo() {
    }

    public Demo(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

class Parent {
    private Integer id;

    public Parent(Integer id) {
        this.id = id;
    }

    public Parent() {
    }
}

通过IDEA的类结构, 也可以看出来生成了哪些东西


测试案例2:

通过 @AllArgsConstructor(access = AccessLevel.PROTECTED) , 可以指定构造器的访问权限

@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Demo{
    private String name;
    private int age;
}

编译后生成:

public class Demo {
    private String name;
    private int age;

    protected Demo(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

测试案例3:

@AllArgsConstructor(access = AccessLevel.PROTECTED, staticName = “test”)

@AllArgsConstructor(access = AccessLevel.PROTECTED, staticName = "test")
public class Demo{
    private final int finalVal = 10;
    private String name;
    private int age;
}

编译后 :

public class Demo {
    private String name;
    private int age;

    private Demo(String name, int age) {
        this.name = name;
        this.age = age;
    }

    protected static Demo test(String name, int age) {
        return new Demo(name, age);
    }
}

上面效果为:可以指定生成的构造器的访问权限。但是如果指定了一个静态方法,那么构造器会自动会被private,只通过静态方法对外提供访问,并且我们发现final的属性值,是不会放进构造函数里面的。

@NoArgsConstructor的使用方式同@AllArgsConstructor

@RequiredArgsConstructor

@RequiredArgsConstructor注解则会将类中所有带有@NonNull注解 / org.jetbrains.annotations.NotNull注解的或者带有final修饰的成员变量生成对应的构造方法

测试案例:

@RequiredArgsConstructor
public class Demo {

    @NonNull
    private final int finalVal;
    @NonNull
    private String name;
    @NonNull
    private int age;
}

编译后生成:

import lombok.NonNull;

public class Demo {
    @NonNull
    private final int finalVal;
    @NonNull
    private String name;
    @NonNull
    private int age;

    public Demo(@NonNull int finalVal, @NonNull String name, @NonNull int age) {
        if (name == null) {
            throw new NullPointerException("name is marked non-null but is null");
        } else {
            this.finalVal = finalVal;
            this.name = name;
            this.age = age;
        }
    }
}


解释:该注解会识别@NonNull字段和final修饰得字段,然后以该字段为元素生成一个构造函数。

如果所有字段都没有@NonNull注解,那效果同@NoArgsConstructor ;
@RequiredArgsConstructor 注解的参数, 和其他两个注解一样

注意: 当然这三个生成构造器的注解,要求成员变量都是非静态的, 否则静态变量不会在构造器中被赋值

@Getter/@Setter

这一对注解从名字上就很好理解,用在成员变量上面或者类上面,相当于为成员变量生成对应的get和set方法,同时还可以为生成的方法指定访问修饰符,当然,默认为public

// 如果指定在类上,所有字段都会生成get/set方法
// 指定在字段上, 只有标注的字段才会生成get/set方法
@Getter
@Setter
public class Demo {

    private String name;
    private int age;
}

这两个注解直接用在类上,可以为此类里的所有非静态成员变量生成对应的get和set方法。如果是final变量,那就只会有get方法

@ToString/@EqualsAndHashCode

这两个注解也比较好理解,就是生成toString,equals和hashcode方法,同时后者还会生成一个canEqual方法,用于判断某个对象是否是当前类的实例。生成方法时只会使用类中的非静态成员变量。

@ToString
@EqualsAndHashCode
public class Demo {

    private String name;
    private int age;
}

有些关键的属性,可以控制toString的输出,我们可以了解一下:

//@EqualsAndHashCode也有类似的下面的属性,
@ToString(
        includeFieldNames = true, //是否使用字段名
        exclude = {"name"}, //排除某些字段
        of = {"age"}, //只使用某些字段
        callSuper = true //是否让父类字段也参与 默认false
)

@Data

相当于注解集合。效果等同于 @Getter + @Setter + @ToString + @EqualsAndHashCode + @RequiredArgsConstructor 效果同和这5个注解的效果

需要注意的是,这里不包括@NoArgsConstructor和@AllArgsConstructor

  • 所以, 一般使用@Data时,要配合这两个注解一起使用
@Data
public class Demo {
    private String name;
    private int age;
}

@Builder

@Builder提供了一种比较推崇的构建值对象的方式; 非常推荐的一种构建值对象的方式。

缺点就是父类的属性不能产于builder

标注@Builder的类, 会在类内部生成一个内部类,用于生成值对象

@Builder
public class Demo {
    private final int finalVal = 10;

    private String name;
    private int age;
}

编译后生成:

public class Demo {
    private final int finalVal = 10;
    private String name;
    private int age;

    Demo(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public static Demo.DemoBuilder builder() {
        return new Demo.DemoBuilder();
    }

    public static class DemoBuilder {
        private String name;
        private int age;

        DemoBuilder() {
        }

        public Demo.DemoBuilder name(String name) {
            this.name = name;
            return this;
        }

        public Demo.DemoBuilder age(int age) {
            this.age = age;
            return this;
        }

        public Demo build() {
            return new Demo(this.name, this.age);
        }

        public String toString() {
            return "Demo.DemoBuilder(name=" + this.name + ", age=" + this.age + ")";
        }
    }
}


使用方式:

public class Main {

    public static void main(String[] args) {
        Demo demo = Demo.builder().name("zss").age(20).build();
        System.out.println(demo);
    }
}

一般我们给POJO类, 标注的Lombok注解, 百分之90就是这4个 : @Data, @NoArgsConstructor, @AllArgsConstructor, @Builder

@Accessors

@Accessors 一个为getter和setter方法设计的更流畅的注解
这个注解要搭配@Getter与@Setter使用,用来修改默认的setter与getter方法的形式。

@Accessors属性详解

  • fluent 属性 : 链式的形式 这个特别好用,方法连缀越来越方便了
  • chain 属性 : 流式的形式(若无显示指定chain的值,也会把chain设置为true)
  • prefix 属性 : 生成指定前缀的属性的getter与setter方法,并且生成的getter与setter方法时会去除前缀

测试不使用@Accessors时

fluent属性

默认为false,当该值为 true 时,对应字段的 getter 方法前面就没有 get,setter 方法就不会有 set。

编译后生成:

public class Demo {
    private final int finalVal = 10;
    private String xxName;
    private int yyAge;

    public Demo() {
    }

    public int finalVal() {
        Objects.requireNonNull(this);
        return 10;
    }

    public String xxName() {
        return this.xxName;
    }

    public int yyAge() {
        return this.yyAge;
    }

    public Demo xxName(String xxName) {
        this.xxName = xxName;
        return this;
    }

    public Demo yyAge(int yyAge) {
        this.yyAge = yyAge;
        return this;
    }

    public String toString() {
        int var10000 = this.finalVal();
        return "Demo(finalVal=" + var10000 + ", xxName=" + this.xxName() + ", yyAge=" + this.yyAge() + ")";
    }
}

使用:

public class Main {

    public static void main(String[] args) {
        Demo demo = new Demo();
        // setter方法; 这里包含了chain=true的功能,可以链式设置值
        demo.xxName("lucky").yyAge(20);
        // getter方法
        System.out.println(demo.xxName() + "," + demo.yyAge());
        System.out.println("demo = " + demo);
    }
}

chain属性

不写默认为false,当该值为 true 时,对应字段的 setter 方法调用后,会返回当前对象, 进行链式设置值


编译后生成:

public class Demo {
    private final int finalVal = 10;
    private String xxName;
    private int yyAge;

    public Demo() {
    }

    public int getFinalVal() {
        Objects.requireNonNull(this);
        return 10;
    }

    public String getXxName() {
        return this.xxName;
    }

    public int getYyAge() {
        return this.yyAge;
    }

    public Demo setXxName(String xxName) {
        this.xxName = xxName;
        return this;
    }

    public Demo setYyAge(int yyAge) {
        this.yyAge = yyAge;
        return this;
    }

    public String toString() {
        int var10000 = this.getFinalVal();
        return "Demo(finalVal=" + var10000 + ", xxName=" + this.getXxName() + ", yyAge=" + this.getYyAge() + ")";
    }
}

使用

public class Main {

    public static void main(String[] args) {
        Demo demo = new Demo();
        // setter方法
        demo.setXxName("lucky").setYyAge(20);
        System.out.println("demo = " + demo);
    }
}

prefix属性

该属性是一个字符串数组,当该数组有值时,表示忽略字段中对应的前缀,生成对应的 getter 和 setter 方法。

如果,我们把它的前缀加到 @Accessors 的属性值中,则可以像没有前缀那样,去调用字段的 getter和 setter 方法。

这里只是把原有属性的前缀给去掉

其他注解

@SneakyThrows

这个注解用在方法上,可以将方法中的代码用try-catch语句包裹起来,捕获异常并在catch中用Lombok.sneakyThrow(e)把异常抛出,可以使用@SneakyThrows(Exception.class)的形式指定抛出哪种异常

public class Demo {
    @SneakyThrows(UnsupportedEncodingException.class)
    public String utf8ToString(byte[] bytes) {
        return new String(bytes, "UTF8");
    }
}

编译生成后:

public class Demo {
    public String utf8ToString(byte[] bytes) {
        try {
            return new String(bytes, "UTF8");
        } catch (UnsupportedEncodingException var3) {
            throw var3;
        }
    }
}

@Value

@Value注解和@Data类似,区别在于它会把所有成员变量默认定义为private final修饰,并且不会生成set方法。

所以@Value更适合只读性更强的类,所以特殊情况下,还是可以使用的。

@Cleanup

@Cleanup能够自动释放资源; 和 try-with-resources的区别: try-with-resources和lombok的@Cleanup

这个注解用在局部变量上,可以保证此变量代表的资源会被自动关闭,默认是调用资源的close()方法。

如果该资源有其它关闭方法,可使用@Cleanup(“methodName”)来指定要调用的方法,就用输入输出流来举个例子吧:

@SneakyThrows(Exception.class)
public static void main(String[] args) {
    @Cleanup InputStream in = new FileInputStream(args[0]);
    @Cleanup OutputStream out = new FileOutputStream(args[1]);
    byte[] b = new byte[1024];
    while (true) {
        int r = in.read(b);
        if (r == -1) break;
        out.write(b, 0, r);
    }
}

编译后生成:

public static void main(String[] args) {
    try {
        FileInputStream in = new FileInputStream(args[0]);

        try {
            FileOutputStream out = new FileOutputStream(args[1]);

            try {
                byte[] b = new byte[1024];

                while(true) {
                    int r = in.read(b);
                    if (r == -1) {
                        return;
                    }

                    out.write(b, 0, r);
                }
            } finally {
                if (Collections.singletonList(out).get(0) != null) {
                    out.close();
                }

            }
        } finally {
            if (Collections.singletonList(in).get(0) != null) {
                in.close();
            }

        }
    } catch (Exception var15) {
        throw var15;
    }
}

@NotNull

这个注解可以用在成员方法或者构造方法的参数上,会自动产生一个关于此参数的非空检查,如果参数为空,则抛出一个空指针异常。

//成员方法参数加上@NonNull注解
public String getName(@NonNull Person p){
    return p.getName();
}

编译生成后:

public String getName(@NonNull Person p){
    if(p == null){
        throw new NullPointerException("person");
    }
    return p.getName();
}

@Synchronized

作用于方法,可以替换 synchronized 关键字或 lock 锁

这个注解用在类方法或者实例方法上,效果和synchronized关键字相同,区别在于锁对象不同,对于类方法和实例方法,synchronized关键字的锁对象分别是类的class对象和this对象

而@Synchronized得锁对象分别是私有静态final对象lock和私有final对象lock,当然,也可以自己指定锁对象

注意: 属性value指定锁对象,当锁对象不存在时,则编译不通过,默认为 “”

public class Demo {

    private Object obj;

    @Synchronized
    public static void hello() {
        System.out.println("world");
    }

    @Synchronized
    public int answerToLife() {
        return 42;
    }

    @Synchronized("obj")
    public void foo() {
        System.out.println("bar");
    }
}

编译后生成:

public class Demo {
    private static final Object $LOCK = new Object[0];
    private final Object $lock = new Object[0];
    private Object obj;

    public Demo() {
    }

    public static void hello() {
        synchronized($LOCK) {
            System.out.println("world");
        }
    }

    public int answerToLife() {
        synchronized(this.$lock) {
            return 42;
        }
    }

    public void foo() {
        synchronized(this.obj) {
            System.out.println("bar");
        }
    }
}

@Log、@Log4j、@Slf4j、@Log4j2、@CommonsLog、@XSlf4j等日志注解

相对日志进行了解的, 请参考我的博客 : 日志系列

这些注解都有topic属性:设置 getLogger(String name) 方法的参数

这些注解用在类上,可以省去从日志工厂生成日志对象这一步,直接进行日志记录。

具体注解根据日志工具的不同而不同,同时,可以在注解中使用topic来指定生成log对象时的类名。不同的日志注解总结如下(上面是注解,下面是实际作用):

@Log注解

默认是会生成java.util.logging.Logger对象, JUL

private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());

使用JUL日志框架

@Log
public class LombokJULTest {
    /*
        日志入口程序
        java.util.logging.Logger
     */
     
    // @Log标注在JULTest类上,就表示自动生成了下面这行代码
    // Logger log = Logger.getLogger("com.lucky.jul.JULTest");
    @Test
    public void test01() {
        // 方式1
        log.info("输出info信息1");
        // 方式2
        log.log(Level.INFO, "输出info信息2");
    }
}

控制台输出:
八月 14, 2022 9:17:13 上午 com.lucky.jul.JULTest test01
信息: 输出info信息1
八月 14, 2022 9:17:13 上午 com.lucky.jul.JULTest test01
信息: 输出info信息2

编译后生成:

public class LombokJULTest {
    private static final Logger log = Logger.getLogger(LombokJULTest.class.getName());

    public LombokJULTest() {
    }

    @Test
    public void testLombokLog() {
        log.info("输出info信息1");
        log.log(Level.INFO, "输出info信息2");
    }
}

@Log4j注解

默认是会生成 org.apache.log4j.Logger 对象

private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class);

使用Log4j框架案例:

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

log4j配置文件 (resources下, log4j.properties文件)

log4j.rootLogger=trace,console,
#配置appender输出方式
log4j.appender.console=org.apache.log4j.ConsoleAppender
#配置输出的格式
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.conversionPattern=[%-5p]%r %c%t%d{yyyy-MM-dd HH:mm:ss:SSS} %m%n
@Log4j
public class LombokLog4jTest {

    // 使用Lombok的@Log4j注解, 相当于会自动生成下面这个logger对象
    // Logger log = Logger.getLogger(LombokLog4jTest.class);

    @Test
    public void testLombokLog4j() {
        // BasicConfigurator.configure(); // 默认指定了log4j的logger,输出位置,输出格式;不需要配置文件也可以打印日志

        // debug是默认的级别, 所以不会输出trace信息
        log.fatal("fatal信息");
        log.error("error信息");
        log.warn("warn信息");
        log.info("info信息");
        log.debug("debug信息");
        log.trace("trace信息");
    }
}

控制台输出:
[FATAL]0 com.lucky.log4j.LombokLog4jTestmain2022-08-14 11:02:45:720 fatal信息
[ERROR]2 com.lucky.log4j.LombokLog4jTestmain2022-08-14 11:02:45:722 error信息
[WARN ]2 com.lucky.log4j.LombokLog4jTestmain2022-08-14 11:02:45:722 warn信息
[INFO ]3 com.lucky.log4j.LombokLog4jTestmain2022-08-14 11:02:45:723 info信息

编译后生成代码:

public class LombokLog4jTest {
    private static final Logger log = Logger.getLogger(LombokLog4jTest.class);

    public LombokLog4jTest() {
    }

    @Test
    public void testLombokLog4j() {
        log.fatal("fatal信息");
        log.error("error信息");
        log.warn("warn信息");
        log.info("info信息");
        log.debug("debug信息");
        log.trace("trace信息");
    }
}

@Log4j2注解

前最优秀的Java日志框架, Log4j2也可以充当简单的日志门面来使用

private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);

使用案例: (本次使用Log4j2充当日志门面,实现简单的日志打印)

<dependencies>
     <!-- log4j2日志门面 -->
      <dependency>
          <groupId>org.apache.logging.log4j</groupId>
          <artifactId>log4j-api</artifactId>
          <version>2.12.1</version>
      </dependency>

      <!-- log4j2日志实现 -->
      <dependency>
          <groupId>org.apache.logging.log4j</groupId>
          <artifactId>log4j-core</artifactId>
          <version>2.12.1</version>
      </dependency>

      <!-- 异步日志依赖 -->
      <dependency>
          <groupId>com.lmax</groupId>
          <artifactId>disruptor</artifactId>
          <version>3.3.7</version>
      </dependency>
</dependencies>

resources目录下, log4j2.xml

<?xml version="1.0" encoding="utf-8" ?>
<Configuration status="debug">
    <!-- 配置appender -->
    <Appenders>
        <Console name="consoleAppender" target="SYSTEM_ERR">
            <PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L %thread %m%n"/>
        </Console>
    </Appenders>
    <!-- 配置logger -->
    <Loggers>
        <!-- 配置root logger -->
        <Root level="trace">
            <!-- 引用appender -->
            <AppenderRef ref="consoleAppender"/>
        </Root>
    </Loggers>
</Configuration>
import lombok.extern.log4j.Log4j2;
import org.junit.Test;

@Log4j2
public class LombokLog4j2Test {

    // @Log4j2会自动生成下面的Logger对象
    // Logger log = LogManager.getLogger(Log4j2Test.class);

    @Test
    public void lombokLog4jTest() {
        log.fatal("fatal信息");
        log.error("error信息");
        log.warn("warn信息");
        log.info("info信息");
        log.debug("debug信息");
        log.trace("trace信息");
    }
}

控制台输出:
[FATAL] 2022-08-14 11:43:31.845 com.lucky.Log4j2Test lombokLog4jTest 14 main fatal信息
[ERROR] 2022-08-14 11:43:31.849 com.lucky.Log4j2Test lombokLog4jTest 15 main error信息
[WARN ] 2022-08-14 11:43:31.849 com.lucky.Log4j2Test lombokLog4jTest 16 main warn信息
[INFO ] 2022-08-14 11:43:31.849 com.lucky.Log4j2Test lombokLog4jTest 17 main info信息
[DEBUG] 2022-08-14 11:43:31.850 com.lucky.Log4j2Test lombokLog4jTest 18 main debug信息
[TRACE] 2022-08-14 11:43:31.850 com.lucky.Log4j2Test lombokLog4jTest 19 main trace信息

编译后生成:

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.Test;

public class LombokLog4j2Test {
    private static final Logger log = LogManager.getLogger(LombokLog4j2Test.class);

    public LombokLog4j2Test() {
    }

    @Test
    public void lombokLog4jTest() {
        log.fatal("fatal信息");
        log.error("error信息");
        log.warn("warn信息");
        log.info("info信息");
        log.debug("debug信息");
        log.trace("trace信息");
    }
}

@Slf4j注解

Slf4j日志门面(Simple Logging Facade For Java) , Slf4j主要是为了给Java日志访问提供一套标准、规范的API框架,其主要意义在于提供接口,具体的实现可以交由其他日志框架,例如log4j和logback等

private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);

使用案例: (Slf4j + Log4j2实现日志输出)

<!-- slf4j日志门面 -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.25</version>
</dependency>
<!-- log4j2适配器 -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>2.12.1</version>
</dependency>

<!-- log4j2日志门面 -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.12.1</version>
</dependency>

<!-- log4j2日志实现 -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.12.1</version>
</dependency>

<!-- 异步日志依赖 -->
<dependency>
    <groupId>com.lmax</groupId>
    <artifactId>disruptor</artifactId>
    <version>3.3.7</version>
</dependency>

resources目录下, log4j2.xml

<?xml version="1.0" encoding="utf-8" ?>
<Configuration status="debug">
    <!-- 配置appender -->
    <Appenders>
        <Console name="consoleAppender" target="SYSTEM_ERR">
            <PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L %thread %m%n"/>
        </Console>
    </Appenders>
    <!-- 配置logger -->
    <Loggers>
        <!-- 配置root logger -->
        <Root level="trace">
            <!-- 引用appender -->
            <AppenderRef ref="consoleAppender"/>
        </Root>
    </Loggers>
</Configuration>
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;

@Slf4j
public class LombokSlf4jTest {

    // @Slf4j会自动生成下面这个Logger对象
    // Logger log = LoggerFactory.getLogger(LombokSlf4jTest.class);

    @Test
    public void lombokSlf4jTest() {
        log.error("error信息");
        log.warn("warn信息");
        log.info("info信息");
        log.debug("debug信息");
        log.trace("trace信息");
    }
}

编译后生成:

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LombokSlf4jTest {
    private static final Logger log = LoggerFactory.getLogger(LombokSlf4jTest.class);

    public LombokSlf4jTest() {
    }

    @Test
    public void lombokSlf4jTest() {
        log.error("error信息");
        log.warn("warn信息");
        log.info("info信息");
        log.debug("debug信息");
        log.trace("trace信息");
    }
}

其他的Lombok日志注解, 自行了解咯

@CommonsLog
private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);
@JBossLog
private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(LogExample.class);
@XSlf4j
private static final org.slf4j.ext.XLogger log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);

@Delegate

被@Delegate注释的属性,会把这个属性类型的公有非静态方法合到当前类

public class Person {

    public void personMsg() {
        System.out.println("Person.personMsg");
    }

    public String printName(String name) {
        return name;
    }

    private Integer printAge(Integer age) {
        return age;
    }

    public static void printOther() {
        System.out.println("Person.printOther");
    }
}
@Getter
@Setter
public class Demo {

    @Delegate
    private Person person;
}

编译后生成:

public class Demo {
    private Person person;

    public Demo() {
    }

    public Person getPerson() {
        return this.person;
    }

    public void setPerson(Person person) {
        this.person = person;
    }

    public void personMsg() {
        this.getPerson().personMsg();
    }

    public String printName(String name) {
        return this.getPerson().printName(name);
    }
}


使用:

public class Main {

    public static void main(String[] args) {
        Demo demo = new Demo();
        demo.setPerson(new Person());
        demo.personMsg();
        System.out.println(demo.printName("lucky"));
    }
}

@Singular

使用 @Singular 注解一个集合字段(如果没有指定 value 属性值,那么集合字段名需要是复数形式),会生成添加元素方法向集合添加单个元素

只能配合@Builder注解使用, 该注解作用于字段和参数上, 一般用在集合属性和集合参数


使用:

public class Main {

    public static void main(String[] args) {
        Demo demo = Demo.builder().name("lucky")
                .num(1).num(2).num(3)
                .build();
        System.out.println("demo = " + demo);
    }
}

控制台输出:
demo = Demo(name=lucky, nums=[1, 2, 3])

lombok: 优秀blog

有关Lombok工具 : 常用注解介绍 (全)的更多相关文章

  1. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

    大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

  2. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  3. Unity 热更新技术 | (三) Lua语言基本介绍及下载安装 - 2

    ?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------

  4. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  5. postman接口测试工具-基础使用教程 - 2

    1.postman介绍Postman一款非常流行的API调试工具。其实,开发人员用的更多。因为测试人员做接口测试会有更多选择,例如Jmeter、soapUI等。不过,对于开发过程中去调试接口,Postman确实足够的简单方便,而且功能强大。2.下载安装官网地址:https://www.postman.com/下载完成后双击安装吧,安装过程极其简单,无需任何操作3.使用教程这里以百度为例,工具使用简单,填写URL地址即可发送请求,在下方查看响应结果和响应状态码常用方法都有支持请求方法:getpostputdeleteGet、Post、Put与Delete的作用get:请求方法一般是用于数据查询,

  6. ruby-on-rails - 有没有一种工具可以在编码时自动保存对文件的增量更改? - 2

    我最喜欢的Google文档功能之一是它会在我工作时不断自动保存我的文档版本。这意味着即使我在进行关键更改之前忘记在某个点进行保存,也很有可能会自动创建一个保存点。至少,我可以将文档恢复到错误更改之前的状态,并从该点继续工作。对于在MacOS(或UNIX)上运行的Ruby编码器,是否有具有等效功能的工具?例如,一个工具会每隔几分钟自动将Gitcheckin我的本地存储库以获取我正在处理的文件。也许我有点偏执,但这点小保险可以让我在日常工作中安心。 最佳答案 虚拟机有些人可能讨厌我对此的回应,但我在编码时经常使用VIM,它具有自动保存功

  7. ruby - 使用 Ruby 开发工具包将文件上传到 Amazon S3 - 2

    我正在尝试上传文件。一个简单的hello.txt。我正在关注文档,但无法将其上传到我的存储桶。#STARTAWSCLIENTs3=Aws::S3::Resource.newbucket=s3.bucket(BUCKET_NAME)begins3.buckets[BUCKET_NAME].objects[KEY].write(:file=>FILE_NAME)puts"Uploadingfile#{FILE_NAME}tobucket#{BUCKET_NAME}."bucket.objects.eachdo|obj|puts"#{obj.key}=>#{obj.etag}"endresc

  8. ruby - 在 StockChart (highchart) 中以编程方式显示柱形图的工具提示 - 2

    我有一个Highstock图表(带有标记和阴影的线条),并且想以编程方式显示一个highstock工具提示,例如,当我选择某个表上的一行(包含图表数据)我想显示相应的highstock工具提示。这可能吗? 最佳答案 股票图表thissolution不起作用:在thisexample你必须更换这个:chart.tooltip.refresh(chart.series[0].data[i]);为此:chart.tooltip.refresh([chart.series[0].points[i]]);解决方案可用here.

  9. 电脑怎么截图?进来看(8种常用截图方法) - 2

    电脑上可以截取图片吗?如果可以,该如何操作呢?相信很多小伙伴都只知道一两种截图的方式,知道的并不全面。其实,电脑上有多种方式截图的,而且非常方便。电脑怎么截图?今天我们就来教大家如何使用电脑截取图片的8种常用方式!操作环境:演示机型:Delloptiplex7050系统版本:Windows10方法一:系统自带截图具体操作:同时按下电脑的自带截图键【Windows+shift+S】,可以选择其中一种方式来截取图片:截屏有矩形截屏、任意形状截屏、窗口截屏和全屏截图。 方法二:QQ截图具体操作:在电脑登录QQ,然后同时按下【Ctrl+Alt+A】,可以任意截图你需要的界面,可以把截图的页面直接下载,

  10. ABB-IRB-1200运动学分析MATLAB RVC工具分析+Simulink-Adams联合仿真 - 2

    一、机器人介绍        此处是基于MATLABRVC工具箱,对ABB-IRB-1200型号的微型机械臂进行正逆向运动学分析,并利Simulink工具实现对机械臂进行具有动力学参数的末端轨迹规划仿真,最后根据机械模型设计Simulink-Adams联合仿真。 图1.ABBIRB 1200尺寸参数示意图ABBIRB 1200提供的两种型号广泛适用于各作业,且两者间零部件通用,两种型号的工作范围分别为700 mm 和 900 mm,大有效负载分别为 7 kg 和5 kg。 IRB 1200 能够在狭小空间内能发挥其工作范围与性能优势,具有全新的设计、小型化的体积、高效的性能、易于集成、便捷的接

随机推荐