两个不同接口类型的变量,但在Java中是同一类的实例
不同两个不同接口类型的变量,但在Java中是同一类的实例,java,oop,inheritance,interface,polymorphism,Java,Oop,Inheritance,Interface,Polymorphism,不同接口类型的两个变量之间的差异是什么,但实际上是用同一对象实例化的 例如,类A实现了接口I1和I2 interface I1 {} interface I2 {} class A implements I1, I2 { ... } 然后我将类A的一个实例转换为接口类型I1,并将另一个实例转换为类型I2 I1 a1 = new A(); I2 a2 = new A(); a1和a2之间有什么区别?类型为I1的a1是否将无法调用接口I2?声明的方法a1和a2都引用类型为A的对象。区分变量的
接口
类型的两个变量之间的差异是什么,但实际上是用同一对象实例化的
例如,类A实现了接口I1和I2
interface I1 {}
interface I2 {}
class A implements I1, I2 {
...
}
然后我将类A的一个实例转换为接口类型I1,并将另一个实例转换为类型I2
I1 a1 = new A();
I2 a2 = new A();
a1
和a2
之间有什么区别?类型为I1
的a1
是否将无法调用接口I2?
声明的方法a1
和a2
都引用类型为A
的对象。区分变量的“声明类型”和它引用的对象的实际类型(如果变量不是null)很重要
当你申报时
I1 a1 = <anything>;
编译器只知道a1
必须引用实现I1
的某个对象。因此,如果方法是为接口I1
(或超级接口,或对象
)声明的,则只能调用方法a1.method(…)
)。此外,a1
只能分配给另一个声明为I1
(或超级接口,或对象
)的变量,并且如果将其用作参数
x.someOtherMethod(a1);
仅当参数的类型为I1
、超级接口或对象时才合法
请注意,将其初始化为newa()
并没有什么区别。出于“允许编译器了解变量的内容”的目的,不考虑初始表达式。(您可以使用代码将a1
分配给不同类型的对象。但即使变量为final
,也适用相同的规则)
但是,可以通过强制转换使编译器将其视为另一种类型。将接口变量强制转换为任何其他接口都是合法的,因为总有可能某个类在某个地方实现这两个接口。因此,这是合法的:
I2 x = (I2)a1;
在运行时,编译器将检查a1
是否引用了除I1
之外还实现了I2
的对象。如果是这样,演员阵容就会成功。如果不是,则引发异常。假设没有例外,(I2)a1现在可以以任何其他I2
可以的任何方式使用。因此,如果I2
定义了一个方法methodOfI2
,您可以说
((I2)a1).methodOfI2(...parameters...)
是的,你知道答案了。正如您所说,您只能在a1上调用I1中定义的方法,而在a2上只能调用I2中定义的方法
用一个具体的例子来思考:
public class PetDog implements FeedableAnimal, PettableAnimal {
...
}
...
FeedableAnimal dog = new PetDog();
现在,如果我将PetDog实例化为可喂养动物,我不能调用PettableAnimal中定义的任何方法。这是因为宠物狗现在的行为就像一只可喂养的动物。我们无法知道宠物狗是可宠物的,因为我们没有将它实例化为可宠物动物
术语澄清:我们不是把宠物狗“扔”给可喂养的动物。我们只是说,在编译时,狗的行为就像一个可喂养的动物。在运行时,dog仍然承担PetDog中定义的行为。a1
和a2
是不同的对象。
例如:
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()
注意:虽然a1
和a2
是不同的对象,但是如果A类
实现了I1
和I2
中的所有方法,那么a1
和a2
就可以起相同的作用。但它们仍然是两个不同的对象a1和a1之间没有区别。a1和a2之间有一个区别,特别是可以调用哪些方法。请注意,强制转换总是将视图缩减为对象。它实际上不会更改对象的类型。例如,如果您执行类似于System.out.println(a1.getClass())
的操作,它将读取类似于class A
的内容,尽管您已将其强制转换为I1
。但是,减少视图有利于模块化,因为如果您只需要I1
,那么您将来总是可以轻松地将对象与I1
的另一个实现交换。此外,编译器还阻止您在将I1
转换为I1
后调用I2
的方法,因为它无法在编译时确定I1
也是I2
。因此,在压缩格式中:a1
和a2
,即使在转换之后,仍然保持A
、I1
和I2
。但是,在强制转换后,实际类型是隐藏的,编译器会阻止您在不进行强制转换的情况下执行不安全的方法调用。我建议您为I1和I2c提供不同的方法,并使用计算机允许您执行的操作。然后想想为什么它会让你做或不做某些事情。说宠物狗像可喂养的动物一样是不准确的。说dog
像feedable animal
更准确PetDog
将始终具有两个接口的方法。我们无法知道PetDog是可移植的-我们确实有一种方法-实例(以及多种其他方法)
I1 a1 = new A();
I2 a2 = new A();