【问题标题】:Java static and dynamic binding, upcast, overloading mixed togetherJava 静态和动态绑定、向上转换、重载混合在一起
【发布时间】:2013-12-01 14:14:23
【问题描述】:

假设我们有以下代码

class TestEqual{
    public boolean equals(TestEqual other ) {
    System.out.println( "In equals from TestEqual" ); return false;
}

    public static void main( String [] args ) {
        Object t1 = new TestEqual(), t2 = new TestEqual();
        TestEqual t3 = new TestEqual();
        Object o1 = new Object();

        int count = 0;
        System.out.println( count++ );// shows 0
        t1.equals( t2 ) ;
        System.out.println( count++ );// shows 1
        t1.equals( t3 );
        System.out.println( count++ );// shows 2
        t3.equals( o1 );
        System.out.println( count++ );// shows 3
        t3.equals(t3);
        System.out.println( count++ );// shows 4
        t3.equals(t2);
    }
}

基本上,在 TestEqual 类(当然,扩展 Object)中,我们有一个从 Object 重载 equals 方法的方法。

此外,我们还有一些变量:Object t1、t2 实例化为 TestEqual、TestEqual t3 实例化为 TestEqual 和 Object o1 实例化为 Object。

如果我们运行程序,这将是输出。

0
1
2
3
In equals from TestEqual
4

这个例子似乎比通常的 Car c = new Vehicle(); 复杂一些。 c.驱动器();因为我们调用该方法的对象的实例化与其类型不同,而且方法的参数实例化与其类型不同。

我想检查我是否正确理解了当我们调用每个方法时会发生什么,关于绑定的逐步说明。

 show 0
 t1.equals(t2)
 show 1

t1 被视为一个 TestEqual 对象。方法equals被重载了,所以绑定是静态的,这意味着我们将t2作为一个Object传递,所以它会调用继承自Object超类的equals方法,所以它不会显示任何文本。

 show 1
 t1.equals(t3)
 show 2

这似乎有点奇怪。我本来希望显示“来自 TestEqual 的等于”,因为 t3 是一个 TestEqual 对象,所以应该调用来自 t1 的等于。我在这里的解释是 t1 是静态绑定的并且被认为是一个对象,所以从 Object 类继承的方法 equals 被调用,参数 TestEqual t3 被向上转换为 Object。 但这不就是说前面 t1.equals(t2) 的解释是错误的吗?

show 2
t3.equals(o1);
show 3

t3是TestEqual对象,参数o1是Object,所以调用了继承自Object的equals方法,所以什么也不打印。

show 3
t3.equals(t3)
show 4

t3是一个TestEqual对象,参数是一个TestEqual对象,所以会调用TestEqual的重载方法,打印“In equals from TestEqual”。

show 4
t3.equals(t2)

t3是TestEqual对象,由于静态绑定(方法重载),参数是Object,所以调用继承自Object的equal方法,不打印。

【问题讨论】:

    标签: java inheritance dynamic-binding static-binding


    【解决方案1】:

    这似乎有点奇怪。我本来希望显示“在等于 TestEqual",因为 t3 是一个 TestEqual 对象,所以来自 t1 的等于 应该调用。我在这里的解释是 t1 是静态绑定的 并被视为一个对象,因此该方法等于继承自 调用 Object 类,参数 TestEqual t3 向上转换为 Object。但这不就是说前面 t1.equals(t2) 的解释是错误的吗?

    要在重载上下文中调用方法,最具体的方法调用发生在编译时确定。选择最具体方法的规则在java language specification 15.12.2.5. Choosing the Most Specific Method中定义:与其他讨论,提到的声明是:

    如果和 仅当 m1 比 m2 更具体且 m2 不比 m2 更具体时 米1。

    为了解释你的上下文,让我们声明两个简单的 Super 类和 sup 类:

    class SuperA
    {
        public void test(SuperA a)
        {
            System.out.println("super class's test() is called");
        }
    }
    
    class SubB extends SuperA
    {
    
        public void test(SubB subB)
        {
            System.out.println("subclass's test() is called");
    
    
        }    
    }
    

    现在,如果我们这样创建两个实例:

    SuperA obj = new SubB();
    SubB obj2 = new SubB();
    obj.test(obj2);
    

    您会看到调用了超类的test(),因为它在编译时被确定为更具体,并且编译器看到objSuperA 类型的实例。现在将obj 转换为SuubB 并调用test(obj2)

    ((SubB)obj).test(obj2); // cast to SubB
    

    它打印:"subclass's test() is called" 暗示它调用了SubBtest(obj) 方法,因为这次编译器知道obj 的类型为SubB 和调用test 的最具体的分辨率。

    但是,现在让我们以这种方式声明两个实例:

       SuperA obj = new SubB();
       SuperA obj2 = new SubB();
       obj.test(obj2); // invokes super class's test method
       ((SubB)obj).test(obj2);// invokes super class's test method
       ((SubB)obj).test((SubB)obj2); // invoke sub class's test method
    

    在这一系列调用中,前两条语句会调用超类SuperA的测试方法,因为这些调用更具体。然而,要解释第二种情况:

    ((SubB)obj).test(obj2);// invokes super class's test method
    

    这次编译器知道obj 具有SubB 的类型,但它仍然看到obj2 具有SperA 的类型,这对于test 方法调用更具体。因此,对于第二个示例,其中 objobj2 都声明为 SuperA 类型:我们需要将它们都转换为 SubB 以调用 SubBtest 方法。

    由于Object是java中所有类的超类,你现在应该明白了,但是你的上下文的equal方法调用系统。为了避免这种调用陷阱,你会看到java类中所有实现的equal方法实际上是equal方法Object类的override,并利用instanceof检查.比如Integer类的equal方法实现:

    public boolean equals(Object obj) {
            if (obj instanceof Integer) { //<<<---- instance of checking
                return value == ((Integer)obj).intValue();
            }
            return false;
        } 
    

    【讨论】:

      【解决方案2】:

      方法Object.equals(Object obj) 将另一个Object 实例作为参数。 如果您将TestEqual 定义为:

      class TestEqual{
          @override
          public boolean equals(Object other ) {
              System.out.println( "In equals from TestEqual" ); return false;
          }
      }
      

      它将按您的预期工作。

      【讨论】:

      • 是的,它会按预期工作,因为我们覆盖了 Object.但我想听到的是,如果我正确理解了当我们调用每个方法时会发生什么,逐步关于绑定。换句话说,我对每个电话的解释正确吗?
      • 我认为这个人根本没有回答罗伯特的问题。我们知道正确的方法,但他希望得到解释。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多