我无法弄清楚为什么我不能使用自引用泛型。
在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);
答案 0 :(得分:1)
与zapl在评论中建议的一样,泛型不提供足够强大的类型保证来处理您所描述的模式。特别是因为Java泛型是非规范的,所以JVM在转换为更通用的类型(OrgIntent
)之后无法恢复更具体的类型(Intent<I, R>
)。由于泛型类型信息在运行时丢失,因此JVM只能依赖于具体的原始类型(Intent
)。
这也是同样的原因,例如,您无法定义具有不同通用签名的两个方法,但具有相同的具体签名 - foo(List<String>)
和foo(List<Integer>)
都变为foo(List)
运行时,因此编译器不允许您在同一个类中定义两个这样的方法。
从广义上讲(我恐怕我不能更好地理解你的用例以便更精确)解决方案是通过关联的Class
object或者{{{{{}}显式地将对象与所需的泛型类型相关联。 {3}}。例如,您可以使用以下签名:
R resolve(Class<I> intentClass, I intent);
TypeToken
中提供的建议也应该有所帮助:
然而,有时候,你需要更多的灵活性[比固定数量的类型参数] ....想法是参数化密钥而不是容器。然后将参数化键呈现给容器以插入或检索值。泛型类型系统用于保证值的类型与其密钥一致。
...
Java的类型系统不足以表达[键和值之间的类型关系]。但我们知道这是真的,当我们找到最喜欢的时候,我们会利用它。