Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/333.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
两个不同接口类型的变量,但在Java中是同一类的实例_Java_Oop_Inheritance_Interface_Polymorphism - Fatal编程技术网

两个不同接口类型的变量,但在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();