gpt4 book ai didi

java - 递归通用用法

转载 作者:太空宇宙 更新时间:2023-11-04 10:10:27 25 4
gpt4 key购买 nike

编辑:“我从'erickson'那里收到了一个非常相关的答案,但是存在一个附带问题(向上投射?),这个问题在我的原始示例中并未明确涵盖,并且无法用他的答案解决。我将该示例扩展到涵盖了另一个问题,我已在本文结尾处将其包括在内。感谢您的帮助。

我目前正面临Java泛型的问题,该问题与被称为"Curiously Recurring Generic Pattern"的东西有关。阅读了乔恩·斯凯特(Jon Skeet)对这个问题"java enum definition"的回答后,我以为我找到了解决方案。但是,当我尝试将其应用到代码中时,发现自己遇到了其他问题。

我想出了一个“小”示例,其中出现了我所面临的问题。我希望它将足够清楚地说明我的问题。

示例说明:我想构建一个节点类型可以变化的图形。我定义了一个抽象类Node,它定义了一些基本方法,以及一个实现这些方法的具体类,即ConcreteNode。我还创建了一个名为City的ConcreteNode专业化产品。

在给定图中,一个重要的要求是所有元素都应由其相同的类型或子类型组成,即,ConcreteNode的图形只能包含ConcreteNodes或Cities。

这些是我的课程的定义:

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


这些定义使用在Enum类的定义中也可以找到的“重复通用模式”:

Class Enum<E extends Enum<E>>


问题:使用这些类时遇到问题。如果我必须停留在层次结构中的城市级别(即将城市连接到城市),我没有问题,但是在尝试访问其他类时遇到很大的问题。

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


addNewNeighbors1a使用原始类型Node,但至少可以使用。
addNewNeighbors1b使用Node类型,但根本不编译(错误包含在代码中)。
addNewNeighbors1c为Node使用了一个更复杂的参数,我希望它可以正常工作,但无法编译(错误包含在代码中)。
addNewNeighbors3对Node使用复杂的参数,但是即使node和newNode的参数相同,也不会再次编译。


综上所述,我的问题是如何对自己参数化的这些泛型类型进行转换?

假设这些方法将位于对City甚至ConcreteNode一无所知的库中,我将非常高兴获得有关GraphUtil方法的最佳签名的帮助。

谢谢你们。

这是示例的完整代码

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

}


运行此代码的输出如下(一点也不奇怪):

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]


问题的第二部分

我认为原始解决方案中也存在一个相关问题,因为我认为该解决方案也将适用。

现在,我向GraphUtil添加了以下方法:

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


从我的主班我正在尝试以下方法:

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


第一种情况有效,因为我知道返回的具体类型,并且它与参数中提供的类型一致。

在第二和第三种情况下,我假设我不知道城市类型。第二种情况可行,但我使用的是原始类型ConcreteNode。

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

在该示例中,我使用'new City(“-”)'作为参数,因为我不知道如何向上转换它们。在第四种情况下,我尝试将City转换为ConcreteNode,但失败了。当前的编译器错误如下:“类型为TestCity.ConcreteNode的方法addNeighbor(capture#4-of?)不适用于参数(TestCity.City)”

问题:


如何在不知道城市类型的情况下修复案例2和3?
如何将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("Add concrete node to node", node.neighbors());
}


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

事实证明,由于它们仅依赖于 Node的接口,因此您只需要其中一个即可处理任何 Node实现。

在将来,您可能会发现有必要像下面这样更改 Node接口:

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

关于java - 递归通用用法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52421962/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com