如何使用类型参数实现容器/元素类? [英] How to implement container/element classes by using type parameters?
问题描述
我的问题很简短而且很抽象:
我想实现一个 Container 类,是由它所包含的元素(即<$ c $ c> Container< T extends Element>
)的 Element
类类型进行类型参数化。迄今为止没有什么特别的。但是:元素
类及其子类应提供一个寄存器
方法,该方法将元素
实例到给定的 Container 类(即
register(Container<?super xxx>容器){...}
)
我觉得这个问题应该可以通过以下几种方式实现。但是,以下代码无效。特别是, ElementBasis#register
和 Sub1Element#register
中的类型参数会导致名称冲突错误。不过,我认为应该有可能找到适当的解决方案。
$ b
public interface Element {
void register(Container< T super Element> container) ;
$ b 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.add(this);
public class Sub2Element extends ElementBasis {
// ...
}
此外,我希望能够通过提供 ElementGroup
子元素来为元素提供结构。类元素
:
public class ElementGroup< T扩展元素>扩展ElementBasis {
// ...
@Override
void register(Container< ;? super T> container){
foreach(T member:groupMemebers){
container.add(member)
}
}
}
我还尝试通过参数化 Element
类来解决这个问题,使得它的类型参数可以在寄存器
方法中使用。不幸的是没有成功。
任何人都可以找到适当的实现吗?
与其他一些语言不同。 Java没有提供关键字来表示这个类。因此,执行 register >仅接受
Container 类型
的方法并不简单。超级这个类
。因此,获取 container.add(this);
这一行是有问题的。
一种可能的解决方法如元素良好的一起>此处。这种方法存在种种问题(我个人很不喜欢它),包括getThis trick解决的问题。
一个问题是它使用3个类/接口的链很难工作
Sub1Element扩展ElementBasis实现元素
$ c如果您制作 ElementBasis
泛型,您可以 以及元素
(链接问题的所有解决方案仅涉及长度为2的链)。这里我简单地用集合
代替 Container
。
public interface Element< E extends Element< E>> {
void register(Collection<?super E>容器);
}
public class ElementBasis< E extends ElementBasis< E>>实现Element< E> {
@Override
public void register(Collection< ;? super E> collection){
collection.add((E)this); //取消选中cast b $ b}
}
public class Sub1Element< E extends Sub1Element< E>>扩展ElementBasis< E> {
@Override
public void register(Collection< ;? super E> collection){
collection.add((E)this); //未经检查的转换
$ $ $
$ b 这是行得通的,但是因为 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>无效注册(E e,Collection<?super E> collection){
collection.add(e);
}
My problem in a short and rather abstract form:
I would like to implement a Container
class which is type-parametrized by the Element
class type of the elements it contains (i.e. Container<T extends Element>
). Nothing really special so far. But: The Element
class and its sub-classes shall provide a register
method that adds the Element
instance to a given Container
class (i.e. register(Container<? super xxx> container) { ...}
)
I think this problem should be approachable in the following kind of way. However, the following code is not valid. In particular, the type parameters in ElementBasis#register
and Sub1Element#register
leads to name clash errors. Still I think it should be possible to find a proper implementation of that problem.
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 {
// ...
}
Moreover, I would like to be able to give the elements a structure by providing an ElementGroup
sub-class of Element
:
public class ElementGroup<T extends Element> extends ElementBasis {
// ...
@Override
void register(Container<? super T> container) {
foreach(T member : groupMemebers) {
container.add(member)
}
}
}
I also tried to solve the problem by parametrizing the Element
classes such that its type parameter can be used in the register
method. Unfortunately with no success.
Can anyone find a proper implementation?
解决方案 Unlike some other languages. Java does not provide a keyword to mean "this class". So there is no easy way to enforce that register
accepts only Container
s of type ? super this class
. As a result, getting the line container.add(this);
to work is problematic.
One possible workaround is to make Element
generic, as explained here. There are all sorts of problems with this approach (I personally dislike it strongly), including the issue that is solved by the "getThis trick".
One problem is that it doesn't work easily with a chain of 3 classes/interfaces
Sub1Element extends ElementBasis implements Element
You can do it if you make ElementBasis
generic as well as Element
(all the solutions to the linked question only involve chains of length 2). Here I have substituted Collection
for Container
for simplicity.
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
}
}
This does work, but because ElementBasis
is a concrete generic class with a self-referential constraint, you can only use it with wildcards.
This compiles cleanly:
ElementBasis<?> e = new ElementBasis<>();
List<ElementBasis<?>> list = new ArrayList<>(Arrays.<ElementBasis<?>>asList(e, e));
e.register(list);
System.out.println(list);
However, the wildcards are extremely confusing, and seem at first to be unnecessary (though they are not).
Considering all the problems with this approach, I would avoid it.
My preferred approach would be to get rid of all the type parameters, stop trying to make register
a member of Element
, and use a static method instead.
public static <E extends Element> void register(E e, Collection<? super E> collection) {
collection.add(e);
}
这篇关于如何使用类型参数实现容器/元素类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!