Java Generics - 为什么<object> = new <specialization>不允许?</specialization> </object>

时间:2013-06-30 18:58:20

标签: java generics

目前,我正在准备考试,我将在两天内写完。 在示例问题中,我发现了以下问题:

这会编译吗?

FavoritesList<Person> l = new FavoritesList<Contact>();

(联系人延伸人)

我会回答是,因为如果我有以下内容:

FavoritesList<Person> l = new FavoritesList<Person>();
l.add(new Contact());

没关系。为什么在第一个例子中不是很好?

请记住:联系人延伸人!

提前致谢 最好的问候

7 个答案:

答案 0 :(得分:2)

这是禁止的,但让我指出为什么

让我们考虑一下:

FavoritesList<Person> l = new FavoritesList<Contact>();

FavoritesList<Person>允许执行但FavoritesList<Contact>禁止的操作,即添加Person的任何子类,这些子类违反了FavoritesList<Contact>的合同。

您可能正在寻找的是:

 FavoritesList<? extends Person> wildcardedList = new FavoritesList<Contact>();

表示:这是一个未指定类型?的列表,此列表中的所有元素都属于此类型?,我们知道此类型?正在扩展人员。请注意,此类型通配符最初可能不直观。基本上他们给你的是这个列表的只读视图。

让我们假设:

 class FavoritesList<T>{

    void add(T t){...}

 }

基本上你不能打电话:

 wildcardedList.add(new Contact()); 

也不:

 wildcardedList.add(new Contact()); 

因为我们不知道PersonContact是否为未指定的类型T.

要做到这一点,你必须在add参数的类型中添加通配符,然后它就会变得混乱。

答案 1 :(得分:1)

这不好,因为Java中的泛型不支持继承,这意味着FavoritesList<Contact>不是FavoritesList<Person>的子类型 请参阅:http://docs.oracle.com/javase/tutorial/java/generics/inheritance.html

答案 2 :(得分:0)

不,它不会起作用。 Java不提供泛型的协方差属性,因此如果Contact扩展Person,这并不意味着FavoritesList<Contact>扩展FavoritesList<Person>

答案 3 :(得分:0)

想象一下,你有另一个Person的子类(例如Enemy,Enemy扩展Person)。

然后,如果第一个被允许,你可以这样做:

FavoritesList<Contact> contacts = // a list of some Contacts
FavoritesList<Person> l = contacts;

// at this point, contacts and l both reference the same object!

l.add(new Enemy()); // allowed by the compiler since enemy is a person

// now contacts contains an enemy which is bad since Enemy is not a Contact!
Contact c = contacts.get(contacts.size() - 1); // would throw a class cast exception if the compiler allowed the above

请注意,Java确实为通配符提供了解决方法,因此您可以这样做:

FavoritesList<Contact> contacts = // a list of some Contacts
FavoritesList<? extends Person> l = contacts;

Person person = l.get(0); // ok since we know everything in l is some class that extends Person
l.add(new Enemy()); // won't compile, since we can't verify that Enemy is the ?

答案 4 :(得分:0)

这不好,因为允许它允许您将其他类型的Person插入Contact列表中。虽然Contact s'的不可变列表确实是Person s的“不可变列表”,但Contact s的可变列表是{{1}的可变列表是不正确的。 s - 您可以将非Person插入Contact的可变列表中,但不能插入Person s的可变列表中。

因此,出于安全考虑,Java禁止您执行此类操作。你能做的就是这样:

Contact

这是有效的,因为您无法通过FavoritesList<? extends Person> l = new FavoritesList<Contact>();插入非Contact并打破您的列表。

其他语言(例如C#,Scala)允许您为类型参数提供协方差和逆变量注释,以便更好地控制在这种情况下允许的转换(例如,如果是,则可以将类型参数标记为协变允许FavoritesList<? extends Person>转换为Thing<Derived>),但Java不会。这是一个关于这个主题的C#参考,可能会让事情更清晰:

http://blogs.msdn.com/b/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx

答案 5 :(得分:0)

FavoritesList<Person> l = new FavoritesList<Contact>(); {}创建了FavoritesList的{​​{1}}。

但是通过引用Contact,您将能够添加任何类型的FavoritesList<Person> l类或扩展Person的类的实例,例如let Person到该列表。

你认为这样会安全吗?

答案 6 :(得分:0)

有一个原因是它不能编译,可以用数组来观察。

  Object[] objects = new String[10];
  objects[0] = new Object();

现在每条线都是有道理的。但是我们最终将一个Object插入一个String数组中,这当然是错误的并抛出异常。

随后添加的泛型和java开发人员决定解决此问题,List<String>List<Object>不兼容,因为两者对其内容有不同的约束,但是他们添加了一个额外的语法,所以{{1} 1}}与List<String>兼容(读取:可包含未知类型对象的列表)。

List<? extends Object>