简体   繁体   English

为什么List <Number>不是List <Object>的子类型?

[英]Why is List<Number> not a sub-type of List<Object>?

public void wahey(List<Object> list) {}

wahey(new LinkedList<Number>());

The call to the method will not type-check. 对方法的调用不会进行类型检查。 I can't even cast the parameter as follows: 我甚至无法将参数强制转换如下:

wahey((List<Object>) new LinkedList<Number>());

From my research, I have gathered that the reason for not allowing this is type-safety. 根据我的研究,我收集到不允许这样做的原因是类型安全。 If we were allowed to do the above, then we could have the following: 如果允许我们执行上述操作,那么我们可以执行以下操作:

List<Double> ld;
wahey(ld);

Inside the method wahey, we could add some Strings to the input list (as the parameter maintains a List<Object> reference). 在方法wahey中,我们可以在输入列表中添加一些字符串(因为参数维护List<Object>引用)。 Now, after the method call, ld refers to a list with a type List<Double> , but the actual list contains some String objects! 现在,在方法调用之后,ld引用一个List<Double>类型的List<Double> ,但实际列表包含一些String对象!

This seems different to the normal way Java works without generics. 这似乎与没有泛型的Java正常工作方式不同。 For instance: 例如:

Object o;
Double d;
String s;

o = s;
d = (Double) o;

What we are doing here is essentially the same thing, except this will pass compile-time checks and only fail at run-time. 我们在这里做的基本上是相同的,除了这将通过编译时检查,并且只在运行时失败。 The version with Lists won't compile. 带有列表的版本将无法编译。

This leads me to believe this is purely a design decision with regards to the type restrictions on generics. 这让我相信这纯粹是关于泛型类型限制的设计决策。 I was hoping to get some comments on this decision? 我希望对这个决定有所评论?

What you are doing in the "without generics" example is a cast, which makes it clear that you are doing something type-unsafe. 你在“没有泛型”的例子中所做的是一个演员,它清楚表明你正在做一些类型不安全的事情。 The equivalent with generics would be: 与泛型相当的是:

Object o;
List<Double> d;
String s;

o = s;
d.add((Double) o);

Which behaves the same way (compiles, but fails at runtime). 其行为方式相同(编译,但在运行时失败)。 The reason for not allowing the behavior you're asking about is because it would allow implicit type-unsafe actions, which are much harder to notice in code. 不允许您询问的行为的原因是因为它允许隐式类型不安全的操作,这在代码中更难注意到。 For example: 例如:

public void Foo(List<Object> list, Object obj) {
  list.add(obj);
}

This looks perfectly fine and type-safe until you call it like this: 这看起来非常好并且类型安全,直到你这样称呼:

List<Double> list_d;
String s;

Foo(list_d, s);

Which also looks type-safe, because you as the caller don't necessarily know what Foo is going to do with its parameters. 这也看起来类型安全,因为你作为调用者并不一定知道Foo将如何处理它的参数。

So in that case you have two seemingly type-safe bits of code, which together end up being type-unsafe. 因此,在这种情况下,您有两个看似类型安全的代码,这些代码最终都是类型不安全的。 That's bad, because it's hidden and therefore hard to avoid and harder to debug. 这很糟糕,因为它是隐藏的,因此难以避免并且难以调试。

Consider if it was... 考虑一下......

List<Integer> nums = new ArrayList<Integer>();
List<Object> objs = nums
objs.add("Oh no!");
int x = nums.get(0); //throws ClassCastException

You would be able to add anything of the parent type to the list, which may not be what it was formerly declared as, which as the above example demonstrates, causes all sorts of problems. 您可以将任何父类型的内容添加到列表中,这可能不是以前声明的内容,如上例所示,会导致各种问题。 Thus, it is not allowed. 因此,不允许这样做。

They aren't subtypes of each other due how generics work. 由于泛型如何工作,它们不是彼此的子类型。 What you want is to declare your function like this: 你想要的是声明你的功能:

public void wahey(List<?> list) {}

Then it will accept a List of anything that extends Object. 然后它将接受任何扩展Object的List。 You can also do: 你也可以这样做:

public void wahey(List<? extends Number> list) {}

This will let you take in Lists of something that's a subclass of Number. 这将允许您列出Number的子类的列表。

I'd recommend you pick up a copy of "Java Generics and Collections" by Maurice Naftalin & Philip Wadler. 我建议你拿一份Maurice Naftalin和Philip Wadler的“Java Generics and Collections”。

There are essentially two dimensions of abstraction here, the list abstraction and the abstraction of its contents. 这里基本上有两个抽象维度,列表抽象和内容的抽象。 It's perfectly fine to vary along the list abstraction - to say, for instance, that it's a LinkedList or an ArrayList - but it's not fine to further restrict the contents, to say: This (list which holds objects) is a (linked list which holds only numbers). 沿着列表抽象变化是完全正确的 - 例如,它是一个LinkedList或一个ArrayList - 但是进一步限制内容并不好说:这个(保存对象的列表)是一个(链表,只持有数字)。 Because any reference that knows it as a (list which holds objects) understands, by the contract of its type, that it can hold any object. 因为任何知道它的引用(保存对象的列表)通过其类型的契约理解它可以容纳任何对象。

This is quite different from what you have done in the non-generics example code, where you've said: treat this String as if it were a Double. 这与您在非泛型示例代码中所做的完全不同,您已经说过:将此String视为Double。 You are instead trying to say: treat this (list which holds only numbers) as a (list which holds anything). 你试图说:把这个(仅包含数字的列表)视为(包含任何东西的列表)。 And it doesn't, and the compiler can detect it, so it doesn't let you get away with it. 它没有,编译器可以检测到它,所以它不会让你逃脱它。

"What we are doing here is essentially the same thing, except this will pass compile-time checks and only fail at run-time. The version with Lists won't compile." “我们在这里做的基本上是相同的,除了这将通过编译时检查,并且只在运行时失败。带有列表的版本将无法编译。”

What you're observing makes perfect sense when you consider that the main purpose of Java generics is to get type incompatibilities to fail at compile time instead of run time. 当您认为Java泛型的主要目的是在编译时而不是运行时使类型不兼容失败时,您所观察到的内容非常有意义。

From java.sun.com 来自java.sun.com

Generics provides a way for you to communicate the type of a collection to the compiler, so that it can be checked. 泛型提供了一种将集合类型传递给编译器的方法,以便可以检查它。 Once the compiler knows the element type of the collection, the compiler can check that you have used the collection consistently and can insert the correct casts on values being taken out of the collection. 一旦编译器知道集合的元素类型,编译器就可以检查您是否一直使用了集合,并且可以在从集合中取出的值上插入正确的强制转换。

In Java, List<S> is not a subtype of List<T> when S is a subtype of T . 在Java中,当ST的子类型时, List<S> 不是 List<T>的子类型。 This rule provides type safety. 此规则提供类型安全性。

Let's say we allow a List<String> to be a subtype of List<Object> . 假设我们允许List<String>List<Object>的子类型。 Consider the following example: 请考虑以下示例:

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);

So, forcing this provides type safety but it also has a drawback, it's less flexible. 因此,强制这样可以提供类型安全,但它也有一个缺点,它的灵活性较差。 Consider the following example: 请考虑以下示例:

class MyCollection<T> {
    public void addAll(Collection<T> otherCollection) {
        ...
    }
}

Here you have a collection of T 's, you want to add all items from another collection. 在这里你有一个T的集合,你想要添加另一个集合中的所有项目。 You can't call this method with a Collection<S> for an S subtype of T . 不能调用此方法用Collection<S>用于S的亚型T Ideally, this is ok because you are only adding elements into your collection, you are not modifying the parameter collection. 理想情况下,这是可以的,因为您只是在集合中添加元素,而不是修改参数集合。

To fix this, Java provides what they call "wildcards". 为了解决这个问题,Java提供了他们所谓的“通配符”。 Wildcards are a way of providing covariance/contravariance. 通配符是一种提供协方差/逆变的方法。 Now consider the following using wildcards: 现在考虑以下使用通配符:

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)
     }
} 

Now, with wildcards we allow covariance in the type T and we block operations that are not type safe (for example adding an item into the collection). 现在,通过通配符,我们允许类型T中的协方差,并且我们阻止非类型安全的操作(例如将项添加到集合中)。 This way we get flexibility and type safety. 这样我们就可以获得灵活性和类型安全性

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 类型擦除和继承:List的对象 <Number> ,当从子类访问时被视为原始列表? - Type erasure and inheritance: An object of List<Number> , when accessed from a sub-class is seen as a raw List? 将子类型转换为超类型 - Convert sub-type to super-type Hibernate Fetch不再是Join的子类型 - Hibernate Fetch is not longer sub-type of Join 给定一个对象和一个类 <?> ,我可以判断该对象是否属于该类的子类型吗? (GWT,客户端) - Given an object and a Class<?>, can I tell whether the object is of sub-type of that class? (GWT, client-side) 尝试使用泛型对类进行子类型化 - Trying to sub-type classes using generics 为什么我不能使用引用子类型实例的父类型的引用来调用子类方法? - why can't I call a subclass method using a reference of a parent type that refers to an instance of a sub-type? 通过在子类型上返回迭代器来实现Iterable - Implementing Iterable by returning an Iterator over a sub-type Jackson反序列化子类型将枚举字段设置为null - Jackson deserialise sub-type sets enum field to null 是否同时抛出主要异常和子类型,是否有适当的方法? - Throwing both main exception and sub-type, is there a proper way? 用Sub对象的列表更新Master对象的列表 - Update list of Master object with list of Sub object
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM