Java 中的自引用泛型

Self-Referencing Generics in Java

我不明白为什么我不能转换自引用泛型。

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

因此,当我对这些 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);

就像 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.