Java中String
的子类型不是Object
吗?
那么,为什么我不能将List<String>
类型的对象传递给接受List<Object>
作为参数的函数?但是,我可以将这样的对象传递给接受List
作为参数的函数。
答案 0 :(得分:10)
String不是String中Object的子类型吗?
是的,但这不会使List<String>
成为List<Object>
的子类型。
考虑这个例子:
List<String> l1 = new ArrayList<String>();
List<Object> l2 = l1; // This is a compilation error in real Java
l2.add(new Integer(42));
String oops = l1.get(0);
如果(假设)List<String>
是List<Object>
的子类型,那么您最终会在最后一个语句中为Integer
变量分配String
。简而言之,我们会破坏静态类型的安全性。
但是,我可以将这样的对象传递给接受List作为参数的函数。
是的,这是事实。但是,您正在处理原始类型,并且在将对象从列表中拉出时必须使用显式类型转换。例如,上述内容需要重写如下:
List<String> l1 = new ArrayList<String>();
List l2 = l1;
l2.add(new Integer(42));
String oops = (String) l1.get(0);
请注意,类型转换意味着我们必须用运行时类型安全替换静态类型安全性。 (当然,在这种情况下,类型转换在运行时会失败,因为实例的类型错误。)
答案 1 :(得分:3)
鉴于List<T>
,T
是参数化类型。
List<String>
与List<Object>
的类型无关,不是List<String>
与List<Number>
相关。 type
为List<String>
,整个签名为type
。但就像一切一样,也有例外;这些被称为Wildcard Parameterized Types
。
List<?>
就是所谓的Unbounded Wildcard Parameterized Type,它的工作方式与原始类型非常相似,它们在语义上是相同的,但会导致更多关于不安全转换的编译器警告。
List<? extends Object>
是一个有界的通配符参数化类型,它将是一个允许你放置extends Object
但是因为Java extends Object
中的每个类都没有语义任何类型的类型。不同于前两个选项。
现在它们的功能都相同,但它们会失败instanceof
测试,它们不相同的类型。
换句话说:
public void myfunction(final List<Object> list) {}
只接受type
的{{1}},List<Object>
与List
与type
或List<Object>
无关,即使他们所有语义功能相同。
List<?>
是type
或List
或List<Object>
,List<String>
中的继承链在{{1}的类型时未被考虑关注,直到你进入Wild Card Parameterized Types
以下是示例:
<>
您不能实例化List<T>
只为其分配具体实现。
Collection<?> coll = new ArrayList<String>();
List<? extends Number> list = new ArrayList<Long>();
是整个类型。
Java中的泛型与C ++中的实现不同,除了令人困惑的名称。
答案 2 :(得分:2)
在Java中,当List<S>
是List<T>
的子类型时,S
不是 T
的子类型。此规则提供类型安全性。
假设我们允许List<String>
成为List<Object>
的子类型。请考虑以下示例:
public void foo(List<Object> objects) {
objects.add(new Integer(42));
}
List<String> strings = new ArrayList<String>();
strings.add("my string");
foo(strings); // this is not allow in java
// now strings has a string and an integer!
// what would happen if we do the following...??
String myString = strings.get(1);
因此,强制这样可以提供类型安全,但它也有一个缺点,它的灵活性较低。请考虑以下示例:
class MyCollection<T> {
public void addAll(Collection<T> otherCollection) {
...
}
}
这里有一个T
的集合,你想要添加另一个集合中的所有项目。对于Collection<S>
S
T
子类型,您无法使用class MyCollection<T> {
// Now we allow all types S that are a subtype of T
public void addAll(Collection<? extends T> otherCollection) {
...
otherCollection.add(new S()); // ERROR! not allowed (Here S is a subtype of T)
}
}
来调用此方法。理想情况下,这是可以的,因为您只是在集合中添加元素,而不是修改参数集合。
为了解决这个问题,Java提供了他们所谓的“通配符”。通配符是一种提供协方差/逆变的方法。现在考虑以下使用通配符:
{{1}}
现在,通过通配符,我们允许类型T中的协方差,并且我们阻止非类型安全的操作(例如将项添加到集合中)。这样我们就可以获得灵活性和类型安全性。
答案 3 :(得分:0)
String
是Object
的子类型,是的,但这并不意味着List是List的子类型。这就是Java的工作方式。您可以阅读更多here。
答案 4 :(得分:0)
除了上面提到的所有内容,为了允许你想要的东西,你需要显式地将参数声明为List<? extends T>
(这意味着,接受任何T的子类,包括T),在这种情况下,{{1}应该工作,但List<? extends Object>
仅限于对象模板列表