19

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

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

但这似乎真的是一种解决方法或类似的东西。还有其他方法吗?

4

4 回答 4

13

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

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

这甚至在编写泛型类时也有效。唯一的缺点:必须强制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
于 2013-05-08T20:11:40.143 回答
7

Java 支持协变返回类型,所以这是一种选择。利用AB都源自的事实Object

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(),即使它们的返回类型不同。那是因为 eachA可以被视为一个Object因此返回类型与基函数的返回类型兼容。(这称为“协方差”。)

事实上,由于AB也被认为是从 派生的,因此出于同样的原因I,您可以替换Object为。I

协变通常是一件好事:拥有接口对象类型的人I可以调用getSelf()并获取另一个接口,这就是她需要知道的全部内容。另一方面,已经知道他有一个A对象的人可以调用getSelf()并且实际上会取回另一个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)
于 2011-11-17T03:32:51.983 回答
3

我想知道是否还有其他方法可以做到这一点?

可以这样写:

public interface I {
   I getSelf();
}

然后将结果转换为您想要的类型。您现有的课程AB课程将按原样工作。

(这是返回类型协方差的一个示例。Java 5 中添加了对返回类型协方差的支持。这种方法会在较旧的 JDK 中产生编译错误。)

使用泛型的替代版本(您称其为解决方法,但实际上并非如此)允许您避免显式类型转换。但是,生成的代码中存在隐式类型转换,并且在运行时......除非 JIT 编译器可以优化它。

没有更好的选择,AFAIK。

于 2011-11-17T03:30:45.547 回答
1

正如其他人所说,您可以覆盖实现类中的返回类型:

public interface I {
    public I getSelf();
}

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

但是,我有两个“为什么”问题要问您:

1:为什么要一个接口返回实现对象?对我来说,这似乎违背了接口和继承的一般思想。你能举例说明如何使用它吗?

2:无论如何,你为什么要这个功能?如果 a.getSelf() == a,为什么不直接使用 a?

于 2011-11-17T03:51:02.803 回答