据我了解,以下代码应根据我对运行时a
的了解打印polymorphism
。
但是,当我运行以下代码时,它正在打印b
:
根据JLS 8.4.8.1,B1.m1不会覆盖A1.m1,所以当A1.m1是 调用时,不应选择B1.m1
package a;
public interface I1 {
public Object m1();
}
public class A1 {
Object m1() {
return "a";
}
}
public class C1 extends b.B1 implements I1 {
public static void main(String[] args) {
a.A1 a = new a.C1();
System.out.println(a.m1());
}
}
package b;
public class B1 extends a.A1 {
public String m1() {
return "b";
}
}
有人可以帮助我理解这种行为。
答案 0 :(得分:2)
预期输出确实为b
。
当您将对象a
声明为类型A1
时,该类仅定义方法的接口。它定义m1
返回一个String,但该方法的实现由用于构建对象的Class定义,即Test1
。 Test1
扩展B1
,它会覆盖方法m1
,因此这是用于您的对象的m1
的实现。
该来电m1()
的输出必须确实是B1
。
答案 1 :(得分:2)
添加软件包后,问题就更加困难了。我试过这个,然后我将主程序改为
public class C1 extends b.B1 implements I1 {
public static void main(String[] args) {
a.A1 a = new a.C1();
System.out.println(a.m1());
a.A1 b = new b.B1();
System.out.println(b.m1());
}
}
(我实际上使用了不同的包名来避免与变量名冲突。所以我的代码与上面的代码有点不同。)
我已经确认这打印" b"和" a"。也就是说,如果我创建新的B1
,则其m1
方法不会覆盖A1
中的方法。因此,如果我打印b.m1()
,因为b
的类型为A1
,多态性不会启动,并且会调用A1
中声明的方法。那么C1
会发生什么?
C1
继承m1
的{{1}}方法。但即使B1
中的m1
方法没有覆盖B1
中的A1
方法,m1
中的C1
方法,它继承自B1
{1}},实际上会覆盖A1
中的那个。我认为这与8.4.8.1中的这个条款有关:
mA在与C相同的包中声明包访问,并且C声明mC或mA是C的直接超类的成员。
此处C
是您的C1
课程。 mC
是m1
继承自B1
的{{1}}。在这种情况下," C声明mC"是错误的,因为C1
没有声明m1
,它会继承它。但是,我相信" mA是C"的直接超类的成员。是真的。据我了解,B1
拥有A1
拥有的所有成员。 B1
声明了自己的m1
,由于它没有覆盖,因此新的m1
会导致m1
从A1
继承mA
} 隐藏。但即使它被隐藏了,它仍然是一个成员。因此,满足C
是B1
的直接超类(即m1
)的成员的条件,因此满足8.4.8.1的所有条件,因此继承{ C1
中的{1}}会覆盖A1
中的{1}}。
答案 2 :(得分:1)
以下行A1 a = new Test1();
仅表示构建Test1
实例并将其存储在A1
框中。
因此实例将是Test1
,但您只能访问A1
的方法/变量,但会访问Test1
中的每个覆盖方法。
这是多态的。
阅读有关访客的JLS about 8.4.8.1. Overriding (by Instance Methods)
在C类中声明或继承的实例方法mC,覆盖C类中声明的另一个方法mA,iff以下所有条件都为真:
- A是C的超类。
- mC的签名是mA签名的子签名(§8.4.2)。
- mA是公开的。
您可以在8.4.8.3. Requirements in Overriding and Hiding
中找到有关访问修饰符的更多信息覆盖或隐藏方法的访问修饰符(第6.6节)必须至少提供与重写或隐藏方法一样多的访问权限,如下所示:
- 如果被覆盖或隐藏的方法是公开的,那么覆盖或隐藏方法必须是公开的;否则,发生编译时错误。
- 如果被覆盖或隐藏的方法受到保护,则必须保护或隐藏覆盖或隐藏方法;否则,发生编译时错误。
- 如果重写或隐藏方法具有包访问权限,则覆盖或隐藏方法不得为私有方法;否则,发生编译时错误。
编辑:
现在,添加了您的包。
让C1
实现m1
(因为界面),你隐藏了A1
的方法和你在B1
中找到的实现,这个方法确实是接口契约的有效定义。
您可以看到您没有覆盖该方法(您无法在super.m1
上致电@Override
或甚至添加B1.m1
。但是来电a.m1()
有效它是在类本身中定义的。
答案 3 :(得分:0)
你是最重要的。包含@Override
注释,您可以看到。只要您的类扩展可以覆盖父类方法,您就可以增加访问权限,但不会减少访问权限。
如果您尝试将B#m1
设为私有,那么有人可以转换为A
并使用该方法。
相反,如果您将A#m1
设为私有,则B
无法覆盖它,您最终可能会得到一个具有相同签名的两个方法的对象。
static class A{
private String get(){
return "a";
}
}
static class B extends A{
public String get(){
return "b";
}
}
public static void main (String[] args) throws java.lang.Exception
{
A b = new B();
System.out.println(b.get());
System.out.println(((B)b).get());
// your code goes here
}
这将输出:
答案 4 :(得分:0)
您有一个I1
接口,由A1
实现
Class B1 extends A1
Class C1 extends B1
(因此隐含extends A1
)。
因此C1
的实例也属于B1, A1 & I1
类型,但无论您是否将其分配给其他类型之一,它仍然是C1
的实例。
如果你有:
I1 instance = new C1();
String value = instance.m1();
将调用从真实类型(m1
)上升继承树的第一个C1
方法,该方法将在B1中并返回“b”。
答案 5 :(得分:0)
所有评论和答案大多都是正确的。他们用语言机制来解释事物。相反,我认为要实现继承和多态的真正含义以及如何使用它们,你必须采取更概念化的方法。
首先,继承是两个事物之间的关系,关系的类型为“是”。换句话说,当您编写语句类C1扩展B1时,您的意思是 C1是B1 。 当然,这不适用于A1,B1和C1。让我用更真实的东西来改变它们。例如:
A1 =动物
B1 =猫科动物
C1 = Cat和C2 =狮子(多态)
在这一点上,你将有猫类延伸猫科动物,你可以在概念上将它读作:猫是猫科动物。我建议使用“是一个”测试来挑战你的继承正式正确性。如果它不起作用,最好重新考虑或重新考虑继承。 生成的代码如下所示:
public interface IAnimal {
public Object saySome();
}
public class Animal {
Object saySome() {
return "I am an animal";
}
}
public class Feline extends Animal {
public String saySome() {
return "I am a feline";
}
public class Cat extends Feline implements IAnimal {
Object saySome() {
return "meow";
}
}
public class Lion extends Feline implements IAnimal {
Object saySome() {
return "roar";
}
}
class Aplication {
public static void main(String[] args) {
Animal anAnimal = new Cat();
Animal anotherAnimal = new Lion();
System.out.println(anAnimal.saySome());
System.out.println(anotherAnimal.saySome());
}
}
显然输出将是
喵
怒吼
我希望这会有所帮助。