我需要实例化一个具有未知泛型类型的对象,然后对它应用泛型方法。这就是我的立场:
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型。有机会使用它吗?
答案 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
的任何其他实现。这就是为什么显式类型转换是唯一正确的解决方案,因为它记录了对对象的实际运行时类型的假设。所有其他尝试都会在某处产生至少一个编译器警告。