问题描述
|
由于泛型不是协变的,我正在努力提出一个简单的API
这是我的新问题:我无法获得可以满足我需要的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一起使用(这意味着它们具有一个为child的Set)。他们需要将孩子作为父母暴露给外界。
尝试#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类之外的Set中添加和删除。这使它毫无用处。真正令我困惑的是,甚至没有添加父母也行得通。
尝试#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()
,但由于不可能进行抽象层设计,因此会迷失方向。此外,在您使用父级的任何地方传递泛型都很难看。
-
我不知道该怎么办。这样做比我想象的要复杂得多。我真的需要一些方法
解决方法
您提出的问题无法解决。
“ѭ6”是一个集合,您可以在其中添加任何“ѭ7”对象,而从其中获得的所有对象都是“ѭ7”对象。
“ 9”是一个集合,您可以在其中添加任何“ 10”对象,而从其中获得的所有对象都是“ 10”对象。
如我们所见,没有一个对象可以同时实现这两个接口:如果可以添加任何
Parent
,则不能确保仅从其中获得Child
个对象。这意味着您的ConcreteStore不能说“我只有孩子”,但可以允许其他人在其中放置父母。
只是为了避免这些错误而制作了Java泛型系统-无论编译器在哪里吠叫,您很可能在做错什么。
Set<? extends Parent>
是unknown7ѭ某些未知亚型的集合。这意味着我们不能在其中放任何东西(因为我们不知道正确的类型),而我们所能得到的只是7个对象。
Set<? super Child>
是unknown10ѭ一些未知超型的集合。这意味着我们可以在其中放入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();
}
但是现在不能有仅具有子级的商店实现-因为您需要能够添加父级。
您可以改为将“ 23”作为参数化类型:
interface Store<X extends Parent> {
public Set<X> getParents();
}
那你就会有
class ConcreteStore implements Store<Child> {
Set<Child> childs;
public Set<Child> getParents() {
return childs;
}
}
当然,这仍然不允许您将父母放进去,但是现在呼叫者可以看到它-并且可能有另一个实现ѭ26的实现,这将允许这样做。
, 我认为这是一个逻辑错误。父母永远是某人的孩子,但孩子并非永远是某人的父母。因此,从父级扩展子级没有任何意义。这很可能会触发对其他方法的需求,这些需求由于这种逻辑逆转而难以表达。
我建议您介绍常见的超类,例如,Human
,然后重新设计。我敢打赌,事情将会变得简单。
, 在尝试#2中,您无法向该集合添加任何内容。这样您就可以在add方法上获得28英镑的收益。
快速回答:您始终可以使用Parent [](数组),并且返回将是协变的。
, 这可能应该是评论,但我的代表人数太少了...
创建一个只能容纳Child的具体类有什么意义?
我会保留:
class ConcreteStore implements Store {
Set<Parent> parents;
public Set<Parent> getParents() {
return parents;
}
}