2014-10-02 71 views
5

如果我举个例子,有一个使用可变参数的类类型的扩展超类像 这样的方法:可变参数

public static <E extends Example> void test(Class<E>... es){} 

然后我尝试调用该方法有两种不同的例子的子类,我只能这样做,如果我用它的两个类的数组。

//this does not work 
test(E1.class,E2.class); 
//this does work 
test(new Class[]{E1.class,E2.class}); 
public class E1 extends Example {} 
public class E2 extends Example {} 

这是为什么?

回答

6

此行不会编译:

test(E1.class,E2.class); 

只有一种类型参数E和Java必须准确推断的参数类型相匹配。它不能推断出Example,因为对象是Class<E1>Class<E2>,而不是Class<Example>。 Java泛型的不变性防止了这种情况的发生。

您可以解决此通过引入上test的泛型类型参数的上限通配符:

public static <E extends Example> void test(Class<? extends E>... es) 

这使得Java推断ExampleE,通过满足与E1E2上限通配符。

第二行创建一个Class es的原始数组,绕过泛型并生成“未经检查的调用”警告。

new Class[]{E1.class,E2.class} 

如果你试图提供一个类型参数Class在这里,你会得到一个编译器错误与任何中途合理的类型参数:

// Needs Class<Example> but found Class<E1> and Class<E2> 
test(new Class<Example>[]{E1.class,E2.class}); 

// Needs Class<E1> but found Class<E2> 
test(new Class<E1>[]{E1.class,E2.class}); 

// Needs Class<E2> but found Class<E1> 
test(new Class<E2>[]{E1.class,E2.class}); 

通过使用通配符通过满足推理这里只是揭示了真正的问题 - 通用数组的创建。

// Generic array creation 
test(new Class<? extends Example>[]{E1.class,E2.class}); 
+0

非常感谢。这对我的计划永远不会造成伤害,但我一直在为此挠头。我非常感谢澄清。 – Squirvin 2014-10-03 01:47:53

2

您可以定义扩展Example的单个类的泛型E.你不能在你的呼叫中引用两个不同的类,因为它不知道E是什么类型。它只期望一种类型。

虽然这不起作用:

test(E1.class, E2.class); 

这并不:

test(E1.class, E1.class); 

你可以用一个数组做到这一点的原因是因为类型擦除。编译器没有看到数组中的类是不同的。

如果你改变你的方法来接受任何延伸Example的类,它就会工作。

public static void test(Class<? extends Example>...classes) 
+1

我想你已经解释得很好。但要等同于OP的示例,您的签名应该是'public static void test(Class ... classes)''。它会(无论如何)发出'-Xlint:unchecked'警告。 – 5gon12eder 2014-10-02 22:16:27

+0

@ 5gon12eder:你给出的签名和这个回答中的签名完全相同,即他们接受完全相同的参数类。更简单的(在这个答案中给出的)应该总是首选的,因为类型参数是不必要的。 – newacct 2014-10-03 03:32:37

+0

@newacct它们接受相同类型的参数,但如果要引用泛型类型,则需要更详细的语法。由于在OP中,'test'返回'void'并且有一个空体,所以您可能会争辩说我们不希望引用该类型,但通常来说,冗长的语法也更加强大。 – 5gon12eder 2014-10-03 03:42:17