当谈到多态性、继承、重载、覆盖、泛型和转换的混合时如何记住事情

How to remember things when it comes to mixture of polymorphism, Inheritance, overloading, overriding, Generics as well as Casting

我正在准备 Java 认证考试,当涉及到多态性、继承、重载、覆盖、泛型以及转换时,很少有情况会变得非常复杂。

我无法理解下面提到的这些示例:

 
// Tree is the base class
class Tree {
    String type = "unknown";

    String getTreeString() {
        return "Tree";
    }
}
 
// DeciduousTree is a subclass of Tree
class DeciduousTree extends Tree {
    String type = "deciduous";

    @Override
    String getTreeString() {
        return "Leafy Tree";
    }
}
 
// FruitTree is a subclass of Tree
class FruitTree extends Tree {
    String type = "fruit";

    @Override
    String getTreeString() {
        return "Fruit Tree";
    }
}
 
public class UpcastExamples {
    public static void main(String[] args) {
 
        UpcastExamples upex = new UpcastExamples();
 
        // Create two specific trees
        Tree tree = new Tree();
        Tree mapleTree = new DeciduousTree();
        Tree appleTree = new FruitTree();
 
        // we upcast deciduousTree to its parent class
        Tree genericTreeMaple = (Tree) mapleTree;
        Tree genericTreeApple = (Tree) appleTree;
        Tree genericTreeTree = (Tree) tree;
 
        // Print mapleTree's type
        System.out.println("Tree type = " + mapleTree.type);
 
        // Let's upcast to use the generic Tree's type..
        System.out.println("Tree type = " + (genericTreeMaple.type));

        // Print Fruit Tree's type
        System.out.println("Tree type = " + appleTree.type);
 
        // Upcasting to pass object as a parameter
        upex.printTreeType(genericTreeTree);
        upex.printTreeType(genericTreeMaple);
        upex.printTreeType(genericTreeApple);

        upex.printTreeType(tree);
        upex.printTreeType(mapleTree);
        upex.printTreeType(appleTree);
    }
 
    public void printTreeType(Tree tree) {
        System.out.println("Tree type = " + tree.getTreeString());
    }
}

我希望这些 upex.printTreeType(genericTreeMaple);upex.printTreeType(genericTreeApple); 打印 Tree 因为它们被升级到 Tree(Base Class) 但不知何故它抓住了它的方法 child classes(我知道它们被覆盖了)但是通过方法重载实现时是一样的 Animal genericDog = new Dog();Animal genericCat = new Cat(); 将坚持 parent 的 class 的方法。

我试着这样记住 parentClass p = new childClass() 将始终引用 parent[=27= 的方法] 但它似乎在重写和向上转换期间失败了。

另外,下面还有一些我看不懂的地方,我不想死记硬背

Here BaseClass class is the parent class
And NextClass inherits the BaseClass

BaseClass[] myNextArray = new NextClass[6];
BaseClass[] myNextArray2 = new BaseClass[6];

//Allows this:
NextClass[] nextArray = (NextClass[]) myNextArray; //Line 1

//But doesn't allows this:
NextClass[] nextArray2 = (NextClass[]) myNextArray2;

Do I have to memorize that Array of SubClass is not equal to Array of SuperClass in Java but how come it allows Line 1 is also a big doubt

在Java中,每个对象(包括数组)都有一个在构造时确定的类型,例如使用 new 运算符。这种类型永远不会改变。

变量仅包含对对象的引用。想想遥控器。您可以使用具有更广泛类型的变量来引用对象,即对象的超类或接口的类型。但这不会改变对象本身的类型。

因此,当您调用可重写方法时,您将始终调用对象实际类型的最具体方法。此外,如果对象的实际类型兼容,则类型转换将成功。变量的引用类型并不能说明类型转换是否会成功,否则我们根本不需要运行时检查¹。

当你初始化一个变量时

BaseClass[] myNextArray = new NextClass[6];

对象的实际类型是NextClass[],因此,后续类型转换为NextClass[]可以成功。相反,使用

BaseClass[] myNextArray2 = new BaseClass[6];

对象的实际类型是 BaseClass[],因此,类型转换为 NextClass[] 将失败。

请注意,可以更改引用变量以使其指向不同的对象,因此将引用转换为特定类型是否会成功也可能会发生变化。

BaseClass[] myNextArray = new NextClass[6];
NextClass[] nextArray = (NextClass[]) myNextArray; // succeeds
myNextArray = new BaseClass[6]; // let myNextArray refer to a different object
nextArray = (NextClass[]) myNextArray; // will fail

¹ 该规则的唯一例外是编译器将拒绝某些类型转换,这些类型转换可以在编译时证明是不可能成功的,例如尝试将 Integer 转换为 String.