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)
下面的代码表明,在某种程度上这是可能的(我没有测试基本类型等极端情况)。
它使用 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)