两个接口类型不同的变量,但在 Java 中是同一类的实例



不同interface类型的两个变量之间的difference是什么,但实际上用same object实例化。

例如,类 A 实现两个接口 I1 I2。

interface I1 {}
interface I2 {}
class A implements I1, I2 {
...
} 

然后,我将类 A 的实例强制转换为接口类型 I1,并将另一个 A 转换为类型 I2

I1 a1 = new A();
I2 a2 = new A(); 

a1a2有什么区别?类型I1a1将无法调用声明接口的方法I2?

a1a2是不同的对象。

例如:

interface I1 {
public void derp();
}
interface I2 {
public void herp();
}

而你有

I1 a1 = new A();
I2 a2 = new A(); 

a1是指类实现I1因此a1只能调用I1中定义的方法,该方法derp()。它不能调用derp(),因为derp()是在I2中定义的。出于同样的原因,a2只能打电话给herp(),而不能打电话derp()

注意:虽然a1a2是不同的对象,但如果class A实现I1I2中的所有方法,那么a1a2可以执行相同的操作,它们就不会有区别。 但它们仍然是两个不同的对象

a1a2都指A类型的对象。 区分变量的"声明类型"和它引用的对象的实际类型(如果变量不为 null(非常重要。

当您声明时

I1 a1 = <anything>;

或者只是

I1 a1;

编译器唯一知道的关于a1是它必须引用实现I1的某个对象。 因此,只有在为接口I1(或超接口或Object(声明方法时,才能调用方法a1.method(...)。 此外,a1只能分配给声明为I1的另一个变量(或超接口或Object(,并且如果您将其用作参数

x.someOtherMethod(a1);

仅当参数的类型为I1、 超接口或Object时,它才是合法的。

请注意,将其初始化为new A()并没有什么区别。 出于"允许编译器了解变量的信息"的目的,不考虑初始表达式。 (您可以使用将a1分配给不同类型的对象的代码。 但即使变量final,也适用相同的规则。

但是,您可以通过强制转换让编译器将其视为另一种类型。 将接口变量强制转换为任何其他接口始终是合法的,因为某个类在某个地方始终有可能实现这两个接口。 因此,这是合法的:

I2 x = (I2)a1;

在运行时,编译器将检查a1是否引用除了I1之外还实现I2的对象。 如果是这样,则演员表成功。 否则,将引发异常。 假设没有例外,(I2)a1现在可以以任何其他I2的任何方式使用。 因此,如果I2定义了一个方法methodOfI2,你可以说

((I2)a1).methodOfI2(...parameters...)

是的,你得到了答案。就像你说的,你只能在 a1 上调用 I1 中定义的方法,并且只能调用 I2 中为 a2 定义的方法。

用一个具体的例子来考虑它:

public class PetDog implements FeedableAnimal, PettableAnimal {
...
}
...
FeedableAnimal dog = new PetDog();

现在,如果我将 PetDog 实例化为 FeedableAnimal,我就无法调用 PettableAnimal 中定义的任何方法。那是因为PetDog现在的行为就像可喂食的动物。我们无法知道PetDog是Pettable的,因为我们没有将其实例化为PettableAnimal。

术语澄清:我们不会将宠物狗"投射"为可喂食动物。我们只是说,在编译时,狗的行为就像一个可喂食的动物。在运行时,dog 仍然采用 PetDog 中定义的行为。

最新更新