为什么java泛型如此棘手?我以为我终于理解了,但是eclipse在下面的somOtherMethod中使用下面的getOuterList方法给出了一个错误。
protected List<?> getOuterList() {
// blah blah
}
protected List<? extends Object> getOuterList() {
// blah blah
}
protected void someOtherMethod() {
...
getOuterList().add((MyObject)myObject); //compile error
...
}
更新:好的 - 所以我现在理解错误。我对List<?>
或List<? extends SomeObject>
的真正含义缺乏了解。在前一种情况下,我认为这意味着可以包含任何内容的列表。在后一种情况下,我假设它是一组扩展SomeObject的对象的列表。正确表达我的理解只是List<Object>
和List<SomeObject>
(没有扩展)。我认为extend帮助我解决了他们没有解决的问题。所以这就是我真正的问题所在:
public interface DogKennel {
public List<Dog> getDogs();
}
public class GreyHoundKennel implements DogKennel {
protected List<GreyHound> greyHounds;
public List<GreyHound> getGreyHounds() {
return this.greyHounds;
}
public List<Dog> getDogs() {
// Is there no way to handle this with generics
// w/out creating a new List?
return getGreyHounds(); //compiler error
}
}
答案 0 :(得分:3)
你是说该方法返回一个“List
某种未知类型”(你无法添加,因为你无法保证你要添加的东西是该类型的子类型) 。你真的想说,“你想要的任何类型的List
”,所以你必须使方法通用:
protected <T> List<T> getOuterList() {
// blah blah
}
好的,我刚看了你的更新:
这完全取决于您打算如何处理getDogs()
的结果。如果您不打算将任何项添加到列表中,那么getDogs()
应该返回类型List<? extends Dog>
,然后问题就会得到解决。
如果您打算能够向其中添加内容,并且类型为List<Dog>
,则表示您可以向其添加任何类型的Dog
,然后逻辑上此列表不能是相同的列表作为greyHounds
,因为greyHounds
的类型为List<GreyHound>
,因此Dog
对象不应包含在内。
这意味着您必须创建一个新列表。请记住,对新列表的任何更改都不会反映在原始列表greyHouds
中。
答案 1 :(得分:3)
此声明:
List<?> getOuterList() { }
告诉编译器“我真的不知道我要回去的列表是什么样的”。然后你基本上执行
list<dunno-what-this-is>.add((MyObject)myObject)
它不能将MyObject添加到它不知道它是什么类型的东西的列表中。
此声明:
protected List<? extends Object> getOuterList() { ... }
告诉编译器“这是Object的子类型的列表”。所以再次,当然你不能转换为“MyObject”然后添加到对象列表。因为所有编译器都知道列表可以包含对象。
你可以然而,做这样的事情:
List<? super MyObject>.getOuterList() { ... }
然后成功添加MyObject。那是因为现在编译器知道List是MyObject的列表,或MyObject的任何超类型,所以它肯定可以接受MyObject。
修改:至于您的DogKennel示例,我认为这段代码符合您的要求:
protected List<GreyHound> greyHounds;
// We only want a List of GreyHounds here:
public List<GreyHound> getGreyHounds() {
return this.greyHounds;
}
// The list returned can be a List of any type of Dog:
public List<? extends Dog> getDogs() {
return getGreyHounds();
}
答案 2 :(得分:1)
你正在嘲笑Java泛型不是类型参数的多态。
通过代码片段,让我们分开示例:
protected List<GreyHound> greyHounds; // List<GreyHound> is fine
/** This method returns a lovely List of GreyHounds */
public List<GreyHound> getGreyHounds() {
return this.greyHounds;
}
/** Here is the problem. A List<GreyHound> is not a List<Dog> */
public List<Dog> getDogs() {
return getGreyHounds(); //compiler error
}
所以你的原始评论是正确的。两个列表肯定是不同的,它们之间没有继承。所以,我建议你研究这两个选项:
尝试按照您在评论中的建议返回新列表。例如,return new ArrayList<Dog>(this.greyHounds);
你真的需要保留特定犬种的名单吗?也许您应该将数据成员定义为List<Dog>
,您可以将其添加到特定的GreyHounds中。即,protected List<Dog> greyHoundsOnly;
您可以通过对象的外部界面管理狗窝中允许的狗。
除非你有充分的理由保留特定类型的列表,否则我会认真考虑选项2。
编辑:充实我上面建议的选项:
选项1:返回新列表。优点:简单,直接,你得到一个打字列表,它消除了一个线程安全问题(不暴露对世界的内部引用)。缺点:看似性能成本。
// Original code starts here.
public interface DogKennel {
public List<Dog> getDogs();
}
public class GreyHoundKennel implements DogKennel {
protected List<GreyHound> greyHounds;
public List<GreyHound> getGreyHounds() {
return this.greyHounds;
}
// Original code ends here
public List<Dog> getDogs() {
// This line eliminates the thread safety issue in returning
// an internal reference. It does use additional memory + cost
// CPU time required to copy the elements. Unless this list is
// very large, it will be hard to notice this cost.
return new ArrayList<Dog>(this.greyHounds);
}
}
选项2:使用其他数据表示。优点:使用多态性更好,返回原始目标的通用列表。缺点:这是一个略有不同的架构,可能不符合原始任务。
public abstract class DogKennel {
protected List<Dog> dogs = new ArrayList<Dog>();
}
public class GreyHoundKennel extends DogKennel {
// Force an interface that only allows what I want to allow
public void addDog(GreyHound greyHound) { dogs.add(greyHound); }
public List<Dog> getDogs() {
// Greatly reduces risk of side-effecting and thread safety issues
// Plus, you get the generic list that you were hoping for
return Collections.unmodifiableList(this.dogs);
}
}
答案 3 :(得分:0)
一般类型?意思是“某些特定的类型,但我不知道哪个”。什么用?基本上是只读的,因为你不知道实际的类型就不能写它。
答案 4 :(得分:0)
已经有一个已接受的答案,但请考虑以下代码修改。
public interface DogKernel {
public List<? extends Dog> getDogs();
}
public class GreyHoundKennel implements DogKernel {
protected List<GreyHound> greyHounds;
public List<GreyHound> getGreyHounds() {
return this.greyHounds;
}
public List<? extends Dog> getDogs() {
return getGreyHounds(); // no compilation error
}
public static void main(String[] args) {
GreyHoundKennel inst = new GreyHoundKennel();
List<? extends Dog> dogs = inst.getDogs();
}
}
Java泛型确实被破坏了,但并没有那么破碎。 BTW Scala通过提供差异处理以非常优雅的方式解决了这个问题。
更新----------
请考虑更新的代码段。
public interface DogKennel<T extends Dog> {
public List<T> getDogs();
}
public class GreyHoundKennel implements DogKennel<GreyHound> {
private List<GreyHound> greyHounds;
public List<GreyHound> getDogs() {
return greyHounds; // no compilation error
}
public static void main(String[] args) {
GreyHoundKennel inst = new GreyHoundKennel();
inst.getDogs().add(new GreyHound()); // no compilation error
}
}