Java 中的自引用泛型
Self-Referencing Generics in Java
我不明白为什么我不能转换自引用泛型。
在Java中,我有一个自引用泛型。有一堆事情(Intent
s),以及查找(解决)这些事情的策略(ResolutionStrategy
s)。
自引用 Intent
类型定义如下。我想在编译时定义只能接收接受相同意图的 ResolutionStrategy
的 类。
public interface Intent<I extends Intent<I, R>, R extends Resolution>
{
void resolve(ResolutionStrategy<I, R> strategy);
R getResolution();
}
因此解决策略是:
public interface ResolutionStrategy<I extends Intent<I, R>, R extends Resolution>
{
R resolve(I intent);
}
因此,当我对这些 Intent
的列表进行操作时,我并不关心它们是什么。但是,我确实想创建特定类型来表示我的域模型中的具体事物。这是一个例子:
public class OrgIntent implements Intent<OrgIntent, IdentifiableResolution>
{
public final String name;
public OrgIntent(String name)
{
this.name = name;
}
@Override
public void resolve(ResolutionStrategy<OrgIntent, IdentifiableResolution> strategy)
{
// Do stuff
}
@Override
public IdentifiableResolution getResolution()
{
//Return resolution got from strategy at some point in the past
return null;
}
}
IdentifiableResolution
是 Resolution
.
的一个简单而无趣的实现
到目前为止一切都很好。然后计划是为这些 Intent
构建一个漂亮的图表,然后迭代它们,将每个传递给 ResolutionStrategyFactory
以获得解决它们的相关策略。但是,我无法将 OrgIntent
转换为任何足以添加到列表中的通用内容!
private <I extends Intent<I, R>, R extends Resolution> DirectedAcyclicGraph<Intent<I, R>, DefaultEdge> buildGraph(Declaration declaration) throws CycleFoundException
{
DirectedAcyclicGraph<Intent<I, R>, DefaultEdge> dag = new DirectedAcyclicGraph<>(DefaultEdge.class);
// Does not compile
Intent<I, R> orgIntent = new OrgIntent("some name");
// Compiles, but then not a valid argument to dag.addVertex()
Intent<OrgIntent, IdentifiableResolution> orgIntent = new OrgIntent("some name");
// Compiles, but then not a valid argument to dag.addVertex()
OrgIntent orgIntent = new OrgIntent("some name");
//Then do this
dag.addVertex(orgIntent);
...
我应该将 orgIntent
声明为什么?
更新
感谢@zapl,我意识到方法定义中的泛型类型参数完全是在转移注意力。
这可以编译,但可能意味着我可以以某种方式得到一个 Intent
被泛化为第一个泛型类型?
private DirectedAcyclicGraph<Intent<?, ? extends Resolution>, DefaultEdge> buildGraph(Declaration declaration) throws CycleFoundException
{
DirectedAcyclicGraph<Intent<?, ? extends Resolution>, DefaultEdge> dag = new DirectedAcyclicGraph<>(DefaultEdge.class);
OrgIntent orgIntent = new OrgIntent("some name");
dag.addVertex(orgIntent);
就像 zapl 在评论中建议的那样,泛型没有提供足够强大的类型保证来处理您所描述的模式。特别是因为 Java 泛型是非具体化的,所以 JVM 无法在转换为更通用的类型 (Intent<I, R>
) 后恢复更具体的类型 (OrgIntent
)。由于泛型类型信息在运行时丢失,JVM 只能依赖具体的原始类型 (Intent
)。
这是同样的原因,例如,您不能定义具有不同通用签名但具有相同具体签名的两个方法 - foo(List<String>)
和 foo(List<Integer>)
都变得简单 foo(List)
在运行时,因此编译器不允许您在同一个 class.
中定义两个这样的方法
从广义上讲(恐怕我对您的用例的理解还不够准确)解决方案是通过关联的 Class
object or a TypeToken
显式地将对象与所需的泛型相关联。例如,您可能能够使以下签名起作用:
R resolve(Class<I> intentClass, I intent);
Effective Java Item 29: Consider typesafe heterogeneous containers 中提供的建议也应该有所帮助:
Sometimes, however, you need more flexibility [than a fixed number of type parameters].... The idea is to parameterize the key instead of the container. Then present the parameterized key to the container to insert or retrieve a value. The generic type system is used to guarantee that the type of the value agrees with its key.
...
Java's type system is not powerful enough to express [the type relationship between keys and values]. But we know that it’s true, and we take advantage of it when it comes time to retrieve a favorite.
我不明白为什么我不能转换自引用泛型。
在Java中,我有一个自引用泛型。有一堆事情(Intent
s),以及查找(解决)这些事情的策略(ResolutionStrategy
s)。
自引用 Intent
类型定义如下。我想在编译时定义只能接收接受相同意图的 ResolutionStrategy
的 类。
public interface Intent<I extends Intent<I, R>, R extends Resolution>
{
void resolve(ResolutionStrategy<I, R> strategy);
R getResolution();
}
因此解决策略是:
public interface ResolutionStrategy<I extends Intent<I, R>, R extends Resolution>
{
R resolve(I intent);
}
因此,当我对这些 Intent
的列表进行操作时,我并不关心它们是什么。但是,我确实想创建特定类型来表示我的域模型中的具体事物。这是一个例子:
public class OrgIntent implements Intent<OrgIntent, IdentifiableResolution>
{
public final String name;
public OrgIntent(String name)
{
this.name = name;
}
@Override
public void resolve(ResolutionStrategy<OrgIntent, IdentifiableResolution> strategy)
{
// Do stuff
}
@Override
public IdentifiableResolution getResolution()
{
//Return resolution got from strategy at some point in the past
return null;
}
}
IdentifiableResolution
是 Resolution
.
到目前为止一切都很好。然后计划是为这些 Intent
构建一个漂亮的图表,然后迭代它们,将每个传递给 ResolutionStrategyFactory
以获得解决它们的相关策略。但是,我无法将 OrgIntent
转换为任何足以添加到列表中的通用内容!
private <I extends Intent<I, R>, R extends Resolution> DirectedAcyclicGraph<Intent<I, R>, DefaultEdge> buildGraph(Declaration declaration) throws CycleFoundException
{
DirectedAcyclicGraph<Intent<I, R>, DefaultEdge> dag = new DirectedAcyclicGraph<>(DefaultEdge.class);
// Does not compile
Intent<I, R> orgIntent = new OrgIntent("some name");
// Compiles, but then not a valid argument to dag.addVertex()
Intent<OrgIntent, IdentifiableResolution> orgIntent = new OrgIntent("some name");
// Compiles, but then not a valid argument to dag.addVertex()
OrgIntent orgIntent = new OrgIntent("some name");
//Then do this
dag.addVertex(orgIntent);
...
我应该将 orgIntent
声明为什么?
更新
感谢@zapl,我意识到方法定义中的泛型类型参数完全是在转移注意力。
这可以编译,但可能意味着我可以以某种方式得到一个 Intent
被泛化为第一个泛型类型?
private DirectedAcyclicGraph<Intent<?, ? extends Resolution>, DefaultEdge> buildGraph(Declaration declaration) throws CycleFoundException
{
DirectedAcyclicGraph<Intent<?, ? extends Resolution>, DefaultEdge> dag = new DirectedAcyclicGraph<>(DefaultEdge.class);
OrgIntent orgIntent = new OrgIntent("some name");
dag.addVertex(orgIntent);
就像 zapl 在评论中建议的那样,泛型没有提供足够强大的类型保证来处理您所描述的模式。特别是因为 Java 泛型是非具体化的,所以 JVM 无法在转换为更通用的类型 (Intent<I, R>
) 后恢复更具体的类型 (OrgIntent
)。由于泛型类型信息在运行时丢失,JVM 只能依赖具体的原始类型 (Intent
)。
这是同样的原因,例如,您不能定义具有不同通用签名但具有相同具体签名的两个方法 - foo(List<String>)
和 foo(List<Integer>)
都变得简单 foo(List)
在运行时,因此编译器不允许您在同一个 class.
从广义上讲(恐怕我对您的用例的理解还不够准确)解决方案是通过关联的 Class
object or a TypeToken
显式地将对象与所需的泛型相关联。例如,您可能能够使以下签名起作用:
R resolve(Class<I> intentClass, I intent);
Effective Java Item 29: Consider typesafe heterogeneous containers 中提供的建议也应该有所帮助:
Sometimes, however, you need more flexibility [than a fixed number of type parameters].... The idea is to parameterize the key instead of the container. Then present the parameterized key to the container to insert or retrieve a value. The generic type system is used to guarantee that the type of the value agrees with its key.
...
Java's type system is not powerful enough to express [the type relationship between keys and values]. But we know that it’s true, and we take advantage of it when it comes time to retrieve a favorite.