java - 如何使用类型参数实现容器/元素类?

标签 java generics

我的问题以简短且相当抽象的形式:

我想实现 ContainerElement 进行类型参数化的类它包含的元素的类类型(即 Container<T extends Element> )。到目前为止没有什么特别的。但是:Element类及其子类应提供 register添加 Element 的方法给定 Container 的实例类(即 register(Container<? super xxx> container) { ...} )

我认为这个问题应该通过以下方式解决。但是,以下代码无效。特别是 ElementBasis#register 中的类型参数和Sub1Element#register导致名称冲突错误。不过我认为应该有可能找到解决该问题的正确方法。

public interface Element {
  void register(Container<T super Element> container);
}

public class ElementBasis {
  @Override
  void register(Container<? super ElementBasis> container) {
    container.add(this);
  }  
}

public class Sub1Element extends ElementBasis {
  // ...
  @Override
  void register(Container<? super Sub1Element> container) {
    container.add(this);
  }  
}

public class Sub2Element extends ElementBasis {
  // ... 
}

此外,我希望能够通过提供 ElementGroup 来为元素提供结构。 Element 的子类:

public class ElementGroup<T extends Element> extends ElementBasis {
  // ...
  @Override
  void register(Container<? super T> container) {
    foreach(T member : groupMemebers) {
      container.add(member)
    }
  }  
}

我还尝试通过参数化 Element 来解决问题类,使其类型参数可以在 register 中使用方法。不幸的是没有成功。

有人能找到合适的实现吗?

最佳答案

与其他一些语言不同。 Java 没有提供关键字来表示“此类”。那么没有简单的方法来强制 register 只接受 类型的 Container 吗? super 这个类。因此,让 container.add(this); 行正常工作是有问题的。

一种可能的解决方法是使 Element 通用,如所述 here 。这种方法存在各种各样的问题(我个人非常不喜欢它),包括“getThis 技巧”解决的问题。

一个问题是它不能轻松地与 3 个类/接口(interface)链一起工作

Sub1Element extends ElementBasis implements Element

如果您使 ElementBasisElement 通用,您可以做到这一点(链接问题的所有解决方案仅涉及长度链2)。为了简单起见,在这里我用 Collection 替换 Container

public interface Element<E extends Element<E>> {
    void register(Collection<? super E> container);
}

public class ElementBasis<E extends ElementBasis<E>> implements Element<E> {
    @Override
    public void register(Collection<? super E> collection) {
        collection.add((E) this);     // Unchecked cast
    }
}

public class Sub1Element<E extends Sub1Element<E>> extends ElementBasis<E> {
    @Override
    public void register(Collection<? super E> collection) {
        collection.add((E) this);     // Unchecked cast
    }
} 

这确实有效,但由于 ElementBasis 是一个具有自引用约束的具体泛型类,因此您只能将其与通配符一起使用。

编译干净:

ElementBasis<?> e = new ElementBasis<>();
List<ElementBasis<?>> list = new ArrayList<>(Arrays.<ElementBasis<?>>asList(e, e));
e.register(list);
System.out.println(list);

但是,通配符非常令人困惑,并且乍一看似乎是不必要的(尽管事实并非如此)。

考虑到这种方法的所有问题,我会避免它。

我的首选方法是摆脱所有类型参数,停止尝试使 register 成为 Element 的成员,而是使用静态方法。

public static <E extends Element> void register(E e, Collection<? super E> collection) {
    collection.add(e);
}

关于java - 如何使用类型参数实现容器/元素类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33632871/

相关文章:

java - 通过接口(interface)发送数组列表会导致类转换异常吗?

java - 在 JTable 中显示行号

java - 什么是 PECS(生产者扩展消费者 super )?

java - 如何在 Java 泛型方法中定义返回类型?

java - 即时创建 java 参数化类型

java - 如何使 JButton 点击​​后不可点击?

java - 从二维数组中读取行

java - 封装的概念

java - 无法将通用列表添加到通用 map

c# - 为运行时初始化传递一个通用(模板)类