2
public class NotActuallyImmutable {
  private final int x;

  public NotActuallyImmutable(int x) {
    this.x = x;// line 1
  }

  public int getX() {
    return x;
  }
}


public class Mutable extends NotActuallyImmutable {
  private int x = 123;

  public Mutable(int x) {
    super(x);
  }

  public int getX() {
    return x++;
  }
}

现在在我的主班

    NotActuallyImmutable n = new Mutable(42); // line2
    int x = n.getX();
    System.out.println("x is"+x);

我期待输出为 42,但它返回输出为 123。我期待 42,因为在第 2 行我正在制作 Mutable 类的对象,然后在第 1 行我将值设置为 42。所以当我这样做时n.getX()我应该得到这个最新值不是默认值 123。我知道我错过了一些东西,但无法弄清楚它背后的逻辑?

4

5 回答 5

2

问题是字段x中的字段和类中Mutable的字段不一样。返回的是in (因为调用的是,不是)。xNotActuallyImmutablexgetX()MutablegetX()Mutable.getX()NotActuallyImmutable.getX()

请注意,如果您从 中删除了实例字段Mutable,那么您将遇到编译器错误,因为NotActuallyImmutable.x它是私有的,NotActuallyImmutable并且不能被Mutable.

如果您创建了NotActuallyImmutable.x一个受保护的字段,Mutable.x则会对其进行遮蔽,并且您仍然会有相同的行为。如果您Mutable.x在这种情况下删除,您仍然会遇到编译器错误,因为您试图增加一个final字段。

如果您删除Mutable.getX(),那么x将返回的getX()将是NotActuallyImmutable.x,尽管在 中存在另一个同名字段Mutable

于 2013-04-14T03:30:48.557 回答
2

private int xinMutableprivate int xin是完全不同的NotActuallyImmutable字段,只是具有相同的名称。

这对编译器来说不是问题,因为您无法访问private另一个类的字段。因此,就编译器而言,当您定义 时MutablexinNotActuallyImmutable是不可见的,也可能不存在。

这当然会让程序员感到困惑。如果您将其中一个字段重命名为y(并将 getter 方法重命名为getY),则行为似乎更加直观。

于 2013-04-14T03:34:47.470 回答
1
NotActuallyImmutable n = new Mutable(42); // line2

这意味着您有一个 NotActuallyImmutable 类型的对象,但创建的对象的实例是可变的。因此,在这段代码中,您处理的 Mutable 对象将返回 123。因为您传递的数字保存在 NotActuallyImmutable 中而不是 Mutable 中,

于 2013-04-14T03:35:37.887 回答
0

我同意上述答案中给出的很好的解释。但要简单地概括一下最后的理解。当我在做 new Mutable(42).getX() 时,jvm 首先会查看 Mutable 对象以获取不在 NotActuallyImmutable 内部的 X 的值。如果我从 Mutable 中删除 getX() 方法,我会得到预期的(根据我的期望)值,即 42。

这个例子变得凌乱,因为变量名称,即 X 在父类和子类中相同,但有助于理解概念

于 2013-04-14T04:36:32.373 回答
0

n有两个x在不同上下文中可见的不同值,父类的私有成员变量和子类的私有成员变量。

NotActuallyImmutable n = new Mutable(42); // line2

创建一个新的Mutable. 执行parent(x)将父类设置x为 42。

int x = n.getX();

n是一个Mutable实例,所以这调用Mutable'sgetX()来返回(123)Mutable的值x而不是父级的值。

于 2013-04-14T03:37:21.270 回答