5

我不知道为什么我不能使用自引用泛型。

在 Java 中,我有一个自引用泛型。有很多事情Intents),以及查找(解决)这些事情的策略(ResolutionStrategys)。

自引用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);
}

因此,当我对这些Intents 的列表进行操作时,我并不真正关心它们是什么。但是,我确实想在我的领域模型中创建代表具体事物的特定类型。这是一个例子:

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

到目前为止一切都很好。然后计划是为这些Intents 构建一个漂亮的图,然后迭代它们,将每个传递给 aResolutionStrategyFactory以获得解决它们的相关策略。但是,我不能将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);
4

1 回答 1

2

就像 zapl 在评论中建议的那样,泛型没有提供足够强大的类型保证来处理您描述的模式。特别是因为 Java 泛型是未具体化的,所以 JVMOrgIntent在将其转换为更通用的类型 ( ) 后无法恢复更具体的类型 ( Intent<I, R>)。由于泛型类型信息在运行时丢失,JVM 只能依赖具体的原始类型 ( Intent)。

这是相同的原因,例如,您不能定义具有不同通用签名但具有相同具体签名的两个方法 -foo(List<String>)并且foo(List<Integer>)两者都在运行时变得简单foo(List),因此编译器不允许您在同一个中定义两个这样的方法班级。

从广义上讲(恐怕我对您的用例理解得不够好,无法更准确),解决方案是通过关联Class对象TypeToken. 例如,您可能能够使以下签名起作用:

R resolve(Class<I> intentClass, I intent);

Effective Java Item 29中提供的建议:考虑类型安全的异构容器也应该有帮助:

然而,有时,您需要[比固定数量的类型参数] 更大的灵活性......这个想法是参数化键而不是容器。然后将参数化键呈现给容器以插入或检索值。泛型类型系统用于保证值的类型与其键一致。

...

Java 的类型系统不足以表达[键和值之间的类型关系]。但我们知道这是真的,我们会在检索收藏夹时利用它。

于 2016-06-02T04:36:57.813 回答