2015-04-27 4 views
24

Если я добавлю @Builder в класс. Создается метод построения.Необходимые аргументы с lombok @Builder

Person.builder().name("john").surname("Smith").build(); 

У меня есть требование, когда требуется конкретное поле. В этом случае поле имени требуется, но фамилия не указана. В идеале я хотел бы заявить об этом так.

Person.builder("john").surname("Smith").build() 

Не могу решить, как это сделать. Я попытался добавить @Builder к конструктору, но это не сработало.

@Builder 
public Person(String name) { 
    this.name = name; 
} 
+0

Проблемы с выпуском Lombok на GitHub имеют одну открытую проблему для этого https://github.com/rzwitserloot/lombok/issues/1043 – lennykey

ответ

29

Вы можете сделать это легко с Ломбок конфигурации аннотаций

import lombok.Builder; 
import lombok.ToString; 

@Builder(builderMethodName = "hiddenBuilder") 
@ToString 
public class Person { 

    private String name; 
    private String surname; 

    public static PersonBuilder builder(String name) { 
     return hiddenBuilder().name(name); 
    } 
} 

И затем использовать его как то

Person p = Person.builder("Name").surname("Surname").build(); 
System.out.println(p); 

Конечно @ToString не является обязательным здесь.

+2

было бы лучше, если бы вы могли объяснить немного больше. – Blip

+0

@Blip означает, что генератор создаст метод 'hiddenBuilder' вместо' builder'. Затем пользовательский метод 'builder' вызывает его с необходимыми аргументами (что может быть любым, что вам нравится). – jax

+0

@Blip as jax explaind 'builderMethodName' value - это имя метода, который будет автоматически создан. Запрос состоял в том, чтобы иметь метод 'builder' с параметром' String name', поэтому для этого потребовалось другое имя автоматически созданного метода построения. Также вы должны прочитать документацию [lombok @Builder] (https://projectlombok.org/features/Builder.html), чтобы понять, что lombok также создает для нас класс PersonBuilder. – Pawel

9

Вот другой подход:

@Builder() 
@Getter 
@ToString 
public class Person { 

    private final String name; 
    private final String surname; 

    public static PersonBuilder builder(String name){ 
     return new PersonBuilder().name(name); 
    } 

    public static void main(String[] args) { 
     Person p = Person.builder("John Doe") 
       .surname("Bill") 
       .build(); 
    } 
} 
+0

Я предпочитаю ваш подход, поскольку он просто перегружает метод 'builder', делая синтаксис кратким и естественным. – Jezor

+0

Проблема с этим подходом заключается в том, что builder() все еще виден, поэтому он не делает * действительно * необходимые параметры. Во что бы то ни стало, да, нам также нужно использовать аннотацию @NonNull, но это проверка времени выполнения - определенно недостаточно хорошая, если вы пытаетесь создать суперинтуитивный объект. Жаль, что в ломбоке не существует такого подхода - даже если бы мы сделали способ сделать метод builder() частным, мы могли бы создать собственный перегруженный метод public builder (...) с требуемыми параметрами. –

14

Я рекомендовал бы против такого подхода, как вы будете бороться, чтобы применить его последовательно на других объектах. Вместо этого вы можете просто пометить поля с помощью аннотации @lombok.NonNull, и Lombok будет генерировать нулевые проверки для вас в конструкторе и сеттерах, так что Builder.build() не сработает, если эти поля не будут установлены.

Использование шаблона построителя позволяет иметь очень четкое определение того, какие поля вы устанавливаете для каких значений. Это уже потеряно для поля имени в вашем примере, и оно будет потеряно для всех других обязательных полей, если вы создаете объект с несколькими обязательными полями. Рассмотрим следующий пример: можете ли вы указать, в каком поле указывается код?

Person.builder("John", "Michael", 16, 1987) // which is name, which is surname? what is 16? 
    .year(1982) // if this is year of birth, then what is 1987 above? 
    .build() 
+1

Ошибка выполнения vs Ошибка времени компиляции. Всегда предпочитайте ошибку времени компиляции! – jax

+0

@jax Они не проверяют одно и то же. Требование установить поле не проверяет нулевое значение. Проверка на null будет ошибкой времени выполнения (в чистой Java) независимо от того, требуется ли вам поле или нет. –

+0

Нет, метод строителя не может быть запущен до тех пор, пока не будут указаны все необходимые аргументы, что удовлетворяет контракту классов. Ваш способ позволяет создать недопустимый объект во время выполнения. – jax

5

Это мое решение проблемы

import lombok.Builder; 
import lombok.Data; 
import lombok.NonNull; 

@Data 
@Builder(builderMethodName = "privateBuilder") 
public class Person { 
    @NonNull 
    private String name; 
    @NonNull 
    private String surname; 
    private int age;//optional 

public static Url safeBuilder() { 
    return new Builder(); 
} 

interface Url { 
    Surname name(String name); 
} 

interface Surname { 
    Build surname(String surname); 
} 

interface Build { 
    Build age(int age); 
    Person build(); 
} 

public static class Builder implements Url, Surname, Build { 
    PersonBuilder pb = Person.privateBuilder(); 

    @Override 
    public Surname name(String name) { 
     pb.name(name); 
     return this; 
    } 

    @Override 
    public Build surname(String surname) { 
     pb.surname(surname); 
     return this; 

    } 

    @Override 
    public Build age(int age) { 
     pb.age(age); 
     return this; 
    } 

    @Override 
    public Person build() { 
     return pb.build(); 
    } 
    } 
} 

вдохновлен этого блога:

https://blog.jayway.com/2012/02/07/builder-pattern-with-a-twist/

+3

Вот что я хочу, чтобы сгенерировал ломбок для меня. – okutane

0

Вот тест JUnit, чтобы продемонстрировать поведение всех комбинаций окончательным и NonNull:

import static org.junit.Assert.fail; 

import org.junit.Test; 

import lombok.Builder; 
import lombok.ToString; 

public class BuilderTests { 
    @Test 
    public void allGiven() { 
     System.err.println(Foo.builder().nonFinalNull("has_value").nonFinalNonNull("has_value").finalNull("has_value") 
       .finalNonNull("has_value").build()); 
    } 

    @Test 
    public void noneGiven() { 
     try { 
      System.err.println(Foo.builder().build().toString()); 
      fail(); 
     } catch (NullPointerException e) { 
      // expected 
     } 
    } 

    @Test 
    public void nonFinalNullOmitted() { 
     System.err.println(
       Foo.builder().nonFinalNonNull("has_value").finalNull("has_value").finalNonNull("has_value").build()); 
    } 

    @Test 
    public void nonFinalNonNullOmitted() { 
     try { 
      System.err.println(
        Foo.builder().nonFinalNull("has_value").finalNull("has_value").finalNonNull("has_value").build()); 
      fail(); 
     } catch (NullPointerException e) { 
      // expected 
     } 
    } 

    @Test 
    public void finalNullOmitted() { 
     System.err.println(
       Foo.builder().nonFinalNull("has_value").nonFinalNonNull("has_value").finalNonNull("has_value").build()); 
    } 

    @Test 
    public void finalNonNullOmitted() { 
     try { 
      System.err.println(Foo.builder().nonFinalNull("has_value").nonFinalNonNull("has_value") 
        .finalNull("has_value").build()); 
      fail(); 
     } catch (NullPointerException e) { 
      // expected 
     } 
    } 

    @Builder 
    @ToString 
    private static class Foo { 
     private String nonFinalNull; 

     @lombok.NonNull 
     private String nonFinalNonNull; 

     private final String finalNull; 

     @lombok.NonNull 
     private final String finalNonNull; 
    } 
} 
Смежные вопросы