在泛型类中推断参数类型,它们是提供的参数的嵌套泛型类型

时间:2014-12-31 12:49:23

标签: java generics java-8

我有一个班级:

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;中)而不使用上面提到的构造函数?

1 个答案:

答案 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实例,无论您如何对其进行参数化,因为它是无状态的(并且仅存在以使编译器满意......)。