Java 仿制药自引用:安全吗?
Java generics self-reference: is it safe?
我有这个简单的界面:
public interface Node<E extends Node<E>>
{
public E getParent();
public List<E> getChildren();
default List<E> listNodes()
{
List<E> result = new ArrayList<>();
// ------> is this always safe? <-----
@SuppressWarnings("unchecked")
E root = (E) this;
Queue<E> queue = new ArrayDeque<>();
queue.add(root);
while(!queue.isEmpty())
{
E node = queue.remove();
result.add(node);
queue.addAll(node.getChildren());
}
return result;
}
}
我看到 this
始终是 Node<E>
的一个实例(根据定义)。
但我无法想象 this
不是 E
的实例的情况...
既然E extends Node<E>
,根据定义,Node<E>
不应该也等同于E
吗??
您能否举一个对象的例子,它是 Node<E>
的实例,但不是 E
的实例??
与此同时,我的大脑正在融化...
前面的class是一个简化的例子。
为了显示为什么我需要一个自绑定,我添加了一点复杂性:
public interface Node<E extends Node<E, R>, R extends NodeRelation<E>>
{
public List<R> getParents();
public List<R> getChildren();
default List<E> listDescendants()
{
List<E> result = new ArrayList<>();
@SuppressWarnings("unchecked")
E root = (E) this;
Queue<E> queue = new ArrayDeque<>();
queue.add(root);
while(!queue.isEmpty())
{
E node = queue.remove();
result.add(node);
node.getChildren()
.stream()
.map(NodeRelation::getChild)
.forEach(queue::add);
}
return result;
}
}
public interface NodeRelation<E>
{
public E getParent();
public E getChild();
}
如果没有 <E extends Node<E>>
,您可能会遇到以下任一情况:
Node<Integer>
通用类型根本不是 Node
,或者
Node<DifferentNode>
通用边界不匹配。
就是说,不 以这种方式查看边界是典型的,因为 Node<E>
应该是 包含 [=29] 的节点=] 一些 E
类型的值,而 children
将是 List<Node<E>>
,而不是 List<E>
。
一个简单的例子来说明问题:不同类型节点的节点:
class NodeA implements Node<NodeA> {
...
}
并且:
class NodeB implements Node<NodeA> {
...
}
在这种情况下,E root = (E) this
将解析为 NodeA root = (NodeA) this
,其中 this
是 NodeB
。那是不相容的。
问题不在E root = (E) this
。在您开始遍历 listNodes()
的结果之前,它可能会很好地工作。
该示例演示了将 ClassCastException
抛出的确切位置:
public interface Node<E extends Node<E>> {
List<E> getRelatedNodes();
default List<E> getAllNodes() {
List<E> result = new ArrayList<>();
result.add((E) this); //<--that cast is not a problem because of type erasure
return result;
}
}
class NodeA implements Node<NodeA> {
public NodeA() {
}
@Override
public List<NodeA> getRelatedNodes() {
return null;
}
}
class NodeB implements Node<NodeA> {
private List<NodeA> relatedNodes;
public NodeB(List<NodeA> relatedNodes) {
this.relatedNodes = relatedNodes;
}
@Override
public List<NodeA> getRelatedNodes() {
return relatedNodes;
}
}
执行:
List<NodeA> nodes = new NodeB(Arrays.asList(new NodeA())).getAllNodes(); //according to generic it is list of NodeA objects
for (NodeA node : nodes) { //ClassCastException will be thrown
System.out.println(node);
}
在这种情况下,getThis
方法通常很有用(按照惯例)returns this
.
我会做以下事情
public interface Node<E extends Node<E, R>,
R extends NodeRelation<E, R>>
{
public List<R> getParents();
public List<R> getChildren();
public List<E> listDescendants() ;
}
public interface NodeRelation<E extends Node<E, R>,
R extends NodeRelation<E, R>>
{
public E getParent();
public E getChild();
}
abstract class ANode<E extends ANode<E,R>,
R extends ARelation<E,R>>
implements Node<E,R> {
abstract protected E getThis() ;
public List<E> listDescendants()
{
List<E> result = new ArrayList<>();
E root = getThis() ;
...
return result;
}
}
abstract class ARelation<E extends ANode<E,R>,
R extends ARelation<E,R>>
implements NodeRelation<E,R> {
}
class CNode extends ANode<CNode, CRelation> {
public CNode getThis() { return this ; }
...
}
class CRelation extends ARelation<CNode, CRelation> {
...
}
尽管我可能不会为同时拥有抽象 class 和界面层而烦恼。
我有这个简单的界面:
public interface Node<E extends Node<E>>
{
public E getParent();
public List<E> getChildren();
default List<E> listNodes()
{
List<E> result = new ArrayList<>();
// ------> is this always safe? <-----
@SuppressWarnings("unchecked")
E root = (E) this;
Queue<E> queue = new ArrayDeque<>();
queue.add(root);
while(!queue.isEmpty())
{
E node = queue.remove();
result.add(node);
queue.addAll(node.getChildren());
}
return result;
}
}
我看到 this
始终是 Node<E>
的一个实例(根据定义)。
但我无法想象 this
不是 E
的实例的情况...
既然E extends Node<E>
,根据定义,Node<E>
不应该也等同于E
吗??
您能否举一个对象的例子,它是 Node<E>
的实例,但不是 E
的实例??
与此同时,我的大脑正在融化...
前面的class是一个简化的例子。
为了显示为什么我需要一个自绑定,我添加了一点复杂性:
public interface Node<E extends Node<E, R>, R extends NodeRelation<E>>
{
public List<R> getParents();
public List<R> getChildren();
default List<E> listDescendants()
{
List<E> result = new ArrayList<>();
@SuppressWarnings("unchecked")
E root = (E) this;
Queue<E> queue = new ArrayDeque<>();
queue.add(root);
while(!queue.isEmpty())
{
E node = queue.remove();
result.add(node);
node.getChildren()
.stream()
.map(NodeRelation::getChild)
.forEach(queue::add);
}
return result;
}
}
public interface NodeRelation<E>
{
public E getParent();
public E getChild();
}
如果没有 <E extends Node<E>>
,您可能会遇到以下任一情况:
Node<Integer>
通用类型根本不是 Node
,或者
Node<DifferentNode>
通用边界不匹配。
就是说,不 以这种方式查看边界是典型的,因为 Node<E>
应该是 包含 [=29] 的节点=] 一些 E
类型的值,而 children
将是 List<Node<E>>
,而不是 List<E>
。
一个简单的例子来说明问题:不同类型节点的节点:
class NodeA implements Node<NodeA> {
...
}
并且:
class NodeB implements Node<NodeA> {
...
}
在这种情况下,E root = (E) this
将解析为 NodeA root = (NodeA) this
,其中 this
是 NodeB
。那是不相容的。
问题不在E root = (E) this
。在您开始遍历 listNodes()
的结果之前,它可能会很好地工作。
该示例演示了将 ClassCastException
抛出的确切位置:
public interface Node<E extends Node<E>> {
List<E> getRelatedNodes();
default List<E> getAllNodes() {
List<E> result = new ArrayList<>();
result.add((E) this); //<--that cast is not a problem because of type erasure
return result;
}
}
class NodeA implements Node<NodeA> {
public NodeA() {
}
@Override
public List<NodeA> getRelatedNodes() {
return null;
}
}
class NodeB implements Node<NodeA> {
private List<NodeA> relatedNodes;
public NodeB(List<NodeA> relatedNodes) {
this.relatedNodes = relatedNodes;
}
@Override
public List<NodeA> getRelatedNodes() {
return relatedNodes;
}
}
执行:
List<NodeA> nodes = new NodeB(Arrays.asList(new NodeA())).getAllNodes(); //according to generic it is list of NodeA objects
for (NodeA node : nodes) { //ClassCastException will be thrown
System.out.println(node);
}
在这种情况下,getThis
方法通常很有用(按照惯例)returns this
.
我会做以下事情
public interface Node<E extends Node<E, R>,
R extends NodeRelation<E, R>>
{
public List<R> getParents();
public List<R> getChildren();
public List<E> listDescendants() ;
}
public interface NodeRelation<E extends Node<E, R>,
R extends NodeRelation<E, R>>
{
public E getParent();
public E getChild();
}
abstract class ANode<E extends ANode<E,R>,
R extends ARelation<E,R>>
implements Node<E,R> {
abstract protected E getThis() ;
public List<E> listDescendants()
{
List<E> result = new ArrayList<>();
E root = getThis() ;
...
return result;
}
}
abstract class ARelation<E extends ANode<E,R>,
R extends ARelation<E,R>>
implements NodeRelation<E,R> {
}
class CNode extends ANode<CNode, CRelation> {
public CNode getThis() { return this ; }
...
}
class CRelation extends ARelation<CNode, CRelation> {
...
}
尽管我可能不会为同时拥有抽象 class 和界面层而烦恼。