Java泛型 - 从方法返回声明类型的子类型

时间:2008-10-21 21:52:00

标签: java generics

我的类正在实现一个返回List<JComponent>的超类方法。返回的列表是只读的:

public abstract class SuperClass {
    public abstract List<JComponent> getComponents();
}

在我的班级中,我想返回一个声明为List的字段 - 即子列表:

public class SubClass extends SuperClass {
    private List<JButton> buttons;
    public List<JComponent> getComponents() {
        return buttons;
    }
}

这会生成编译器错误,因为List<JButton>不是List<JComponent>的子类型。

我可以理解它为什么不编译,因为不应该允许将JTextField添加到JButtons列表中。

但是,由于列表是只读的,因此应该允许“概念上”。但是,当然,编译器不知道它是只读的。

有没有办法实现我想要实现的目标,而不更改超类中的方法声明,以及子类中的字段声明?

谢谢, Calum

8 个答案:

答案 0 :(得分:9)

getComponents()声明为:

public List<? extends JComponent> getComponents()

答案 1 :(得分:6)

我想我找到了我正在寻找的答案......

return Collections.<JComponent>unmodifiableList(buttons);

我之前尝试过:

return Collections.unmodifiableList(buttons);

但这是List<JButton>的类型推断。

使用显式类型参数可以将List<JButton>视为List<JComponent>unmodifiableList()允许这样做。

我现在很开心;-)而且由于讨论,我学到了很多东西。

Calum

答案 2 :(得分:4)

我不认为你可以做你想做的事情而不改变超类方法签名或子类列表声明。超类将返回类型严格定义为JComponent。除了JComponent之外,没有办法让你的返回类型。

如果它是只读的,我会做类似的事情:

public class SubClass extends SuperClass {
    private List<JComponent> buttons = new ArrayList<JComponent>();
    public void addButton(JButton button) {
        buttons.add(button);
    }
    public List<JComponent> getComponents() {
        return Collections.unmodifiableList(buttons);
    }
}

如果你可以修改超类,你可以试试类似的东西:

public abstract class SuperClass<E extends JComponent> {
    public abstract List<E> getComponents();
}

public class SubClass extends SuperClass<JButton> {
    private List<JButton> buttons = new ArrayList<JButton>();
    public List<JButton> getComponents() {
        return Collections.unmodifiableList(buttons);
    }
}

答案 3 :(得分:1)

使用通配符返回泛型类型并不是一个好主意,因为它会强制客户端考虑返回的内容...它还会使客户端代码更加麻烦且难以阅读。

答案 4 :(得分:0)

要做到这一点,你需要拓宽泛型。 :)

public abstract class SuperClass {
    public abstract List<? extends JComponent> getComponents();
}


public class SubClass extends SuperClass {
    private List<JButton> buttons;
    public List<? extends JComponent> getComponents() {
        return buttons;
    }
}

答案 5 :(得分:0)

嗯,不确定

设置它的通常方法是让超级看起来像

public abstract class SuperClass {
    public abstract List<? extends JComponent> getComponents();
}

不确定怎么做而不改变它。

答案 6 :(得分:0)

泛型的全部意义在于提供编译时类型安全性,例如集合的“类型”。不幸的是,List<JButton>不是List<JComponent>的子类型。原因很简单,如下:

List<JComponent> jc;
List<JButton> jb = new ArrayList<JButton>();
//if List<JButton> was a sublcass of List<JComponent> then this is a valid assignment
jc = jb;
jc.add(new JCheckBox()); //valid, as jc accepts any JComponent

JButton b = jb.get(0); //this will throw a ClassCastException, rendering Generic type-safety pointless

泛型以这种方式反变异,你最初的愿望可能是覆盖一个返回String的方法,返回Float < / p>

在设计泛型类时,我认为非常小心是一个很好的一般规则。你真的需要仿制药吗?如何使用它?一些通用设计最终会出现糟糕的结构,并且意味着用户必须恢复到 raw 版本。 JTable的过滤机制就是一个很好的例子:当你想要实现一个过滤链(你当然会这样做)时,整个过程就会崩溃。事实上;在这种情况下,添加仿制药是有代价的,实际上没有任何好处。

答案 7 :(得分:-1)

您可以使用@SuppressWarnings进行演员表。我认为在这种情况下这是合适的,只需确保在评论中记录原因。

或者,请执行以下操作:

public List<JComponent> getComponents()
{
  return new ArrayList<JComponent>( buttons );
}

是的,我知道这会复制,列表已经是只读的。但是在探查者告诉你之前,我会假设惩罚很小。

@Calum:我同意在返回类型中使用?-expressions是不好的形式,因为调用代码无法执行此操作,例如:

List<JComponent> list = obj.getComponents();