Java泛型语法的区别是什么?

3
这些方法定义有何不同?
public <T extends Foo> void loadData(Class<T> p_class);

public void loadData(Class<? extends Foo> p_class);

11
第二种形式中,在方法内部无法引用 T - Vyncent
我想不出只有一种方法可以让你做到实际的事情,而另一种方法会禁止你这样做。 - Sergey Kalinichenko
1
@Vyncent:在那个特定的方法内部,但它可以直接调用一个捕获助手,该助手可以在其中使用 T。对于外部代码来说,一个方法能做到的另一个方法也能做到。 - newacct
2个回答

3

这两个签名接受完全相同的参数集,因此在这个意义上是等效的。正如评论中指出的那样,在第二种情况下,您将无法在方法体中引用T。然而,根据Effective Java的说法,第二个签名更受欢迎(因为它更短且稍微更清晰)。在该书中,给出的建议是使用第二个签名,并在需要在方法中使用T时使用具有第一个签名的私有帮助器方法。像这样:

private <T extends Foo> void helper(Class<T> p_class) {
    // code
}

public void loadData(Class<? extends Foo> p_class) {
    helper(p_class);
}

所以,两者的功能相同,但第二个签名在函数体中可以做得更少。 因此需要使用第一个签名的第二种方法。然而,《Effective Java》指出第二种方法更短,更受欢迎?对我来说,感觉相反是真的,第一个签名只需要一个方法,显然更短,更不容易混淆。或者我有什么遗漏吗? - MetalSnake
1
@Diskutant对于调用该方法的人来说,这样做更不容易产生困惑,而对编写代码的人来说则不一定。如果有多个类型参数,则更清晰明了。我认为public <T, U, V extends Number> void foo(List<T> list, Map<U, V> map)public void foo(List<?> list, Map<?, ? extends Number> map)更加复杂。在第二种情况下,List可以是任何类型,Map中的值必须是Number的子类,这已经非常明显了。但在第一种情况下,有3个令人困惑的类型参数,你必须在两个地方看到extends Number是关于映射值的。 - Paul Boddington
1
@Diskutant,这也涉及到封装性。方法体中需要T的事实是调用者不关心的实现细节。调用者关心的只是如果他们传递一个扩展Foo的某种类型的Class对象,该方法是否有效。由于实现细节,他们不应该使用不同的签名。 - Paul Boddington

0

在语法使用上有所不同,因为第二种方法具有通用类型参数。

class Bar extends Foo { }

obj.<Bar>loadData(klazz);

这需要 klazz 是确切的 Class<Bar>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接