在 Java 中向上转换子类引用

Upcasting a subclass reference in Java

我正在 Thinking in Java 第 4 版 中做以下练习,作者 Bruce Eckel:

Exercise 16: (2) Create a class called Amphibian. From this, inherit a class called Frog. Put appropriate methods in the base class. In main(), create a Frog and upcast it to Amphibian and demonstrate that all the methods still work.

Frog f = new Frog();Amphibian f = new Frog();有什么区别:

class Amphibian {
    void go() { System.out.println("go"); }
    void stop() { System.out.println("stop!"); }
}

class Frog extends Amphibian {
    void go() { System.out.println("not go"); }
}

public class EFrog {
    public static void main(String[] args) {
        Frog f = new Frog();
        f.go();
    }
}

在这个简单的程序中,其实并没有什么区别。但是如果你有一个 Amphibian f,它可以指代任何两栖动物,而不仅仅是 Frog。您仍然使用 Amphibian 提供的所有方法,即使它是 Frog 的实例 有关多态性的描述,请参阅 What is polymorphism, what is it for, and how is it used?

这就是我们所说的多态性。那是许多形状。同一物体的多种形状。正如您所了解的,同一对象可以称为青蛙,也可以称为两栖动物。当 referral 为 Amphibian 时,原始对象仍然是 Frog,因此调用重写方法。

But I don't understand What is the difference between Frog f = new Frog(); and Amphibian f = new Frog();

为了理解差异,让我们在 Frog 中添加另一个 Amphibian

中没有的方法
class Frog extends Amphibian {
    void go() { System.out.println("not go"); }
    void come() { System.out.println("come"); }
}

现在让我们来看看有什么区别:

public class EFrog {
    public static void main(String[] args) {
        Frog f = new Frog();
        f.go();
        f.come();
        Amphibian a = f;
        a.come();//this won't compile
    }
}

底线。 Frog 是一个 Amphibian,所以 Frog 可以做 Amphibian 可以做的任何事情。 Amphibian 不是 Frog,所以 Amphibian 不能做 Frog 能做的所有事情。

当你说Amphibian a = new Frog()时,你是在为一个接口编程(不是java接口而是接口的一般含义)。当您说 Frog f = new Frog() 时,您正在为实现编程。

现在开始讨论本书要求您尝试的实际问题:

In main( ), create a Frog and upcast it to Amphibian and demonstrate that all the methods still work.

public class EFrog {
        public static void main(String[] args) {
            Frog f = new Frog();
            Amphibian g = (Amphibian)f;//this is an upcast
            g.go(); //okay since Amphibian can go
            g.come();//not okay since Amphibian can't come                
        }
    }

我不认为你想问upcasting有什么用但是既然标题已经被别人编辑过了,为什么不一并回答呢?向上转型在某些情况下很有用,例如显式调用重载方法的特殊形式。有关更多详细信息,请参阅 this 答案。

太明显的区别,你可以添加一个方法 jump() 到青蛙(但不是基础 class 两栖动物:

class Amphibian {
    void go() { System.out.println("go"); }
    void stop() { System.out.println("stop!"); }
}

class Frog extends Amphibian {
    void go() { System.out.println("not go"); }
    void jump() { System.out.println("jump!"); }
}

public class EFrog {
    public static void main(String[] args) {
        Frog f = new Frog();
        f.go();
        f.jump(); // works
        Amphibian a = new Frog();
        a.jump(); // will not compile!
    }
}

这个很简单 如果你像下面这样使用

Frog f = new Frog();

那么你不能将它转换为任何其他类型。但是,您可以调用所有 Frog 方法。

但是如果你像下面这样使用

Amphibian f = new Frog();

您仍然可以将对象更改为其他一些两栖动物,而无需通过简单地这样做来更改其引用。

f = new SomeOtherAmphibian();

这种编码方式非常流行,被称为编码到基础Class或接口

What is the difference between Frog f = new Frog(); and Amphibian f = new Frog();

从你的例子我们可以看出Frog继承了Amphibian。因此,任何声明为 Amphibian 类型的变量都可以始终将具有超级 class 的对象实例存储为 Amphibian 在您的情况下 Frog。至于示例代码,它没有区别。

但是在您的示例中找不到两者之间的细微差别。考虑以下因素:

class Frog extends Amphibian {
    void go() { System.out.println("not go"); }
    void croak(){ System.out.println("croak");} //Added this extra line
}

现在,如果您将 Frog 实例存储为

Amphibian f = new Frog();
f.croak(); //This will not work.

这不起作用,因为 JVM 将 f 视为类型 Amphibian,而在 Amphibian class 中没有方法 croak。所以会抛出一个错误。但是如果你像这样存储它:

Frog f = new Frog();
f.croak(); //This will work.

这是有效的,因为 JVM 将 f 视为类型 Frog 并且 class Frog 具有方法 croak.

当然,您可以通过将 f 类型转换为 Frog 作为 :

Amphibian f = new Frog();
((Frog)f).croak(); //This will work.