Java中的自引用泛型

时间:2016-06-01 14:50:59

标签: java generics nested-generics

我无法弄清楚为什么我不能使用自引用泛型。

在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;
    }
}

IdentifiableResolutionResolution的一个简单而无趣的实现。

到目前为止一切顺利。然后计划构建这些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);

1 个答案:

答案 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的类型系统不足以表达[键和值之间的类型关系]。但我们知道这是真的,当我们找到最喜欢的时候,我们会利用它。