我有一个班级:
class MyClass<F extends Field, G extends OtherClass<F>> {
...
}
问题是,鉴于有关课程G
的信息,我可以轻松推断出类型F
。但是,现在实例化需要我这样做:
new MyClass<ConcreteField, ConcreteOtherClass>();
要使用<>
语法,我使用了一个小的hack并添加了一个构造函数:
public MyClass(Class<G> gClass) {}
这允许我使用菱形语法,因为编译器现在能够轻松地推断出类型:
new MyClass<>(ConcreteOtherClass.class);
有没有办法让我只需要传递G
类型(如在&#39;构造函数hack&#39;中)而不使用上面提到的构造函数?
答案 0 :(得分:1)
考虑了一段时间,但我没有找到任何允许这种类型推断的简单通用构造。
关于专门的伪参数构造函数,值得注意的是,您不需要传递实际的Class
实例;任何允许推断G
的类型,包括G
本身都可行。例如,如果您将public MyClass(Class<G> gClass) {}
构造函数更改为public MyClass(G ignored) {}
,则可以使用new MyClass<>((ConcreteOtherClass)null)
实例化该类。
您还可以通过添加提供虚拟值的方法隐藏null
引用并编写实例化而不进行任何类型转换:
class MyClass<F extends Field, G extends OtherClass<F>> {
public MyClass() {}
public MyClass(G dummy) {}
public static <T> T infer() {
return null;
}
}
然后你可以像这样使用完整类型推断来实例化类:
new MyClass<>(MyClass.<ConcreteOtherClass>infer())
由于您要求解决方案没有专门的构造函数,我将添加这样的解决方案。但是你会发现这并没有真正提高代码的可维护性:
interface Instantiator<T extends OtherClass<?>> extends Runnable {
default <M extends MyClass<?,? extends T>> M get(Supplier<? extends M> s) {
return s.get();
}
static <X extends OtherClass<?>> Instantiator<X> i() {
return ()->{};
}
}
使用此助手类,您可以使用默认构造函数将类型推断实例化为MyClass
Instantiator.<ConcreteOtherClass>i().get(MyClass::new)
请注意,这个两步实例化也适用于普通类型,但是使这个Instantiator
成为一个功能接口并通过lambda表达式实例化它是使它成为类型安全单例的唯一方法,即当前Oracle的实现,运行时中只有一个Instantiator
实例,无论您如何对其进行参数化,因为它是无状态的(并且仅存在以使编译器满意......)。