Редкое учебное пособие по Ломбоку.

Java Project Lombok
Редкое учебное пособие по Ломбоку.

1. Знакомство с Ломбоком

LombokЭто подключаемый модуль разработки Java, который позволяет разработчикам Java устранять длинные и громоздкие коды в бизнес-проектах с помощью некоторых определенных аннотаций, особенно для простых объектов модели Java (POJO).Используя подключаемый модуль Lombok в среде разработки, разработчики Java могут сэкономить много времени на многократном создании таких методов, как hashCode и equals, а также таких методов, как accessor и toString, для различных моделей бизнес-объектов.Для этих методов Lombok может автоматически генерировать эти методы для нас во время компиляции исходного кода, но это не снижает производительность программы, как рефлексия.

2. Ломбок установка

2.1 Инструменты сборки

Gradle

существуетbuild.gradleДобавьте в файл зависимость lombok:

dependencies {
	compileOnly 'org.projectlombok:lombok:1.18.10'
	annotationProcessor 'org.projectlombok:lombok:1.18.10'
}
Maven

в проекте Мавенpom.xmlДобавьте в файл зависимость lombok:

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

предположим вlibуже есть в каталогеlombok.jar, а затем настройте задачу javac:

<javac srcdir="src" destdir="build" source="1.8">
	<classpath location="lib/lombok.jar" />
</javac>

2.2 IDE

Поскольку Lombok генерирует код только на этапе компиляции, исходный код, аннотированный Lombok, будет выделен в среде IDE, а ошибку можно устранить, установив соответствующий плагин для среды IDE. Здесь это не подробно описано, конкретный метод установки может относиться кSetting up Lombok with Eclipse and IntelliJЭта статья.

3. Подробное объяснение Ломбока

Примечание. В следующем примере используется версия Lombok 1.18.10.

3.1 Аннотации @Getter и @Setter

ты можешь использовать@Getterили@SetterАннотируйте любой класс или поле, и Lombok автоматически сгенерирует методы получения/установки по умолчанию.

@Getter аннотация
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface Getter {
  // 若getter方法非public的话,可以设置可访问级别
	lombok.AccessLevel value() default lombok.AccessLevel.PUBLIC;
	AnyAnnotation[] onMethod() default {};
  // 是否启用延迟初始化
	boolean lazy() default false;
}
@Setter аннотация
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface Setter {
  // 若setter方法非public的话,可以设置可访问级别
	lombok.AccessLevel value() default lombok.AccessLevel.PUBLIC;
	AnyAnnotation[] onMethod() default {};
	AnyAnnotation[] onParam() default {};
}
Пример использования
package com.semlinker.lombok;

@Getter
@Setter
public class GetterAndSetterDemo {
    String firstName;
    String lastName;
    LocalDate dateOfBirth;
}

После того, как приведенный выше код будет скомпилирован Lombok, будет сгенерирован следующий код:

package com.semlinker.lombok;

public class GetterAndSetterDemo {
    String firstName;
    String lastName;
    LocalDate dateOfBirth;

    public GetterAndSetterDemo() {
    }

    // 省略其它setter和getter方法
    public String getFirstName() {
        return this.firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
}
Lazy Getter

@GetterАннотация поддерживает ленивый атрибут, который по умолчанию имеет значение false. Если установлено значение true, включается ленивая инициализация, т. е. инициализация происходит при первом вызове метода получения.

Пример
package com.semlinker.lombok;

public class LazyGetterDemo {
    public static void main(String[] args) {
        LazyGetterDemo m = new LazyGetterDemo();
        System.out.println("Main instance is created");
        m.getLazy();
    }

    @Getter
    private final String notLazy = createValue("not lazy");

    @Getter(lazy = true)
    private final String lazy = createValue("lazy");

    private String createValue(String name) {
        System.out.println("createValue(" + name + ")");
        return null;
    }
}

После того, как приведенный выше код будет скомпилирован Lombok, будет сгенерирован следующий код:

package com.semlinker.lombok;

public class LazyGetterDemo {
    private final String notLazy = this.createValue("not lazy");
    private final AtomicReference<Object> lazy = new AtomicReference();

    // 已省略部分代码
    public String getNotLazy() {
        return this.notLazy;
    }

    public String getLazy() {
        Object value = this.lazy.get();
        if (value == null) {
            synchronized(this.lazy) {
                value = this.lazy.get();
                if (value == null) {
                    String actualValue = this.createValue("lazy");
                    value = actualValue == null ? this.lazy : actualValue;
                    this.lazy.set(value);
                }
            }
        }

        return (String)((String)(value == this.lazy ? null : value));
    }
}

Как видно из приведенного выше кода, при вызове метода getLazy, если значение оказывается нулевым, операция инициализации будет выполняться в синхронизированном блоке кода.

3.2 Constructor Annotations

Аннотация @NoArgsConstructor

использовать@NoArgsConstructorАннотации могут генерировать конструкторы по умолчанию для указанных классов,@NoArgsConstructorАннотации определяются следующим образом:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface NoArgsConstructor {
  // 若设置该属性,将会生成一个私有的构造函数且生成一个staticName指定的静态方法
	String staticName() default "";	
	AnyAnnotation[] onConstructor() default {};
  // 设置生成构造函数的访问级别,默认是public
	AccessLevel access() default lombok.AccessLevel.PUBLIC;
  // 若设置为true,则初始化所有final的字段为0/null/false
	boolean force() default false;
}
Пример
package com.semlinker.lombok;

@NoArgsConstructor(staticName = "getInstance")
public class NoArgsConstructorDemo {
    private long id;
    private String name;
    private int age;
}

После того, как приведенный выше код будет скомпилирован Lombok, будет сгенерирован следующий код:

package com.semlinker.lombok;

public class NoArgsConstructorDemo {
    private long id;
    private String name;
    private int age;

    private NoArgsConstructorDemo() {
    }

    public static NoArgsConstructorDemo getInstance() {
        return new NoArgsConstructorDemo();
    }
}
Аннотация @AllArgsConstructor

использовать@AllArgsConstructorАннотация может генерировать конструктор со всеми членами для указанного класса,@AllArgsConstructorАннотации определяются следующим образом:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface AllArgsConstructor {
  // 若设置该属性,将会生成一个私有的构造函数且生成一个staticName指定的静态方法
	String staticName() default "";
	AnyAnnotation[] onConstructor() default {};
  // 设置生成构造函数的访问级别,默认是public
	AccessLevel access() default lombok.AccessLevel.PUBLIC;
}
Пример
package com.semlinker.lombok;

@AllArgsConstructor
public class AllArgsConstructorDemo {
    private long id;
    private String name;
    private int age;
}

После того, как приведенный выше код будет скомпилирован Lombok, будет сгенерирован следующий код:

package com.semlinker.lombok;

public class AllArgsConstructorDemo {
    private long id;
    private String name;
    private int age;

    public AllArgsConstructorDemo(long id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
}
@RequiredArgsConstructorDemo аннотация

использовать@RequiredArgsConstructorАннотации могут генерировать соответствующие конструкторы для переменных-членов, которые должны быть инициализированы в указанном классе, например окончательные переменные-члены,@RequiredArgsConstructorАннотации определяются следующим образом:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface RequiredArgsConstructor {
  // 若设置该属性,将会生成一个私有的构造函数且生成一个staticName指定的静态方法
	String staticName() default "";
	AnyAnnotation[] onConstructor() default {};
  // 设置生成构造函数的访问级别,默认是public
	AccessLevel access() default lombok.AccessLevel.PUBLIC;
}
Пример
package com.semlinker.lombok;

@RequiredArgsConstructor
public class RequiredArgsConstructorDemo {
    private final long id;
    private String name;
    private int age;
}

После того, как приведенный выше код будет скомпилирован Lombok, будет сгенерирован следующий код:

package com.semlinker.lombok;

public class RequiredArgsConstructorDemo {
    private final long id;
    private String name;
    private int age;

    public RequiredArgsConstructorDemo(long id) {
        this.id = id;
    }
}

3.3 Аннотация @EqualsAndHashCode

использовать@EqualsAndHashCodeАннотации могут генерировать методы equals и hashCode для указанных классов,@EqualsAndHashCodeАннотации определяются следующим образом:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface EqualsAndHashCode {
  // 指定在生成的equals和hashCode方法中需要排除的字段列表
	String[] exclude() default {};
	
  // 显式列出用于identity的字段,一般情况下non-static,non-transient字段会被用于identity
	String[] of() default {};
	
  // 标识在执行字段计算前,是否调用父类的equals和hashCode方法
	boolean callSuper() default false;
	
	boolean doNotUseGetters() default false;
	
	AnyAnnotation[] onParam() default {};
	
	@Deprecated
	@Retention(RetentionPolicy.SOURCE)
	@Target({})
	@interface AnyAnnotation {}
	
	@Target(ElementType.FIELD)
	@Retention(RetentionPolicy.SOURCE)
	public @interface Exclude {}
	
	@Target({ElementType.FIELD, ElementType.METHOD})
	@Retention(RetentionPolicy.SOURCE)
	public @interface Include {
		String replaces() default "";
	}
}
Пример
package com.semlinker.lombok;

@EqualsAndHashCode
public class EqualsAndHashCodeDemo {
    String firstName;
    String lastName;
    LocalDate dateOfBirth;
}

После того, как приведенный выше код будет скомпилирован Lombok, будет сгенерирован следующий код:

package com.semlinker.lombok;

public class EqualsAndHashCodeDemo {
    String firstName;
    String lastName;
    LocalDate dateOfBirth;

    public EqualsAndHashCodeDemo() {
    }

    public boolean equals(Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof EqualsAndHashCodeDemo)) {
            return false;
        } else {
            EqualsAndHashCodeDemo other = (EqualsAndHashCodeDemo)o;
            if (!other.canEqual(this)) {
                return false;
            } else {
              // 已省略大量代码
        }
    }

    public int hashCode() {
        int PRIME = true;
        int result = 1;
        Object $firstName = this.firstName;
        int result = result * 59 + ($firstName == null ? 43 : $firstName.hashCode());
        Object $lastName = this.lastName;
        result = result * 59 + ($lastName == null ? 43 : $lastName.hashCode());
        Object $dateOfBirth = this.dateOfBirth;
        result = result * 59 + ($dateOfBirth == null ? 43 : $dateOfBirth.hashCode());
        return result;
    }
}

3.4 Аннотация @ToString

использовать@ToStringАннотация может генерировать метод toString для указанного класса,@ToStringАннотации определяются следующим образом:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface ToString {
  // 打印输出时是否包含字段的名称
	boolean includeFieldNames() default true;
	
  // 列出打印输出时,需要排除的字段列表
	String[] exclude() default {};
	
  // 显式的列出需要打印输出的字段列表
	String[] of() default {};
	
  // 打印输出的结果中是否包含父类的toString方法的返回结果
	boolean callSuper() default false;
	
	boolean doNotUseGetters() default false;
	
	boolean onlyExplicitlyIncluded() default false;
	
	@Target(ElementType.FIELD)
	@Retention(RetentionPolicy.SOURCE)
	public @interface Exclude {}
	
	@Target({ElementType.FIELD, ElementType.METHOD})
	@Retention(RetentionPolicy.SOURCE)
	public @interface Include {
		int rank() default 0;
		String name() default "";
	}
}
Пример
package com.semlinker.lombok;

@ToString(exclude = {"dateOfBirth"})
public class ToStringDemo {
    String firstName;
    String lastName;
    LocalDate dateOfBirth;
}

После того, как приведенный выше код будет скомпилирован Lombok, будет сгенерирован следующий код:

package com.semlinker.lombok;

public class ToStringDemo {
    String firstName;
    String lastName;
    LocalDate dateOfBirth;

    public ToStringDemo() {
    }

    public String toString() {
        return "ToStringDemo(firstName=" + this.firstName + ", lastName=" + 
          this.lastName + ")";
    }
}

3.5 Аннотация @Data

@DataАннотации имеют тот же эффект, что и совместное использование следующих аннотаций:

  • @ToString
  • @Getter
  • @Setter
  • @RequiredArgsConstructor
  • @EqualsAndHashCode

@DataАннотации определяются следующим образом:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface Data {
	String staticConstructor() default "";
}
Пример
package com.semlinker.lombok;

@Data
public class DataDemo {
    private Long id;
    private String summary;
    private String description;
}

После того, как приведенный выше код будет скомпилирован Lombok, будет сгенерирован следующий код:

package com.semlinker.lombok;

public class DataDemo {
    private Long id;
    private String summary;
    private String description;

    public DataDemo() {
    }

    // 省略summary和description成员属性的setter和getter方法
    public Long getId() {
        return this.id;
    }
  
    public void setId(Long id) {
        this.id = id;
    }

    public boolean equals(Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof DataDemo)) {
            return false;
        } else {
            DataDemo other = (DataDemo)o;
            if (!other.canEqual(this)) {
                return false;
            } else {
               // 已省略大量代码
            }
        }
    }

    protected boolean canEqual(Object other) {
        return other instanceof DataDemo;
    }

    public int hashCode() {
        int PRIME = true;
        int result = 1;
        Object $id = this.getId();
        int result = result * 59 + ($id == null ? 43 : $id.hashCode());
        Object $summary = this.getSummary();
        result = result * 59 + ($summary == null ? 43 : $summary.hashCode());
        Object $description = this.getDescription();
        result = result * 59 + ($description == null ? 43 : $description.hashCode());
        return result;
    }

    public String toString() {
        return "DataDemo(id=" + this.getId() + ", summary=" + this.getSummary() + ", description=" + this.getDescription() + ")";
    }
}

3.6 Аннотация @Log

если вы будете@LogВарианты , в классе (для любой системы ведения журналов, которую вы используете); после этого у вас будет статическое конечное поле журнала, которое вы затем можете использовать для вывода журналов.

@Log
private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());
@Log4j
private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class);
@Log4j2
private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
@Slf4j
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
@XSlf4j
private static final org.slf4j.ext.XLogger log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);
@CommonsLog
private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);

3.7 @Синхронизированная аннотация

@Synchronizedявляется более безопасным вариантом модификатора метода synchronized. иsynchronizedОпять же, эта аннотация может применяться только к статическим методам и методам экземпляра. Он работает какsynchronizedключевое слово, но оно заблокировано на другом объекте.synchronizedКогда ключевое слово применяется к методу экземпляра, оно блокирует объект this, а при применении к статическому методу оно блокирует объект класса. Для методов, объявленных с аннотацией @Synchronized, он блокирует$LOCKили$lock.@SynchronizedАннотации определяются следующим образом:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Synchronized {
  // 指定锁定的字段名称
	String value() default "";
}
Пример
package com.semlinker.lombok;

public class SynchronizedDemo {
    private final Object readLock = new Object();

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

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

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

После того, как приведенный выше код будет скомпилирован Lombok, будет сгенерирован следующий код:

package com.semlinker.lombok;

public class SynchronizedDemo {
    private static final Object $LOCK = new Object[0];
    private final Object $lock = new Object[0];
    private final Object readLock = new Object();

    public SynchronizedDemo() {
    }

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

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

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

3.8 Аннотация @Builder

использовать@BuilderАннотация может реализовывать шаблон построителя для данного класса, и аннотацию можно поместить в класс, конструктор или метод.@BuilderАннотации определяются следующим образом:

@Target({TYPE, METHOD, CONSTRUCTOR})
@Retention(SOURCE)
public @interface Builder {
	@Target(FIELD)
	@Retention(SOURCE)
	public @interface Default {}

  // 创建新的builder实例的方法名称
	String builderMethodName() default "builder";
	// 创建Builder注解类对应实例的方法名称
	String buildMethodName() default "build";
	// builder类的名称
	String builderClassName() default "";
	
	boolean toBuilder() default false;
	
	AccessLevel access() default lombok.AccessLevel.PUBLIC;
	
	@Target({FIELD, PARAMETER})
	@Retention(SOURCE)
	public @interface ObtainVia {
		String field() default "";
		String method() default "";
		boolean isStatic() default false;
	}
}
Пример
package com.semlinker.lombok;

@Builder
public class BuilderDemo {
    private final String firstname;
    private final String lastname;
    private final String email;
}

После того, как приведенный выше код будет скомпилирован Lombok, будет сгенерирован следующий код:

package com.semlinker.lombok;

public class BuilderDemo {
    private final String firstname;
    private final String lastname;
    private final String email;

    BuilderDemo(String firstname, String lastname, String email) {
        this.firstname = firstname;
        this.lastname = lastname;
        this.email = email;
    }

    public static BuilderDemo.BuilderDemoBuilder builder() {
        return new BuilderDemo.BuilderDemoBuilder();
    }

    public static class BuilderDemoBuilder {
        private String firstname;
        private String lastname;
        private String email;

        BuilderDemoBuilder() {
        }

        public BuilderDemo.BuilderDemoBuilder firstname(String firstname) {
            this.firstname = firstname;
            return this;
        }

        public BuilderDemo.BuilderDemoBuilder lastname(String lastname) {
            this.lastname = lastname;
            return this;
        }

        public BuilderDemo.BuilderDemoBuilder email(String email) {
            this.email = email;
            return this;
        }

        public BuilderDemo build() {
            return new BuilderDemo(this.firstname, this.lastname, this.email);
        }

        public String toString() {
            return "BuilderDemo.BuilderDemoBuilder(firstname=" + this.firstname + ", lastname=" + this.lastname + ", email=" + this.email + ")";
        }
    }
}

3.9 Аннотация @SneakyThrows

@SneakyThrowsАннотации используются для автоматического генерирования проверенных исключений без явного генерирования их в методе с помощью инструкции throw.@SneakyThrowsАннотации определяются следующим образом:

@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.SOURCE)
public @interface SneakyThrows {
	// 设置你希望向上抛的异常类
	Class<? extends Throwable>[] value() default java.lang.Throwable.class;
}
Пример
package com.semlinker.lombok;

public class SneakyThrowsDemo {
    @SneakyThrows
    @Override
    protected Object clone() {
        return super.clone();
    }
}

После того, как приведенный выше код будет скомпилирован Lombok, будет сгенерирован следующий код:

package com.semlinker.lombok;

public class SneakyThrowsDemo {
    public SneakyThrowsDemo() {
    }

    protected Object clone() {
        try {
            return super.clone();
        } catch (Throwable var2) {
            throw var2;
        }
    }
}

3.10 Аннотация @NonNull

Вы можете использовать параметры метода или конструктора@NonNullаннотацию, он автоматически сгенерирует для вас ненулевой оператор проверки.@NonNullАннотации определяются следующим образом:

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE, ElementType.TYPE_USE})
@Retention(RetentionPolicy.CLASS)
@Documented
public @interface NonNull {
}
Пример
package com.semlinker.lombok;

public class NonNullDemo {
    @Getter
    @Setter
    @NonNull
    private String name;
}

После того, как приведенный выше код будет скомпилирован Lombok, будет сгенерирован следующий код:

package com.semlinker.lombok;

public class NonNullDemo {
    @NonNull
    private String name;

    public NonNullDemo() {
    }

    @NonNull
    public String getName() {
        return this.name;
    }

    public void setName(@NonNull String name) {
        if (name == null) {
            throw new NullPointerException("name is marked non-null but is null");
        } else {
            this.name = name;
        }
    }
}

3.11 @Чистая аннотация

@CleanАннотации используются для автоматического управления ресурсами. Прежде чем они будут использованы в локальных переменных, ресурсы будут автоматически очищены перед выполнением и выходом из текущей области переменных.try-finallyкод, подобный этому, чтобы закрыть поток.

@Target(ElementType.LOCAL_VARIABLE)
@Retention(RetentionPolicy.SOURCE)
public @interface Cleanup {
  // 设置用于执行资源清理/回收的方法名称,对应方法不能包含任何参数,默认名称为close。
	String value() default "close";
}
Пример
package com.semlinker.lombok;

public class CleanupDemo {
    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);
        }
    }
}

После того, как приведенный выше код будет скомпилирован Lombok, будет сгенерирован следующий код:

package com.semlinker.lombok;

public class CleanupDemo {
    public CleanupDemo() {
    }

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

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

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

                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();
            }
        }
    }
}

3.11 @С аннотацией

Применить к полю класса@WithПосле аннотации,withFieldName(newValue)метод, который вызовет соответствующий конструктор на основе newValue для создания экземпляра, соответствующего текущему классу.@WithАннотации определяются следующим образом:

@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface With {
    AccessLevel value() default AccessLevel.PUBLIC;

    With.AnyAnnotation[] onMethod() default {};

    With.AnyAnnotation[] onParam() default {};

    @Deprecated
    @Retention(RetentionPolicy.SOURCE)
    @Target({})
    public @interface AnyAnnotation {
    }
}
Пример
public class WithDemo {
    @With(AccessLevel.PROTECTED)
    @NonNull
    private final String name;
    @With
    private final int age;

    public WithDemo(String name, int age) {
        if (name == null) throw new NullPointerException();
        this.name = name;
        this.age = age;
    }
}

После того, как приведенный выше код будет скомпилирован Lombok, будет сгенерирован следующий код:

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

    public WithDemo(String name, int age) {
        if (name == null) {
            throw new NullPointerException();
        } else {
            this.name = name;
            this.age = age;
        }
    }

    protected WithDemo withName(@NonNull String name) {
        if (name == null) {
            throw new NullPointerException("name is marked non-null but is null");
        } else {
            return this.name == name ? this : new WithDemo(name, this.age);
        }
    }

    public WithDemo withAge(int age) {
        return this.age == age ? this : new WithDemo(this.name, age);
    }
}

3.12 Другие функции

val

val используется перед локальными переменными, что эквивалентно объявлению переменной как final, и 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());
    }
  }
}

Приведенный выше код эквивалентен:

public class ValExample {
  public String example() {
    final ArrayList<String> example = new ArrayList<String>();
    example.add("Hello, World!");
    final String foo = example.get(0);
    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());
    }
  }
}

До сих пор был представлен мощный инструмент Lombok. Если вас интересует принцип его реализации, то рекомендуется прочитатьДорога кода обезьяныбольшой пареньДесять минут, чтобы понять использование и принцип ЛомбокаЭта статья.

Примерный адрес проекта:Github - springboot2-lombok

4. Справочные ресурсы