【问题标题】:How to implement container/element classes by using type parameters?如何使用类型参数实现容器/元素类?
【发布时间】:2016-02-11 12:00:50
【问题描述】:

我的问题简短而抽象:

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

我认为这个问题应该可以通过以下方式解决。但是,以下代码无效。特别是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 {
  // ... 
}

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

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

我还尝试通过参数化Elementclasses 来解决这个问题,这样它的类型参数就可以在register 方法中使用。可惜没有成功。

谁能找到合适的实现?

【问题讨论】:

  • 你的意思是ContainerBasisContainer&lt;? super ContainerBasis&gt; 对我来说没有多大意义。
  • 你说得对,保罗。不是ContainerBasis,而是ElementBasis。对不起。我已经更正了。

标签: java generics


【解决方案1】:

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

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-02-02
    • 1970-01-01
    • 2012-09-05
    • 2017-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多