2

当从这些类创建对象时,我对多态性和扩展类等在 Java 中的工作原理有点困惑。

我最近遇到了一个问题,这里有人帮我解决了(请参阅无法访问对象子类的变量?(Java / Android))作为背景。

我试图创建一个像这样的对象:

Quad hero = new Hero();

其中 Hero 是 Quad() 的子类;

我的 Hero 类中有我无法访问的变量。

解决方案是将我的对象创建更改为:

Hero hero = new Hero();

这样做,通过我的 hero 对象,我能够访问 Quad 和 Hero 类中的所有方法和变量。

我现在的问题是 - 为什么会这样?

考虑到这一点,什么时候使用我原来的方法有用:

Quad hero = new Hero();

这对我来说很有意义,因为 Hero 也是一个四边形。我在 Java 代码示例中多次看到这种类型的声明,并认为我理解,但最近的事件证明并非如此。

如果有人可以为我解释这一点,将不胜感激 - 谢谢

4

3 回答 3

4

AssumeOtherHero也是Quad具有不同行为的子类:

这将正常工作:

Quad hero = new Hero();
// some code
hero = new OtherHero();

而以下不会编译:

Hero hero = new Hero();
// some code
hero = new OtherHero();

这似乎没有用。现在假设您有一个方法,它具有Quad作为返回类型。我将用伪代码编写它:

Quad method() {
    if (condition)
        return new Hero();
    else
        return new OtherHero();
}

所以你实际上不知道它是否会返回 aHero或 anOtherHero

现在,您可以编写:

Quad foo = method();

现在,您实际上并不知道 的确切类型foo,但您可以将其用作Quad.

一个流行的例子是java.util.List

你可以写:

List<Integer> list = Arrays.asList(1,2,3,4,5,6);
Collections.shuffle(list);

List是一个接口,由ArrayListLinkedList许多其他类实现。在不知道代码的情况下,我们不知道 究竟返回了哪个类Arrays.asList(),但我们可以将其用作List.

于 2013-04-28T21:15:32.337 回答
2

如果您将对象存储在Quadthen 的变量中,就编译器所知,存储在该变量中的对象是 a Quad,仅此而已。当然,它可能是一个子类型,但编译器不知道那个子类型是什么。据它所知,该子类型将向基本类型引入绝对零个成员。如果编译器不能保证对象不仅仅是 a Quad,那么它不会允许你做任何比它允许你用 a 做的事情更多的事情Quad

当然,可以看到这个变量只被分配了一个new Hero()表达式。而且,当然,您可能知道特定变量将仅保存该Hero类型的对象。但这是您可以在实践中推断出的运行时信息。编译器懒得计算所有这些场景。这就是为什么它有你。如果您知道存储在该变量中的对象将始终是 type Hero,您可以继续将其表达给编译器。

该语言允许您将对象转换为超类型的一个原因是,在许多情况下,您希望将该对象传递给不必知道基类的所有可能子类型的方法(例如,Hero),但只传递的对象至少是一个基类(比如,Quad)。另一个典型的场景是,当您想要存储各种不同类型的对象时,这些对象并非都属于相同的特定类型(它们不是 all Hero),但它们都有一个共同的祖先(它们都是Quad),并且您想对它们进行集体处理。一般来说,将对象转换为祖先类是允许的,因为在需要基类型的情况下,使对象与其基类型兼容很有用

于 2013-04-28T21:23:45.213 回答
2

将变量类型声明为Quad,意味着该变量还可以引用其他类型的子类Quad,而不仅仅是Hero. 如果你传递那个引用,接收者只会知道它是“某种四边形”,而不能依赖它是一个Hero. 因此,它不能访问仅在子类中声明的引用字段。

至于何时使用这种赋值,我通常只在使用接口类型时才这样做。喜欢:

public List<SomeObject> doSomething() {
    List<SomeObject> result = new ArrayList<>();
    // do something
    return result;
}

但这真的只是一种习惯。

于 2013-04-28T21:13:36.313 回答