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

时间:2015-11-10 14:55:18

标签: java generics

我的问题是一个简短而抽象的形式:

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

我认为这个问题应该以下列方式平易近人。但是,以下代码无效。特别是,ElementBasis#registerSub1Element#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方法中使用其类型参数。遗憾的是没有成功。

有人能找到合适的实施方案吗?

1 个答案:

答案 0 :(得分:2)

与其他一些语言不同。 Java没有提供关键字来表示“此类”。因此,没有简单的方法可以强制register仅接受Container类型的? super this class。因此,让行container.add(this);生效是有问题的。

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

一个问题是它不能轻易地与3个类/接口链连接

Sub1Element extends ElementBasis implements Element

如果您ElementBasis通用以及Element可以执行此操作(链接问题的所有解决方案仅涉及长度为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);
}