努力想出具有接口和通用集的API

时间:2011-06-12 17:00:02

标签: java generics

我很难想出一个简单的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(),但是由于一层抽象是不可能的并且会丢失。此外,在你使用父母的地方传递泛型看起来很难看。

-

我不知道该怎么做。这样做比我想象的要复杂得多。我真的需要一些方法来获得

4 个答案:

答案 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,这是所有内容的超类型。)

    < / LI>

回到你的问题:

为您的运营

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;
    }
}