运行时多态性给出错误的输出

时间:2017-06-28 06:57:54

标签: java

据我了解,以下代码应根据我对运行时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";
    }
}

有人可以帮助我理解这种行为。

6 个答案:

答案 0 :(得分:2)

预期输出确实为b

当您将对象a声明为类型A1时,该类仅定义方法的接口。它定义m1返回一个String,但该方法的实现由用于构建对象的Class定义,即Test1Test1扩展B1,它会覆盖方法m1,因此这是用于您的对象的m1的实现。

该来电m1()的输出必须确实是B1

编辑:这个答案是针对问题的第一个版本编写的。 OP改变了很多代码,但解释的根源仍然相同。

答案 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课程。 mCm1继承自B1的{​​{1}}。在这种情况下," C声明mC"是错误的,因为C1没有声明m1,它会继承它。但是,我相信" mA是C"的直接超类的成员。是真的。据我了解,B1拥有A1拥有的所有成员。 B1声明了自己的m1,由于它没有覆盖,因此新的m1会导致m1A1继承mA } 隐藏。但即使它被隐藏了,它仍然是一个成员。因此,满足CB1的直接超类(即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
}

这将输出:

  • 一个
  • B'/ LI>

答案 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());
    }
}

显然输出将是

怒吼

我希望这会有所帮助。