如何使用泛型而不是注释来实现构建器类?

tow*_*owi 3 java generics wrapper factory-pattern

我想编写一个通用的构建器类,它包含任何 java 类并提供特定样式的 setter 函数。我不确定这是否可以称为“动态生成的函数”。

当我有一个豆豆 Pojo 类时,即

class Pojo {
    public void setValue(int value) {...}
    public void setName(String name) {...}
}
Run Code Online (Sandbox Code Playgroud)

我的Maker班级应该可以这样使用:

Pojo p = Builder<Pojo>.create(new Pojo())
             .setName("Funny")
             .setValue(123)
             .build();
Run Code Online (Sandbox Code Playgroud)

如您所见,它所做的工作应该类似于

class PojoBuilder {
    private Pojo pojo;
    PojoBuilder(Pojo pojo) { this.pojo = pojo; }
    public static PojoMaker create(Pojo p) { return new PojoBuilder(p); }
    public PojoBuilder setName(String name) { pojo.setName(name); return this; }
    public PojoBuilder setValue(int val) { pojo.setValue(val); return this; }
    public Pojo make() { return pojo; }
}
Run Code Online (Sandbox Code Playgroud)

只是,我想Maker成为通用的。显然,“setXyz”-Methods 依赖于泛型参数。怎么做?

当然,功能相同但语法不同的方法也可以。

我想在没有注释的情况下执行此操作:使用我收集的注释,我需要对我的源代码进行第二次 javac-pass,生成包装器代码。这似乎是Limbok所做的或某些JPA 包装器的工作方式。但是当我与Mockito 一起工作时,似乎不需要这个通行证。那么,我该如何使用泛型来实现呢?

小智 8

最简单的方法是使用 Lombok 库。

@Value
@Builder
public class MessageEvent<T> {
    T message;
    TypeEnum type;
}
Run Code Online (Sandbox Code Playgroud)

用法是这样的。

MessageEvent<String> messageEvent = MessageEvent.<String>builder().message("test").build();
Run Code Online (Sandbox Code Playgroud)


NoD*_*und 2

下面的代码表明,在某种程度上这是可能的(我没有测试基本类型等极端情况)。

它使用 Java 8、lamdba 和类型擦除。

由于在 Java 8 中,您可以使用 X::new 引用构造函数,并使用相同的语法引用方法,因此它的工作原理是将每个方法及其参数堆叠到映射中,这样我们就不会依赖于特定实例(因此 build( ) 可以创建 Foobar 的新实例)。

package foobar;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.Supplier;

public class Maker<T> {
  private final Supplier<? extends T> supplier;
  /**
   * We need to store the instance since build must return a new instance.
   * <p>
   * Sadly, we need to rely on type erasure (hence BiConsumer, not BiConsumer<T,V>).
   */
  @SuppressWarnings("rawtypes")
  private final Map<BiConsumer, Object> values = new HashMap<>();

  public Maker(final Supplier<? extends T> supplier) {
    this.supplier = supplier;
  }

  public static <T> Maker<T> create(final Supplier<? extends T> builder) {
    return new Maker<>(builder);
  }

  public <U> Maker<T> set(final BiConsumer<T, U> consumer, final U value) {
    values.put(consumer, value);
    return this;
  }

  @SuppressWarnings("unchecked")
  public T create() {
    final T instance = supplier.get();

    values.forEach((key, value) -> {
      key.accept(instance, value);
    });

    return instance;
  }

  public static void main(final String[] args) {
    final Maker<Foobar> maker = Maker.create(Foobar::new).set(Foobar::setName, "Name");

    final AtomicInteger generator = new AtomicInteger(0);
    Arrays.asList("Alpha", "Beta", "Gamma").forEach(name -> {
      final Integer id = generator.incrementAndGet();

      maker.set(Foobar::setName, name);
      maker.set(Foobar::setId, id);
      final Foobar foobar = maker.create();

      if (!name.equals(foobar.getName())) {
        throw new AssertionError("expected " + name + ", got " + foobar.getName());
      }
      if (!id.equals(foobar.getId())) {
        throw new AssertionError("expected " + id + ", got " + foobar.getId());
      }

      System.out.println(foobar);

    });

  }          
}
Run Code Online (Sandbox Code Playgroud)

使用 Foobar 类:

public class Foobar {
  private Integer id;
  private String name;    
  public Integer getId() {return id;}
  public void setId(final Integer id) {this.id = id;}    
  public String getName() {return name;
  public void setName(final String name) {this.name = name;}    
  @Override public String toString() {
    return "Foobar [id=" + id + ", name=" + name + "]";
  }    
}
Run Code Online (Sandbox Code Playgroud)