【问题标题】:new List<A & ? extends B>();新列表<A & ?扩展 B>();
【发布时间】:2018-10-08 08:34:01
【问题描述】:

如果您知道如何运行标题行,只需向下滚动到 tl;dr 或写下答案!

我写了其余的文字以避免XY problem。什么时候完成都无所谓了。


上下文

我想创建一个接受泛型(列表、地图等)的对象。

列表中的对象应满足 2 个条件:

  1. 拥有mymethod()(我通过创建一个将此作为方法头的接口实现)
  2. 扩展JComponent

我是如何做到这一点的:

  1. 我创建了多个类,其中任何一个都实现了MyInterface,并且正在扩展一个 JComponent(它的子类)。这就是我在标题问题中遇到的问题:new List&lt;MyInterface extends JComponent);
  2. 另一个想法:创建一个抽象类(符合所有标准)而不是接口。问题:我需要的具体类不扩展同一个类。
  3. 我想到了泛型,就像:public abstract class MyClass&lt;X extends JComponent&gt; extends X,但这行不通——原因很明显。 (例如,我必须实现超类的抽象方法,但如果它是泛型,我现在不知道。)

tl;博士

我遇到了一个问题,如果你能让这条线正常工作,这个问题很容易解决:

new List<A extends B>();

或者,由@khelwood 介绍: 新列表();

地点:

  • A 是一个接口(就像我可以声明一个 new List&lt;MyInterface&gt;()
  • 并且任何项目都扩展? extends B(例如:它可以是 JTextField 或 JTextArea 或任何他们喜欢的,但它必须是 JComponent 的(子类) (JComponent = B))

【问题讨论】:

  • 我正在尝试按照您的要求进行操作。你想要一个列表,其中每个项目都扩展AB
  • @khelwood 差不多。我希望每个项目都扩展 ? extends A(一个 JComponent 或其子类之一)并实现 B。
  • @Asqiir 如果您所说的具体类不扩展同一个类,那么它们如何扩展JComponent
  • @BartoszKP 如编辑中所述,它们不扩展同一个类,但它始终是 JComponent 的子类(因此他们可以决定 JTextArea 或 JTextField 或 JRadioBox 等)
  • @Asqiir 您对List&lt;A extends B&gt; 的使用会引起混淆。 A 类型要么扩展 B 类型,要么不扩展。如果你真的想要一个扩展 A&amp;B 的类型,那就不是一回事了。

标签: java generics interface abstract-class


【解决方案1】:

如果您希望能够创建扩展 JComponent 并实现您的接口的事物列表,并将事物添加到这样的列表中,您需要定义一个实现两者的具体类型:

List<ThatType> list = new ArrayList<>();
list.add(new ThatType());

你可以为此声明一个类型变量,以便编写一个泛型方法:

<T extends JComponent & YourInterface> void something(T thing) {
  // ...
}

&lt;Something extends SomethingElse&gt; 仅适用于类型变量的声明。

此外,您不会创建Somethings that extends SomethingElse 的列表:您会创建Somethings 的列表,而Something extends SomethingElse 是否取决于这些类型的定义方式。

为了更具体,您不要创建 new List&lt;String extends Object&gt;:您创建 List&lt;String&gt;,而 String 恰好扩展了 Object

您需要适当地声明类型变量,例如:

class Foo<B> {
  <A extends B> List<A> list() {
    return new List<>();
  }
}

【讨论】:

  • StringObject 不能很好地类比 OP 的要求。他们需要确保列表的元素同时实现子类JComponent的不相关接口。
  • @BartoszKP extends 表示它们并非不相关。
  • 我只是重申OP想要什么,而不是使用extends是否可以实现。
  • 我真的很喜欢你的第一行代码,但我的 IDE 将其标记为错误(&amp;
  • 你想如何创建ThatType? ;-) 假设:class Concrete1 extends JTextField implements MyInterfaceclass Concrete2 extends JTextArea implements MyInterface。现在,您提出了一个 c1 和 c2 都在扩展的抽象类:class ThatType extends ????? implements MyInterface。您能否告诉我,在这里放置什么而不丢失有关 JComponent 的信息? (并让子类能够自己决定扩展哪个 JComponent? 请注意:我想到了这一点。您可以在 2. 和 3 中找到方面。)
【解决方案2】:

你这么说:

另一个想法:创建一个抽象类(符合所有标准)而不是接口。问题:我需要的具体类不扩展同一个类。

但同时你说它们扩展了JComponent。发生这种情况的唯一可能性是它们都传递扩展JComponent。所以在继承层次结构的某个点上,有一个来自JComponent 的直接继承。

如果这仍然是您的代码,那么您可以在此处引入另一个类,而不是扩展 JComponent 并实现您的接口的 JComponent

如果不是,那么我认为仅使用语法是不可能实现的。

【讨论】:

    【解决方案3】:

    我不认为类型系统允许你做你想做的事并获得编译时类型安全。

    一个近似值如下:

    List<JComponent> componentList = new ArrayList<>();
    List<MyInterface> myInterfaceList = (List) componentList;
    

    如果您不介意编译时警告,您现在在同一个列表上有两个视图:一个显示内容为JComponent,另一个显示为MyInterface

    您可以将对象添加到任一视图,然后从为上下文提供正确类型的视图中读取它们。

    自然,当您添加对象时,编译器不会检查插入的对象是否是JComponentMyInterface 的子类。那是你的责任。

    如果你想要更多(运行时)类型安全,你可以这样:

    List rawList = new ArrayList();
    List checkedList = Collections.checkedList(rawList, JComponent.class);
    List doublyCheckedList = Collections.checkedList(checkedList, MyInterface.class);
    List<JComponent> componentList = doublyCheckedList;
    List<MyInterface> myInterfaceList = doublyCheckedList;
    

    如果您只插入doublyCheckedList,则在运行时列表将检查插入的对象是否是JComponentMyInterface 的子类。阅读时,你可以像我之前的片段一样使用这两个视图。

    【讨论】: