【发布时间】:2014-01-27 20:12:24
【问题描述】:
据我所知,使用上限泛型和使用超类作为方法参数都接受相同的可能参数。哪个是首选,两者之间有什么区别(如果有的话)?
上界泛型作为参数:
public <T extends Foo> void doSomething(T foo) {}
超类作为参数:
public void doSomething(Foo foo) {}
【问题讨论】:
据我所知,使用上限泛型和使用超类作为方法参数都接受相同的可能参数。哪个是首选,两者之间有什么区别(如果有的话)?
上界泛型作为参数:
public <T extends Foo> void doSomething(T foo) {}
超类作为参数:
public void doSomething(Foo foo) {}
【问题讨论】:
这是一个上限类型参数。下限是使用super 创建的,对于类型参数,您实际上无法做到这一点。 You can't have a lower bounded type parameter。
如果你想传递一个List<T>,那会有所不同。所以,对于以下两种方法:
public <T extends Foo> void doSomething(List<T> foos) {}
public void doSomething(List<Foo> foo) {}
对于给定的类:
class Bar extends Foo { }
以下方法调用:
List<Bar> list = new ArrayList<Bar>();
doSomething(list);
对第一种方法有效,但对第二种方法无效。第二种方法失败,因为List<Foo> 不是List<Bar> 的超类型,尽管Foo 是Bar 的超类型。但是,第一种方法通过了,因为那里的类型参数T 将被推断为Bar。
【讨论】:
Foo 类型或其子类型的单个参数时,您不需要泛型方法。您可以将Foo 的子类的实例传递给Foo 的参数类型。
Foo 是T 的超类型。而在您的情况下,List<Foo> 不是List<T> 的超类型,所以这就是它不同的原因。在您的情况下,List<T> 的超类型将改为 List<? extends Foo>。
一般来说,只有在类/方法/字段声明中的多个位置使用类型变量时,您才需要它。当您在方法(而不是类)上声明一个时,唯一可以使用它的地方是该方法的参数和返回值。
例如,您可以在多个参数上使用它以确保它们的类型匹配:
public static <T> void addToList(List<T> list, T element) {
list.add(element);
}
这是一个简单的例子,但你可以看到它阻止你给它一个与列表的泛型类型不匹配的元素:
List<Integer> list = new ArrayList<>();
addToList(list, 7);
//addToList(list, 0.7); // doesn't compile
//addToList(list, "a"); // doesn't compile
你也可以将参数和返回类型声明为同一类型:
public static <T> T nullCheck(T value, T defValue) {
return value != null ? value : defValue;
}
由于此方法返回给定的两个T 对象之一,我们可以肯定地说返回的对象也是T 类型。
Integer iN = null;
Integer i = nullCheck(iN, 7);
System.out.println(i); // "7"
Double dN = null;
Double d = nullCheck(dN, 0.7);
System.out.println(d); // "0.7"
Number n = nullCheck(i, d); // T = superclass of Integer and Double
System.out.println(n); // "7"
对于问题中的示例,类型变量只使用一次,因此相当于使用超类。在这种情况下,您应该避免声明类型变量,这只是不必要的混乱。
另外我应该注意,另一个答案将示例更改为使用List<T> 和List<Foo>,但正如 cmets 中所述,超类实际上是List<? extends Foo>,因此那里也不需要类型变量。
【讨论】: