简体   繁体   English

Java中的泛型

[英]Generics in Java

I came across something peculiar today. 我今天遇到了一些奇怪的事情。 Take a look at this code snippet: 看一下这段代码:

List <Rectangle> test1 = new LinkedList<Rectangle>();
List <Shape> test2 = test1; //Compiler Error;

This is of course is assuming that class Rectangle is a subclass of Shape. 这当然是假设Rectangle类是Shape的子类。 Can someone explain to me why this is an error? 有人可以向我解释为什么这是一个错误?

You need to use wild card. 你需要使用外卡。 Like this: 像这样:

List<Rectangle> test1 = new LinkedList<Rectangle>();
List<? extends Shape> test2 = test1;

As Rectangle extends Shape . 随着Rectangle扩展Shape

The reason for this is that if test2 is just List<Shape> , you will expect test2.add(..) to accept any shape but that is not the case if you allows test1 (which is List<Rectangle> ), test1 will not accept any shape that is not Rectangle . 原因是如果test2只是List<Shape> ,你会期望test2.add(..)接受任何形状,但如果你允许test1(它是List<Rectangle> )则不是这样,test1将不接受任何不是Rectangle形状。

Hope this helps. 希望这可以帮助。

If this code worked, you could continue by inserting into test2 a Circle -- thus utterly breaking the guarantee that test1 makes, that only Rectangle s will ever be inserted in it. 如果这段代码有效,你可以继续向test2插入一个Circle这样就完全打破了test1所做的保证,只会将Rectangle插入其中。

The general principle (language independent -- a matter of logic -- even though counter-intuitive): a bag of bananas is NOT a bag of fruit... in a world of mutable objects (the functional programming world, where every object is immutable once created, is MUCH simpler!). 一般原则(语言无关 - 逻辑问题 - 尽管反直觉):一袋香蕉不是一袋水果......在一个可变对象的世界里(功能编程世界,每个对象都是一旦创建不可变, 就更简单了!)。 That's because you can add an apple to a bag of fruit (since an apple is a piece of fruit), but you can't add an apple to a bag of bananas (since an apple is not a banana). 那是因为你可以在一袋水果中添加一个苹果(因为苹果是一块水果),但你不能在一袋香蕉上加一个苹果(因为苹果不是香蕉)。

BTW, this is very similar to the reason a square is not a rectangle (again, in a world of mutable objects): because given a (mutable) rectangle you can mutate the two sides independently, but, given a square, you can't. 顺便说一句,这与正方形不是一个矩形的原因非常相似(同样,在一个可变对象的世界中):因为给定一个(可变的)矩形,你可以独立地改变两边,但是,给定一个正方形,你可以'吨。 (In geometry, a square IS indeed a rectangle - but that's because, in geometry like in functional programming, there is no concept of "mutating" an object!-). (在几何学中,正方形确实是一个矩形 - 但这是因为,在函数式编程中的几何体中,没有“改变”对象的概念! - )。

This is a common source of confusion - generics are not covariant in Java. 这是混淆的常见原因 - 泛型在Java中不协变。

For a really good explanation of this please see Java theory and practice: Generics gotchas : 有关这方面的一个非常好的解释,请参阅Java理论和实践:泛型陷阱

While you might find it helpful to think of collections as being an abstraction of arrays, they have some special properties that collections do not. 虽然您可能会发现将集合视为数组的抽象是有帮助的,但它们具有集合不具有的一些特殊属性。 Arrays in the Java language are covariant -- which means that if Integer extends Number (which it does), then not only is an Integer also a Number, but an Integer[] is also a Number[], and you are free to pass or assign an Integer[] where a Number[] is called for. Java语言中的数组是协变的 - 这意味着如果Integer扩展Number(它确实如此),那么不仅Integer也是Number,而Integer []也是Number [],你可以自由地传递或者指定一个Integer [],其中需要一个Number []。 (More formally, if Number is a supertype of Integer, then Number[] is a supertype of Integer[].) You might think the same is true of generic types as well -- that List is a supertype of List, and that you can pass a List where a List is expected. (更正式地说,如果Number是Integer的超类型,则Number []是Integer []的超类型。)您可能认为泛型类型也是如此 - List是List的超类型,而您可以传递List所在的List。 Unfortunately, it doesn't work that way. 不幸的是,它没有那种方式。

List< Shape > has nothing to do with List< Rectangle >. List < Shape >与List < Rectangle >无关。 When you are using an array, Shape[] is actually supertype of Rectangle[], because arrays are covariant. 当你使用数组时,Shape []实际上是Rectangle []的超类型,因为数组是协变的。 But Collections such as List are not covariant. 但是像List这样的集合不是协变的。 In fact at run time, they will both be erased as List< Object >, and when you call any methods from any instances of List< Shape >, List< Rectangle >, the VM will call the methods of List< Object > but with some other code inserted to check the type safety. 事实上,在运行时,它们都将被清除为List < Object >,当您从List < Shape >,List < Rectangle >的任何实例调用任何方法时,VM将调用List < Object >的方法,但是插入一些其他代码以检查类型安全性。

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM