我有两个班级:
public class ClassA {
public void method(Number n) {
System.out.println("ClassA: " + n + " " + n.getClass());
}
}
和
public class ClassB extends ClassA {
public void method(Integer d) {
System.out.println("ClassB: " + d + " " + d.getClass());
}
}
但是当我跑步时:
ClassA a = new ClassB();
a.method(3);
我明白了:
ClassA: 3 class java.lang.Integer
我的问题是,为什么不使用ClassB
的方法? a
是ClassB
的一个实例,而ClassB
的{{1}}有一个method()
参数......
答案 0 :(得分:24)
我的问题是,为什么不使用ClassB的方法?
不正确。使用的方法是ClassB
的方法,它继承自ClassA
。
我认为混淆背后的主要原因是该方法实际上不是重写,而是重载。虽然Integer
是Number
的子类型,但由于方法参数在Java中是不变的,因此方法public void method(Integer d)
不会覆盖方法public void method(Number n)
。因此,ClassB
最终会有两个(重载)方法。
静态绑定用于重载方法,编译器选择具有最特定参数类型的方法。但在这种情况下,为什么编译器选择public void method(Number n)
而不是public void method(Integer d)
。这是因为您用于调用方法的引用属于ClassA
类型。
ClassA a = new ClassB(); //instance is of ClassB (runtime info)
a.method(3); //but the reference of type ClassA (compiletime info)
ClassA
唯一的方法是public void method(Number n)
,这就是编译器选择的方法。请记住,此处预期的参数类型为Number
,但传递的实际参数(整数3)将自动装入类型Integer
。它起作用的原因是因为方法参数在Java中是协变的。
现在,我认为它打印的原因很清楚
ClassA:3类java.lang.Integer
答案 1 :(得分:10)
你的问题源于这样一个事实(正如关于继承的官方Java教程所引用的那样):
在子类中,您可以重载从超类继承的方法。这种重载方法既不隐藏也不覆盖超类方法 - 它们是新方法,对于子类来说是唯一的。
有关更多详细信息,请参阅官方Java教程: http://docs.oracle.com/javase/tutorial/java/IandI/override.html
答案 2 :(得分:6)
a类型为ClassA,因此ClassB 中的方法对实例a不可见,除非它被声明为ClassB
ClassB a = new ClassB();
将产生您的预期输出。 Number是Integer的超类型。因此,无论您传入的是什么,都会被自动装箱到适当的子类型,并且将调用ClassA中的方法。尝试传递
a.method(3.0f) // Float
a.method(3.0) // Double
答案 3 :(得分:4)
因为参数中的Number和Integer创建了两个不同的方法签名。因此,B类只有两种不同的方法可供使用。
答案 4 :(得分:4)
因为数字3会自动装箱为整数。
请看以下链接: http://www.javabeat.net/articles/print.php?article_id=31
一般规则:隐式扩展参数以匹配方法参数。 从一个包装类扩展到另一个包装类是不合法的。
答案 5 :(得分:1)
由于这两个操作具有不同的参数(参数)类型(即使它们是子类),它们被认为是不同的(与C不同),您没有用第二个方法覆盖第一个方法。相反,你最终得到了B类,现在有两种方法
public void method(Number n) and
public void method(Integer n)
默认情况下执行a.method(3)时3会被转换为Integer对象。 您可以通过调用
来验证这一点a.method((Number)3); //this would call the second method/operation.
您还可以通过使用反射来迭代B类的方法来验证这一点。
答案 6 :(得分:1)
class ClassA
{
public void method( Number n )
{
System.out.println( "ClassA: " + n + " " + n.getClass() );
}// void method( Number n )
}// class ClassA
public class ClassB
extends
ClassA
{
public void method( Integer d )
{
System.out.println( "ClassB: " + d + " " + d.getClass() );
}// void method( Integer d )
public static void main( String[] args )
{
ClassB b = new ClassB();
ClassA a = b;
a.method( new Integer( 3 )); // 1. ClassA: 3 class java.lang.Integer
b.method( new Integer( 4 )); // 2. ClassB: 4 class java.lang.Integer
b.method( new Float( 5.6 )); // 3. ClassA: 5.6 class java.lang.Float
}// void main( String[] args )
}// class ClassB
答案 7 :(得分:1)
要清除我在show()
和classA
中添加的classB
方法。
public void show() {
System.out.println(getClass());
}
我这样打电话,
// Case 1
ClassA a = new ClassB();
a.method(3);// ClassA: 3 class java.lang.Integer
a.show(); // class ClassB
// Case 2
ClassB b = new ClassB();
b.method(3);// ClassB: 3 class java.lang.Integer
b.show(); // class ClassB
这里方法(数字n)和方法(整数d)具有不同的签名。 它不是压倒一切。它正在超载。
但show()方法是方法覆盖。
在案例1中, 只有对象a可以访问A类的方法。 a是类型classA,类B中的方法不可见。 这就是你调用classA方法的原因。 但是对于show()方法,因为它是重写方法,所以调用类B的show()方法。
在案例2中, A类和B类的两种方法都可以通过对象b访问,因为ClassB扩展了ClassA。
答案 8 :(得分:0)
您有以下代码
Class A a = new ClassB();
a.method(3);
但是想象一下你有一个方法,其中“a”和“3”作为参数传递给你,你仍然执行相同的代码
public void foo(A a, Number n)
{
a.method(n);
}
编译器不知道您是要传递A类还是B类(或数字或整数)。它仍然必须解析该方法,以便它可以从a.method
进行返回值的类型检查答案 9 :(得分:0)
我在这个问题上做了一些R& D并提出解决方案来消除你的困惑。希望它能帮助你理解。
查找以下代码:
class A {
public void func(Number obj){
System.out.println("In a func");
}
public void func(Integer obj){
System.out.println("In b func");
}
}
class B extends A {
}
public class X {
public static void main(String s[]){
B b = new B();
b.func(3);
A a = new B();
a.func(3);
}
}
如果您将运行此代码,您将获得输出:
"在b func"
"在b func" 。
在这种情况下,这里有4种方法:
现在当你在B的引用上调用b.func(3)时,它将看到"方法3"和"方法4",其中包含最适合派生类的参数。这里,Number和Integer类都适用于参数 3 ,但Integer是从Number派生的,因此将调用func(Integer)[方法3]。因此,输出是"在b func"
第二个输出也是"在b方法"中,因为相同的逻辑。 首先要记住,你不能在A类没有的A类引用上调用任何方法。所以你只能在它所拥有的A类的引用上调用这些方法。无论实例是A类还是它的子类实例。
你需要用2个术语来理解它,编译,链接和执行。
现在,A类同时具有这两种方法,因此当编译器在类A的引用上查看a.func(3)时,编译器将查看"方法1"和"方法2" A类并绑定方法签名,该签名具有最适合派生类的参数。所以" func(整数)"。
现在在运行时,将执行func(Integer),它被调用为B类,因为实例是B类。(在运行时,方法是从类执行的,其实例调用方法)。因此调用方法4。因此输出。
我确信,你会感到困惑,为什么不调用方法2而调用方法4。
如果您将运行以下代码:
class A {
public void func(Number obj){
System.out.println("In a func");
}
public void func(Integer obj){
System.out.println("In b func");
}
}
class B extends A {
public void func(Number obj){
System.out.println("In a func of class B");
}
public void func(Integer obj){
System.out.println("In b func of class B");
}
}
public class X {
public static void main(String s[]){
B b = new B();
b.func(3);
A a = new B();
a.func(3);
}
}
输出将是:
在B级的b函数中
在B级b函数中
现在您可以通过上述说明理解此代码。我们要么在引用A类或B类时调用fun(3)。每次调用B类方法时(方法4)。因为实例属于B类。但是如果A类没有(方法2)。方法4不会在" a.func(3)"
上调用让我们看看下面的代码:
class A {
public void func(Number obj){
System.out.println("In a func");
}
}
class B extends A {
public void func(Integer obj){
System.out.println("In b func");
}
}
public class X {
public static void main(String s[]){
B b = new B();
b.func(3);
A a = new B();
a.func(3);
}
}
该计划的输出是:
在b func中
在一个功能
现在你可能会感到困惑为什么输出不同?
请记住,这里不是4种方法。这里只有3种方法:
现在如果你调用a.fun(3),A类没有func(Integer),你就不能在类A的引用上调用哪个类没有。因此编译器不会绑定func(Integer),因为A类中没有这样的方法。但是还有另一种方法func(Number)可以使用相同的代码调用a.func(3)java的自动装箱概念。
所以当a.func(3)调用时,它基本上调用func(Number)。现在因为实例是B类,所以调用B类的方法func(Number)。因此输出是"在函数中#34;
这是一个非常大的答案,但我深入解释,因此您可以轻松了解不同用例中输出的不同可能性。
享受编码!
答案 10 :(得分:-1)
在重载时,方法解析总是由编译器根据引用类型来处理。因此,在重载运行时对象[new ClassB()]不起任何作用。 因此,在您的情况下,执行了ClassA的方法。
ClassA a = new ClassB();
a.method(3);