如何引用接口在 Java 中实现的类类型?

2022-09-02 10:16:01

我在我正在制作的程序中遇到了一个接口问题。我想创建一个接口,其中一个方法接收/返回对自己对象类型的引用。它是这样的:

public interface I {
    ? getSelf();
}

public class A implements I {
    A getSelf() {
        return this;
    }
}

public class B implements I {
    B getSelf() {
        return this;
    }
}

我不能在“?”的地方使用“I”,因为我不想返回对接口的引用,而是返回类。我搜索并发现Java中没有“自我引用”的方法,所以我不能只是用示例中的“?”来代替“self”关键字或类似的东西。实际上,我提出了一个解决方案,如下所示

public interface I<SELF> {
    SELF getSelf();
}

public class A implements I<A> {
    A getSelf() {
        return this;
    }
}

public class B implements I<B> {
    B getSelf() {
        return this;
    }
}

但它似乎真的是一种解决方法或类似的东西。有没有另一种方法可以做到这一点?


答案 1

有一种方法可以在扩展接口时使用自己的类作为参数:

interface I<SELF extends I<SELF>> {
    SELF getSelf();
}

class A implements I<A> {
    A getSelf() {
        return this;
    }
}

class B implements I<A> { // illegal: Bound mismatch
    A getSelf() {
        return this;
    }
}

这甚至在编写泛型类时也有效。唯一的缺点:一个人必须把它扔给SELF

正如Andrey Makarov在下面的评论中指出的那样,这在编写泛型类时不能可靠地工作。

class A<SELF extends A<SELF>> {
    SELF getSelf() {
        return (SELF)this;
    }
}
class C extends A<B> {} // Does not fail.

// C myC = new C();
// B myB = myC.getSelf(); // <-- ClassCastException

答案 2

Java支持协变返回类型,所以这是一种选择。利用以下事实,即两者都派生自:ABObject

public interface I {
    Object getSelf();  // or I, see below
}
public class A implements I {
    A getSelf() { return this; }
}
public class B implements I {
    B getSelf() { return this; }
}

关键是 两者都是 的合法覆盖,即使它们的返回类型不同。这是因为每个都可以被视为 ,因此返回类型与基函数的类型兼容。(这称为“协方差”。A.getSelf()B.getSelf()I.getSelf()AObject

事实上,由于 和 也已知派生自 ,因此可以出于同样的原因将其替换为 。ABIObjectI

协方差通常是一件好事:拥有接口对象类型的人可以调用并获取另一个接口,这就是她需要知道的。另一方面,已经知道他有一个对象的人可以调用并实际上会取回另一个对象。附加信息可用于获取更具体的派生类型,但缺少该信息的人仍然可以获取接口基类规定的所有内容:IgetSelf()AgetSelf()A

I x = new A();
A y = new A();

I a = x.foo();    // generic
A b = y.foo();    // we have more information, but b also "is-an" I
A c = (A)x.foo(); // "cheating" (we know the actual type)