实例化并应用具有未知泛型类型的泛型方法 - Java

时间:2015-12-16 15:14:32

标签: java generics reflection

我需要实例化一个具有未知泛型类型的对象,然后对它应用泛型方法。这就是我的立场:

public static void main(String[] args)
{
    BarIf bar = newRandomBarImpl();

    Foo foo1 = newFooBar(bar.getClass()); // warning
    Foo<?> foo2 = newFooBar(bar.getClass()); // error
    Foo<? extends BarIf> foo3 = newFooBar(bar.getClass()); // error

    foo1.doSomething(bar); // warning
    foo2.doSomething(bar); // error
    foo3.doSomething(bar); // error
}

static <T extends FooIf<S>, S extends BarIf> T newFooBar(Class<S> barClass){}

static <T extends BarIf> T newRandomBarImpl(){}

interface FooIf<T extends BarIf>
{
    public void doSomething(T t);
}

interface BarIf{}

class Foo<T extends BarIf> implements FooIf<T>
{
    public void doSomething(T t){}
}

奇怪的是,对于foo2和foo3,newFooBar()方法返回FooIf而不是Foo。我猜类型推断是混乱的。但我不能通过方法通用参数,因为我不知道条形类型。

我需要的是Foo<bar.getClass()>。有办法吗?

我尝试使用TypeToken但最终得到的是T型而不是实际的Bar型。有机会使用它吗?

1 个答案:

答案 0 :(得分:1)

首先,声明如

static <T extends BarIf> T newRandomBarImpl(){}

是胡说八道。它基本上说“无论调用者替代T,该方法都会返回它”。换句话说,你可以写

ArbitraryTypeExtendingBarIf x=newRandomBarImpl();

没有收到编译器警告。显然,这是行不通的。 newRandomBarImpl()ArbitraryTypeExtendingBarIf一无所知。方法名称表明您实际上想表达newRandomBarImpl()可以返回BarIf的任意实现,但这是对泛型的不必要使用,

BarIf newRandomBarImpl(){}

已经表示此方法可以返回BarIf的任意子类型。实际上,由于BarIf是一个抽象类型,因此此方法必须返回BarIf的子类型,并且它没有指定它将是哪一个。

同样适用于声明

static <T extends FooIf<S>, S extends BarIf> T newFooBar(Class<S> barClass){}

它还声称调用者可以选择该方法返回的FooIf实现。正确的声明是

static <S extends BarIf> FooIf<S> newFooBar(Class<S> barClass){}

因为该方法决定它将返回FooIf的哪个实现,而不是调用者。

关于您处理FooIf的其他尝试,您不能使用通配符参数化的类型,也不能使用Reflection修复它。但您可以使用类型参数编写泛型代码:

public static void main(String[] args)
{
    BarIf bar = newRandomBarImpl();
    performTheAction(bar.getClass(), bar);
}
static <T extends BarIf> void performTheAction(Class<T> cl, BarIf obj) {
    FooIf<T> foo=newFooBar(cl);
    foo.doSomething(cl.cast(obj));
}
static <S extends BarIf> FooIf<S> newFooBar(Class<S> barClass){}
static BarIf newRandomBarImpl(){}

interface FooIf<T extends BarIf> {
    public void doSomething(T t);
}
interface BarIf{}

方法performTheAction是通用的,换句话说,使用表示为类型参数T的未知类型。如? extends BarIf方法所示,可以使用未知类型main调用此方法。

但是,请记住,对类型X的每次引用都意味着引用的对象可能具有X的子类型,而无需担心它。

您可以在此处使用基类BarIf,无论该对象具有BarIf的实际子类型:

BarIf bar = newRandomBarImpl();
FooIf<BarIf> foo=newFooBar(BarIf.class);
foo.doSomething(bar);

请注意,如果要使用未在界面中指定的实际实现类型Foo的方法,则必须将FooIf强制转换为Foo。您可以将FooIf<BarIf>投射到Foo<BarIf>而不会发出警告,因为如果Foo<X> implements FooIf<X>,通用类型转换是正确的。

但是,它可能在运行时失败,因为方法newFooBar不需要返回Foo的实例而不是FooIf的任何其他实现。这就是为什么显式类型转换是唯一正确的解决方案,因为它记录了对对象的实际运行时类型的假设。所有其他尝试都会在某处产生至少一个编译器警告。