将基类参数传递给具有子类签名的方法

huangapple go评论52阅读模式
英文:

Passing base class paramter to method with subclass signature

问题

我有一个简单的层次结构,BaseDerived 具有它们通常的意义。

class Base{}
class Derived1 extends Base{}
class Derived2 extends Base{}
class Test {
    void method1() {
        Base b1 = new Derived1();
        Base b2 = new Derived2();

        call(b1);
        call(b2);
    }

    void call(Derived1 d){}
    void call(Derived2 d){}
}

为什么 Java 不允许我使用不同类型调用 call 方法?

它显示:Test 类中的 call(Derived1) 方法对于参数 (Base) 不适用

它不能在运行时确定类型并调用适当的方法吗?

英文:

I have a simple heirarchy, Base and Derived have their usual meanings.

class Base{}
class Derived1 extends Base{}
class Derived2 extends Base{}
class Test {
    void method1() {
        Base b1 = new Derived1();
        Base b2 = new Derived2();

        call(b1);
        call(b2);
    }

    void call(Derived1 d){}
    void call(Derived2 d){}
}

Why does java not allow me calling call method with different types?

It says: The method call(Derived1) in the type Test is not applicable for the arguments (Base)

Can't it figure out the type at runtime and call the appropriate method?

答案1

得分: 1

在Java中,方法的解析是静态的,换句话说,是在编译时进行的。

这带来了各种优势。例如,以下代码在编译时会被正确地确定为不可运行的,因此不会编译通过:

Base b3 = new Derived3();
call(b3); // 在Java中无法编译通过。
// 这是正确的,因为根本就没有 call(Derived3) 方法。

更重要的是,Java代码中的每个方法调用在编写时(编译时)就已经确定了。这意味着,例如,您可以在编辑器中对着方法按下 CMD/CTRL+click,直接跳转到它调用的方法。而这不是猜测,这是在更动态的语言如Python或JavaScript中所获得的。这是无可争辩的事实,因为这实际上是写入类文件中的内容。

Java确实具有动态分派:方法的“身份”(这是名称、参数类型、返回类型以及所在类的组合)在写入(编译)时解析并写入类文件中,但实际上选择哪个方法实现,这部分是动态的:

class Base {
    void foo() {
        System.out.println("base");
    }
}

class Child extends Base {
    void foo() {
        System.out.println("child");
    }
}

Base b = new Child();
b.foo(); // 输出 "child"

foo() 方法的“身份”在Base类中查找,因此这段代码会编译通过:Base有一个foo()方法。但在运行时调用的实际实现是动态的,因为b引用的是Child类型的对象,所以最终调用的是Child的实现。

英文:

Method resolution in java is static. In other words, compile time.

This has all sorts of advantages. For example, this code is correctly determined as being unrunnable and will not compile:

Base b3 = new Derived3();
call(b3); // does not compile in java.
// which is correct, as there is no call(Derived3) method at all.

More importantly, every method invocation in java code is known at write time (compile time). This means, for example, that you can CMD/CTRL+click on the method in your editor and hop right to the method that it invokes. And that is not a guess, which is what you get with more dynamic languages like python or javascript. It is unassailable fact, because that is literally what ends up in the class file.

Java DOES have dynamic dispatch: The method's 'identity' (this is a combination of the name, the parameter type(s), the return type, and the class it is in) is resolved at write (compile) time and written in the class file, but which actual implementation of that method ends up being chosen, that part is dynamic:


class Base {
    void foo() {
        System.out.println("base");
    }
}

class Child extends Base {
    void foo() {
        System.out.println("child");
    }
}

Base b = new Child();
b.foo(); // prints "child"

The 'identity' of foo() is looked up on the Base class, and therefore this code compiles: Base has a foo() method. But the actual implementation invoked at runtime is dynamic, and because b is referencing an object of type Child, ends up being Child's implementation of it.

huangapple
  • 本文由 发表于 2020年10月12日 00:12:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/64306264.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定