java 当基类和派生类都有同名变量时会发生什么

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5630486/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 11:59:44  来源:igfitidea点击:

What happens when base and derived classes each have variables with the same name

java

提问by moonsun

Consider the int avariables in these classes:

考虑int a这些类中的变量:

class Foo {
    public int a = 3;
    public void addFive() { a += 5; System.out.print("f "); }
}
class Bar extends Foo {
    public int a = 8;
    public void addFive() { this.a += 5; System.out.print("b " ); }
}
public class test {
    public static void main(String [] args){
        Foo f = new Bar();
        f.addFive();
        System.out.println(f.a);
    }
}

I understand that the method addFive()have been overridden in the child class, and in class test when the base class reference referring to child class is used to call the overridden method, the child class version of addFiveis called.

我了解到该方法addFive()已在子类中被覆盖,并且在类测试中,当使用引用子类的基类引用来调用被覆盖的方法时,会调用子类版本addFive

But what about the public instance variable a? What happens when both base class and derived class have the same variable?

但是公共实例变量a呢?当基类和派生类具有相同的变量时会发生什么?

The output of the above program is

上面程序的输出是

b 3 

How does this happen?

这是怎么发生的?

回答by Stephen C

There are actually two distinct public instance variables called a.

实际上有两个不同的公共实例变量称为a.

  • A Foo object has a Foo.avariable.
  • A Bar object has both Foo.aand Bar.avariables.
  • Foo 对象有一个Foo.a变量。
  • Bar 对象同时具有Foo.aBar.a变量。

When you run this:

当你运行这个:

    Foo f = new Bar();
    f.addFive();
    System.out.println(f.a);

the addFivemethod is updating the Bar.avariable, and then reading the Foo.avariable. To read the Bar.avariable, you would need to do this:

addFive方法是更新Bar.a变量,然后读取Foo.a变量。要读取Bar.a变量,您需要执行以下操作:

    System.out.println(((Bar) f).a);

The technical term for what is happening here is "hiding". Refer to the JLS section 8.3, and section 8.3.3.2for an example.

这里发生的事情的技术术语是“隐藏”。有关示例,请参阅 JLS第 8.3 节第 8.3.3.2 节

Note that hiding also applies to staticmethods with the same signature.

请注意,隐藏也适用于static具有相同签名的方法。

However instance methods with the same signature are "overridden" not "hidden", and you cannot access the version of a method that is overridden from the outside. (Within the class that overrides a method, the overridden method can be called using super. However, that's the only situation where this is allowed. The reason that accessing overridden methods is generally forbidden is that it would break data abstraction.)

但是,具有相同签名的实例方法是“覆盖”而不是“隐藏”,并且您无法访问从外部覆盖的方法的版本。(在覆盖方法的类中,可以使用 调用被覆盖的方法super。但是,这是唯一允许的情况。通常禁止访问被覆盖的方法的原因是它会破坏数据抽象。)



The recommended wayto avoid the confusion of (accidental) hiding is to declare your instance variables as privateand access them via getter and setter methods. There are lots of othergood reasons for using getters and setters too.

避免(意外)隐藏混淆的推荐方法是将您的实例变量声明为private并通过 getter 和 setter 方法访问它们。还有很多其他使用 getter 和 setter 的好理由。



It should also be noted that: 1) Exposing public variables (like a) is generally a bad idea, because it leads to weak abstraction, unwanted coupling, and other problems. 2) Intentionally declaring a 2nd public avariable in the child class is a truly awful idea.

还应该注意的是: 1) 公开公共变量(如a)通常是一个坏主意,因为它会导致弱抽象、不需要的耦合和其他问题。2)故意a在子类中声明第二个公共变量是一个非常糟糕的主意。

回答by Dead Programmer

From JLS

JLS

8.3.3.2 Example: Hiding of Instance Variables This example is similar to that in the previous section, but uses instance variables rather than static variables. The code:

class Point {
  int x = 2;
}
class Test extends Point {
  double x = 4.7;
  void printBoth() {
      System.out.println(x + " " + super.x);
  }
  public static void main(String[] args) {
      Test sample = new Test();
      sample.printBoth();
      System.out.println(sample.x + " " + 
                                              ((Point)sample).x);
  }
}

produces the output:

4.7 2
4.7 2

because the declaration of x in class Test hides the definition of x in class Point, so class Test does not inherit the field x from its superclass Point. It must be noted, however, that while the field x of class Point is not inherited by class Test, it is nevertheless implemented by instances of class Test. In other words, every instance of class Test contains two fields, one of type int and one of type double. Both fields bear the name x, but within the declaration of class Test, the simple name x always refers to the field declared within class Test. Code in instance methods of class Test may refer to the instance variable x of class Point as super.x.

Code that uses a field access expression to access field x will access the field named x in the class indicated by the type of reference expression. Thus, the expression sample.x accesses a double value, the instance variable declared in class Test, because the type of the variable sample is Test, but the expression ((Point)sample).x accesses an int value, the instance variable declared in class Point, because of the cast to type Point.

8.3.3.2 示例:隐藏实例变量 这个例子与上一节类似,但使用的是实例变量而不是静态变量。代码:

class Point {
  int x = 2;
}
class Test extends Point {
  double x = 4.7;
  void printBoth() {
      System.out.println(x + " " + super.x);
  }
  public static void main(String[] args) {
      Test sample = new Test();
      sample.printBoth();
      System.out.println(sample.x + " " + 
                                              ((Point)sample).x);
  }
}

产生输出:

4.7 2
4.7 2

因为类Test 中x 的声明隐藏了类Point 中x 的定义,所以类Test 不会从其超类Point 继承字段x。然而,必须注意的是,虽然类 Point 的字段 x 不是由类 Test 继承的,但它仍然由类 Test 的实例实现。换句话说,类 Test 的每个实例都包含两个字段,一个是 int 类型,一个是 double 类型。两个字段都带有名称 x,但在类 Test 的声明中,简单名称 x 始终指代在类 Test 中声明的字段。类 Test 的实例方法中的代码可以将类 Point 的实例变量 x 称为 super.x。

使用字段访问表达式访问字段 x 的代码将访问引用表达式类型指示的类中名为 x 的字段。这样,表达式 sample.x 访问了一个 double 值,即类 Test 中声明的实例变量,因为变量 sample 的类型是 Test,而表达式 ((Point)sample).x 访问的是一个 int 值,即声明的实例变量在 Point 类中,因为类型转换为 Point。

回答by Mahendra Liya

In inheritance, a Base class object can refer to an instance of Derived class.

在继承中,基类对象可以引用派生类的实例。

So this is how Foo f = new Bar();works okay.

所以这是Foo f = new Bar();正常工作的方式。

Now when f.addFive();statement gets invoked it actually calls the 'addFive() method of the Derived class instance using the reference variable of the Base class. So ultimately the method of 'Bar' class gets invoked. But as you see the addFive()method of 'Bar' class just prints 'b ' and not the value of 'a'.

现在,当f.addFive();语句被调用时,它实际上使用基类的引用变量调用派生类实例的 'addFive() 方法。所以最终'Bar'类的方法被调用。但是正如您所看到的addFive(),'Bar' 类的方法只打印 'b' 而不是 'a' 的值。

The next statement i.e. System.out.println(f.a)is the one that actually prints the value of a which ultimately gets appended to the previous output and so you see the final output as 'b 3'. Here the value of a used is that of 'Foo' class.

下一个语句 ieSystem.out.println(f.a)是实际打印 a 值的语句,该值最终附加到前一个输出中,因此您会看到最终输出为“b 3”。这里使用的值是 'Foo' 类的值。

Hope this trick execution & coding is clear and you understood how you got the output as 'b 3'.

希望这个技巧的执行和编码是清楚的,并且你明白你是如何得到“b 3”的输出的。

回答by Ammu

Here F is of type Foo and f variable is holding Bar object but java runtime gets the f.a from the class Foo.This is because in Java variable names are resolved using the reference type and not the object which it is referring.

这里 F 是 Foo 类型,f 变量保存 Bar 对象,但 java 运行时从 Foo 类中获取 fa。这是因为在 Java 中,变量名称是使用引用类型解析的,而不是它所引用的对象。