递归泛型用法 [英] Recursive Generic Usage

查看:169
本文介绍了递归泛型用法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我收到了'erickson'的一个非常相关的答案,但是有一个方面的问题(up-casting?),这在我的原始示例中没有明确提及,没有用他的答案解决,我已经扩展了这个例子来解决这个问题,并且我已经将它包含在这篇文章的结尾处,感谢您的帮助。



我目前正面临一个与Java泛型有关的问题,这个问题与一些被称为java enum definition。不过,当我尝试将其应用于我的代码时,发现自己遇到了不同的问题。



我提出了一个小示例,其中显示了我正面临的问题耳朵,我希望它能清楚地说明我的问题。



示例说明:我想要构建一个图形,其中节点类型可以变化。我定义了一个定义一些基本方法的抽象类 Node 和一个实现这些方法的具体类,即 。我也创建了一个名为 City 的ConcreteNode的专业化。在给定的图中,一个重要的要求是所有的元素都应该被创建它的相同类型或子类型,即ConcreteNode的图形只能具有ConcreteNodes Cities。



这些是我的类的定义:

  abstract class Node< T extends Node< T>> 
class ConcreteNode< T extends ConcreteNode< T>>扩展节点< T>
class City延伸ConcreteNode< City>

这些定义使用Enum类定义中的'Recurring Generic Pattern':

 类枚举< E扩展枚举< E>> 

问题:我在使用这些类时遇到问题。如果我必须留在城市级别,即将城市连接到城市,但我在尝试访问其他课程时遇到巨大问题,则我没有问题。



在以下代码中,可以在GraphUtil的方法签名中看到我的问题:


  1. addNewNeighbors1a 使用原始类型Node,但至少可以工作。
  2. addNewNeighbors1b 使用Node类型,但它不会' (所有错误都包含在代码中)。
  3. addNewNeighbors1c 对Node使用更复杂的参数,我希望能够工作,但它不会' t编译(错误包含在代码中)。
  4. addNewNeighbors3 使用Node的复杂参数,但它不会再次编译,即使参数是相同的节点和newNode。

在综合中,我的问题是如何上传这些自己参数化的泛型? 。



我很乐意为GraphUtil的方法获得最佳签名方面的帮助,假设这些方法将位于一个库中, 'b

谢谢大家。

以下是全部内容代码示例

  package test.city; 

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;

public class TestCity {
abstract class Node< T extends Node< T>> {
public abstract void addNeighbor(T n);
public abstract void addNeighbors(Collection< ;? extends T> nodes);
public abstract Collection< T>邻居();
}

class ConcreteNode< T extends ConcreteNode< T>>扩展节点< T> {
受保护的收藏< T> _neighbors = new ArrayList< T>();

@Override
public void addNeighbor(T n){
_neighbors.add(n);
}

@Override
public void addNeighbors(Collection< ;? extends T> nodes){
_neighbors.addAll(nodes);
}

@Override
public Collection< T> neighbours(){
return _neighbors;
}
}

class City延伸ConcreteNode< City> {
protected String _name;

public City(String name){
_name = name;
}

@Override
public String toString(){
return _name;



public TestCity(){
City nyc = new City(NYC);
nyc.addNeighbor(new City(Boston));
nyc.addNeighbor(new City(Wash));

GraphUtil.print(打印城市,nyc.neighbors());

GraphUtil.printNeighbors1(nyc);
GraphUtil.printNeighbors2(nyc);
GraphUtil.printNeighbors3(nyc);
GraphUtil.printNeighbors4(nyc);
GraphUtil.addNewNeighbors1a(nyc,new City(Miami));
GraphUtil.addNewNeighbors2(nyc,new City(NewOr));
GraphUtil.addNewNeighbors3(nyc,new City(Dallas));


static class GraphUtil {
static void printNeighbors1(Node <?> node){
print(Nodes,node.neighbors());


static void printNeighbors2(ConcreteNode <?> node){
print(Concrete nodes,node.neighbors()); (节点<>节点>节点){
print(节点2,node.neighbors());


static void printNeighbors3


static void printNeighbors4(ConcreteNode< ;? extends ConcreteNode<>> node){
print(Concrete nodes2,node.neighbors());
}

static void addNewNeighbors1a(Node node,City newNode){
node.addNeighbor(newNode);
print(Add city to node,node.neighbors());
}

static void addNewNeighbors1b(Node <?> node,City newNode){
// node.addNeighbor(newNode); < ----不编译!!!
//类型为
的方法addNeighbor(capture#8-of?)// // TestCity.Node< capture#8-of?>
//不适用于参数(TestCity.City)
}

static void addNewNeighbors1c(节点< ;? extends Node<>节点,City newNode) {
// node.addNeighbor(newNode); < ----不编译!!!
//在类型
// // TestCity.Node 中添加方法addNeighbor(capture#9-of?extends TestCity.Node<?>)
//扩展了TestCity.Node<?>>不是
//适用于参数(TestCity.City)

}

static void addNewNeighbors2(节点节点,ConcreteNode newNode){
节点.addNeighbor(newNode);
print(将具体节点添加到节点,node.neighbors());
}

static void addNewNeighbors3(Node <?extends Node <>> node,
Node <?extends Node> newNode){
// node.addNeighbor(newNode); < ----不编译!!!
//在类型
// // TestCity.Node< capture#8-of?//中的方法addNeighbor(capture#8-of?extends TestCity.Node<?>)
//?扩展了TestCity.Node<?>>不是
//适用于参数
//(TestCity.Node< capture#10-of?extends TestCity.Node<>>)
}

static void print(String msg,Collection<?> col){
System.out.println(msg +:+ Arrays.toString(col.toArray()));



public static void main(String [] args){
new TestCity();
}

}

运行此代码的输出是(波士顿,华盛顿)
节点:[波士顿,华盛顿州,波士顿,华盛顿州]
打印城市:[波士顿,华盛顿]
节点:[波士顿,洗涤]
混凝土节点:[波士顿,华盛顿州]
节点2:[波士顿,华盛顿州]
具体节点2:[波士顿,华盛顿]
将城市添加到节点: ,迈阿密]
将具体节点添加到节点:[波士顿,华盛顿,迈阿密,新奥尔]

问题的第二部分



有一个相关的问题,我没有包含在原始示例中,因为我认为解决方案也正在进行



现在我已经将以下方法添加到了GraphUtil中:

  static< T extends Node< T>> T getSomeNeighbor(T节点){
return node.neighbors()。iterator()。next();
}

在我的主类中,我正在尝试以下方法:

  City someCity = GraphUtil.getSomeNeighbor(nyc); 
someCity.addNeighbor(新城市(伦敦)); // OK

ConcreteNode someCN1 = GraphUtil.getSomeNeighbor(nyc);
someCN1.addNeighbor(new City(Paris)); // OK,但原始

ConcreteNode<?> someCN2 = GraphUtil.getSomeNeighbor(nyc);
someCN2.addNeighbor(新城市(柏林)); //不编译

ConcreteNode<?> nc =新城市();
nc.addNeighbor(new City(Bern)); //不会编译

第一种情况起作用,因为我知道返回的具体类型,它与参数中提供的类型一致。



在第二种和第三种情况下,我假设我不知道类型City。第二种情况有效,但我使用的是原始类型ConcreteNode。

在第三种情况下,第二行出现编译错误:中的方法addNeighbor(capture#3 of?)。 ConcreteNode不适用于参数(TestCity.City)。



在示例中,我使用'new City( - )'as一个参数,因为我不知道如何上演他们。在第四种情况下,我试图将City上传到ConcreteNode,但失败了。当前的编译器错误如下:类型TestCity.ConcreteNode中的方法addNeighbor(capture#4 of?)不适用于参数(TestCity.City)



问题:


  1. 如何修正个案2和3知道类型的城市?

  2. 如何将City上传到ConcreteNode(或Node)?

您的帮助。

解决方案

您可以制作泛型方法以及泛型类型。使用这些方法, GraphUtils 中的问题方法可以像这样修复:

  static< T extends Node< T>> void addNewNeighbors1a(T node,T newNode)
{
node.addNeighbor(newNode);
print(Add city to node,node.neighbors());
}

static< T extends Node< T>> void addNewNeighbors2(T node,T newNode)
{
node.addNeighbor(newNode);
print(将具体节点添加到节点,node.neighbors());
}

嘿,等一下......那些是相同的方法!

事实证明,因为它们只依赖于 Node 的接口,所以你只需要其中的一个处理任何 Node 实现。



在这条路上,您可能会发现需要更改 Node 接口是这样的:

  public abstract< S extends T> void addNeighbor(S n); 


Edited:" I received a very pertinent answer from 'erickson', but there is a side problem (up-casting?) that was not explicitly covered in my original example and is not solved with his answer. I've extended the example to cover this other problem, and I've included it at the end of this post. Thanks for your help.

I'm currently facing a problem with Java generics that is related to something that has been called the "Curiously Recurring Generic Pattern". I thought I had found the solution after reading the answer from Jon Skeet to this question "java enum definition". Nevertheless, I found myself with different problems when I tried to apply it in my code.

I've come up with a 'small' example where the problem I'm facing appears. I hope it will be clear enough to illustrate my questions.

Description of the example: I want to build a graph where node types can vary. I've defined an abstract class Node, which defines some basic methods, and a concrete class that implements those methods, namely ConcreteNode. I've also created a specialization of ConcreteNode called City.

In a given graph, an important requirement is that all the elements should be made of the same types or subtypes of it, i.e. a graph of ConcreteNode can have only ConcreteNodes or Cities.

These are the definitions of my classes:

abstract class Node<T extends Node<T>>
class ConcreteNode<T extends ConcreteNode<T>> extends Node<T>
class City extends ConcreteNode<City>

These definitions make use the 'Recurring Generic Pattern' also found in the definition of the Enum class:

Class Enum<E extends Enum<E>>

Questions: I'm having problem using these classes. I don't have problems if I have to stay at the City level in the hierarchy, i.e. connecting City to City, but I'm having huge problems when trying to access other classes.

In the following code, my problems can be seen in the signature of the methods of GraphUtil:

  1. addNewNeighbors1a uses the raw type Node, but at least it works.
  2. addNewNeighbors1b uses the type Node, but it doesn't compile at all (the error is included the code).
  3. addNewNeighbors1c uses a more complex parameter for Node, that I expected to work, but it doesn't compile (the error is included the code).
  4. addNewNeighbors3 uses complex parameters for Node, but it doesn't compile again, even though the parameters are the same for node and newNode.

In synthesis, my question is how to upcast these generic types that are parametrized on themselves?.

I will be really glad to get help with the best signature for the methods of GraphUtil, assuming that these methods are going to be located in a library that doesn't know anything about City or even ConcreteNode.

Thank you all.

Here's the full code of the example

package test.city;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;

public class TestCity {
    abstract class Node<T extends Node<T>> {
    public abstract void addNeighbor(T n);
    public abstract void addNeighbors(Collection<? extends T> nodes);
    public abstract Collection<T> neighbors();
    }

    class ConcreteNode<T extends ConcreteNode<T>> extends Node<T> {
    protected Collection<T> _neighbors = new ArrayList<T>();

    @Override
    public void addNeighbor(T n) {
        _neighbors.add(n);
    }

    @Override
    public void addNeighbors(Collection<? extends T> nodes) {
        _neighbors.addAll(nodes);
    }

    @Override
    public Collection<T> neighbors() {
        return _neighbors;
    }
    }

    class City extends ConcreteNode<City> {
    protected String _name;

    public City(String name) {
        _name = name;
    }

    @Override
    public String toString() {
        return _name;
    }
    }

    public TestCity() {
    City nyc = new City("NYC");
    nyc.addNeighbor(new City("Boston"));
    nyc.addNeighbor(new City("Wash"));

    GraphUtil.print("Printing cities", nyc.neighbors());

    GraphUtil.printNeighbors1(nyc);
    GraphUtil.printNeighbors2(nyc);
    GraphUtil.printNeighbors3(nyc);
    GraphUtil.printNeighbors4(nyc);
    GraphUtil.addNewNeighbors1a(nyc, new City("Miami"));
    GraphUtil.addNewNeighbors2(nyc, new City("NewOr"));
    GraphUtil.addNewNeighbors3(nyc, new City("Dallas"));
    }

    static class GraphUtil {
    static void printNeighbors1(Node<?> node) {
        print("Nodes", node.neighbors());
    }

    static void printNeighbors2(ConcreteNode<?> node) {
        print("Concrete nodes", node.neighbors());
    }

    static void printNeighbors3(Node<? extends Node<?>> node) {
        print("Nodes2", node.neighbors());
    }

    static void printNeighbors4(ConcreteNode<? extends ConcreteNode<?>> node) {
        print("Concrete nodes2", node.neighbors());
    }

    static void addNewNeighbors1a(Node node, City newNode) {
        node.addNeighbor(newNode);
        print("Add city to node", node.neighbors());
    }

    static void addNewNeighbors1b(Node<?> node, City newNode) {
        // node.addNeighbor( newNode ); <---- DOES NOT COMPILE!!!
        // The method addNeighbor(capture#8-of ?) in the type
        // TestCity.Node<capture#8-of ?>
        // is not applicable for the arguments (TestCity.City)
    }

    static void addNewNeighbors1c(Node<? extends Node<?>> node, City newNode) {
        // node.addNeighbor( newNode ); <---- DOES NOT COMPILE!!!
        // The method addNeighbor(capture#9-of ? extends TestCity.Node<?>)
        // in the type
        // TestCity.Node<capture#9-of ? extends TestCity.Node<?>> is not
        // applicable for the arguments (TestCity.City)

    }

    static void addNewNeighbors2(Node node, ConcreteNode newNode) {
        node.addNeighbor(newNode);
        print("Add concrete node to node", node.neighbors());
    }

    static void addNewNeighbors3(Node<? extends Node<?>> node,
        Node<? extends Node<?>> newNode) {
        // node.addNeighbor( newNode ); <---- DOES NOT COMPILE!!!
        // The method addNeighbor(capture#8-of ? extends TestCity.Node<?>)
        // in the type
        // TestCity.Node<capture#8-of ? extends TestCity.Node<?>> is not
        // applicable for the arguments
        // (TestCity.Node<capture#10-of ? extends TestCity.Node<?>>)
    }

    static void print(String msg, Collection<?> col) {
        System.out.println(msg + ": " + Arrays.toString(col.toArray()));
    }
    }

    public static void main(String[] args) {
    new TestCity();
    }

}

The output of running this code is the following (no surprises at all):

Printing cities: [Boston, Wash]
Nodes: [Boston, Wash]
Concrete nodes: [Boston, Wash]
Nodes2: [Boston, Wash]
Concrete nodes2: [Boston, Wash]
Add city to node: [Boston, Wash, Miami]
Add concrete node to node: [Boston, Wash, Miami, NewOr]

Second part of the problem

There is a related problem that I had not included in the original example because I thought the solution was also going to apply.

I've now added the following method to GraphUtil:

static <T extends Node<T>> T getSomeNeighbor(T node) {
    return node.neighbors().iterator().next();
}

And from my main class I'm trying the following:

City someCity = GraphUtil.getSomeNeighbor(nyc); 
someCity.addNeighbor(new City("London")); // OK

ConcreteNode someCN1 = GraphUtil.getSomeNeighbor(nyc); 
someCN1.addNeighbor(new City("Paris")); // OK, but raw

ConcreteNode<?> someCN2 = GraphUtil.getSomeNeighbor(nyc); 
someCN2.addNeighbor(new City("Berlin")); // Does not compile

ConcreteNode<?> nc = new City("");
nc.addNeighbor(new City("Bern")); // Does not compile

The first case works, because I know the concrete type that is returned, and it is coherent with the type provided in the parameter.

In the second and third cases I'm assuming I don't know the type City. The second case works, but I'm using the raw type ConcreteNode.

In the third case, there is a compilation error in the second line: "The method addNeighbor(capture#3-of ?) in the type TestCity.ConcreteNode is not applicable for the arguments (TestCity.City)."

In the example I'm using 'new City("-")' as a parameter because I don't know how to up-cast them. In the fourth case I tried to up-cast City to ConcreteNode, but it failed. The current compiler error is the following: "The method addNeighbor(capture#4-of ?) in the type TestCity.ConcreteNode is not applicable for the arguments (TestCity.City)"

Questions:

  1. How can I fix cases 2 and 3 without knowing the type City?
  2. How can up-cast City to ConcreteNode (or to Node)?

Thanks for your help.

解决方案

You can make generic methods as well as generic types. Using these, the problem methods in GraphUtils can be fixed like this:

static <T extends Node<T>> void addNewNeighbors1a(T node, T newNode)
{
  node.addNeighbor(newNode);
  print("Add city to node", node.neighbors());
}

static <T extends Node<T>> void addNewNeighbors2(T node, T newNode)
{
  node.addNeighbor(newNode);
  print("Add concrete node to node", node.neighbors());
}

Hey, wait a second… those are the same methods!

It turns out that, since they only depend on the interface of Node, you only need one of them to handle any Node implementation.

Down the road, you might find it necessary to change the Node interface like this:

public abstract <S extends T> void addNeighbor(S n);

这篇关于递归泛型用法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆