我很难想出一个简单的API due to generics not being covariant
这是我的新问题:我无法获得Set<>这就是我需要的。我曾尝试阅读各种指南,但他们都使用了许多流行语,让我迷失方向。
考虑以下课程
public class Parent {}
public class Child extends Parent {}
interface Store {
public Set<Parent> getParents(); //PROBLEM!! This needs to change
}
至少,我需要这些操作才能工作
Set<Parent> parents = store.getParents();
parents.add(new Parent());
parents.add(new Child());
store.getParents().add(new Child()); //Note the lack of generics
for(Parent curEntry : store.getParents()) {
}
实现Store的类需要能够与Child一起工作(意味着他们有一个子集)。他们需要将孩子们作为父母暴露给外界。
尝试#1
interface Store {
public Set<Parent> getParents();
}
class ConcreteStore implements Store {
Set<Child> childs;
public Set<Parent> getParents() {
return (Set<? extends Parent>)childs; //ERROR: inconvertible types
}
}
尝试#2
interface Store {
public Set<? extends Parent> getParents();
}
class ConcreteStore implements Store {
Set<Child> childs;
public Set<Child> getParents() {
return childs;
}
}
Store store = new ConcreteStore();
Set<? extends Parent> parents = store.getParents();
parents.add(new Child()); //ERROR: cannot find symbol
parents.add(new Parent()); //ERROR: cannot find symbol. What?!
for (Parent curEntry : store.getParents()) {
}
该版本虽然我非常喜欢,但意味着无法在Concrete类之外添加和删除。这使得它毫无用处。让我感到困惑的是,即使添加父母也不会有效。
尝试#3
interface Store<T extends Parent> {
public Set<T> getParents();
}
public static class ConcreteStore implements Store<Child> {
Set<Child> childs;
@Override
public Set<Child> getParents() {
return childs;
}
}
Store store = new ConcreteStore();
Set<Parent> parents = store.getParents();
parents.add(new Parent());
parents.add(new Child());
for (Parent curEntry : store.getParents()) { //ERROR: incompatible types. Found object, requited Parent
}
请注意,我知道我可以Store<Child> store = new DatabaseStore()
,但是由于一层抽象是不可能的并且会丢失。此外,在你使用父母的地方传递泛型看起来很难看。
-
我不知道该怎么做。这样做比我想象的要复杂得多。我真的需要一些方法来获得
答案 0 :(得分:4)
您提出的表单中的问题无法解决。
Set<Parent>
是一个集合,您可以在其中添加任何Parent
对象,并且您从中获取的所有对象都是Parent
个对象。Set<Child>
是一个集合,您可以在其中添加任何Child
对象,并且您从中获取的所有对象都是Child
个对象。正如我们所看到的,不可能有一个实现这两个接口的对象:如果你可以添加任何Parent
,你不能确定只从中获取Child
个对象。这意味着您的ConcreteStore不能说“我只有孩子”,但允许其他人将父母放入其中。
Java泛型系统只是为了避免这些错误 - 在编译器咆哮的地方,你最有可能做错了。
Set<? extends Parent>
是Parent
某个未知子类型的集合。这意味着我们不能在其中添加任何内容(因为我们不知道正确的类型),我们可以从中获取Parent
个对象。
Set<? super Child>
是Child
的某种未知超类型的集合。这意味着我们可以将Child
放入其中,但我们无法确定我们从中得到了什么(除了Object
,这是所有内容的超类型。)
回到你的问题:
为您的运营
Set<Parent> parents = store.getParents();
parents.add(new Parent());
parents.add(new Child());
store.getParents().add(new Child()); //Note the lack of generics
for(Parent curEntry : store.getParents()) {
}
工作,你不需要比你已发布的更多的东西:
public class Parent {}
public class Child extends Parent {}
interface Store {
public Set<Parent> getParents();
}
但现在不能是只有Childs的商店实现 - 因为您需要能够添加Parent。
您可以改为Store
参数化类型:
interface Store<X extends Parent> {
public Set<X> getParents();
}
然后你会
class ConcreteStore implements Store<Child> {
Set<Child> childs;
public Set<Child> getParents() {
return childs;
}
}
当然,这仍然不允许你把父母放进去,但是现在调用者可以看到这个 - 并且可能有另一个实现Store<Parent>
的实现,这将允许它。
答案 1 :(得分:1)
我认为这里存在逻辑错误。父母总是某人的孩子,但孩子并不总是某人的父母。因此,从Parent扩展Child是没有意义的。这可能会触发由于这种逻辑逆转而难以表示的其他方法的要求。
我建议您引入常见的超类,例如Human
,然后重新访问您的设计。我打赌事情会变得更简单。
答案 2 :(得分:0)
在尝试#2中,您无法向该集合添加任何内容。这就是你在add方法上获得cannot find symbol
的方式。
作为一个快速的答案:你总是可以使用Parent [](数组),返回将是协变的。
答案 3 :(得分:0)
这应该是评论,但我的代表太低了......
创建一个只能容纳Child的具体类有什么意义?
我会保留:
class ConcreteStore implements Store {
Set<Parent> parents;
public Set<Parent> getParents() {
return parents;
}
}