我的问题简短而抽象:
我想实现一个Container
类,该类由其包含的元素的Element
类类型(即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 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);
}