简体   繁体   English

Java继承:继承超类成员和变量时的区别

[英]Java Inheritance: Difference while inheriting super class members and variables

I was going through Java inheritance and I tried the following code 我正在经历Java继承,并尝试了以下代码

class A {
    public int x = 1;

    public void print1() {
        System.out.println("Print from A");
    }
}

class B extends A {
    public int x = 2;

    public void print1() {
        System.out.println("Print from B");
    }
}

class C extends B {
    public int x = 3;

    public void print1() {
        System.out.println("Print from C");
    }

    public static void main(String aa[]) {
        C c = new C();
        ((A) c).print1();
        System.out.println(c.x);    
        System.out.println(((A) c).x);          
    }
}

The output was 输出是

Print from C 从C打印
3 3
1 1个

Now, my question is that I am able to access class A member x from class C instance c but why am I not able to access the class A method print1() . 现在,我的问题是我可以从class C instance c访问class A member x ,但是为什么我不能访问class A method print1() I know, if I want I can call new A().print1(); 我知道,如果我愿意,我可以叫new A().print1(); . But, I want to access it from an instance of class C directly. 但是,我想直接从C类的实例访问它。 So by overriding methods, are the top parent class methods lost in the bottom child class? 因此,通过覆盖方法,顶部的父类方法是否在底部的子类中丢失了? However, the parent class members are retained (although hidden). 但是,父类成员将保留(尽管隐藏)。 Why so? 为什么这样? And is there a way to call a method from class A in class C without creating an instance of A ? 有没有一种方法可以在不创建A实例的情况下从C类中的A类调用方法?

EDIT 编辑

What's happening here? 这里发生了什么事?

When you say ((A)c).print1(); 当你说((A)c).print1(); , JVM knows that actual the instance, on which it needs to call is print1() is of type C hence class C 's print1() version is executed. ,JVM知道实际需要调用的实例是print1() ,其类型为C因此将执行类Cprint1()版本。

But when you type ((A)c).x , you are referring to state, not behavior and that too using reference of type A . 但是当您键入[ ((A)c).x ,您是在指状态,而不是行为,也是在使用类型A引用。 Which version of x will be picked up, is decided at compile time as compiler knows what is the type reference (In this case A ). 因为编译器知道什么是类型引用(在本例中为A ),所以在编译时确定将使用x哪个版本。 That is why you see A 's state. 这就是为什么您看到A的状态。

This statement from Java doc might also be interesting for you: Java doc中的以下语句可能对您也很有趣:

Within a class, a field that has the same name as a field in the superclass hides the superclass's field, even if their types are different. 在类中,即使其类型不同,与超类中的字段具有相同名称的字段也会隐藏超类的字段。 Within the subclass, the field in the superclass cannot be referenced by its simple name. 在子类中,无法通过其简单名称引用超类中的字段。 Instead, the field must be accessed through super, which is covered in the next section. 相反,必须通过super访问该字段,这将在下一部分中介绍。 Generally speaking, we don't recommend hiding fields as it makes code difficult to read. 一般来说,我们不建议隐藏字段,因为这会使代码难以阅读。


Why is it this way? 为什么这样呢?

Answer to your questions: 回答您的问题:

Question 1: 问题1:

my question is that I am able to access class A member x from class C instance c 我的问题是我能够从C类实例c访问A类成员x

About variable x , since it is declared as public , no business validation can be applied on that. 关于变量x ,因为它被声明为public ,所以不能对其应用任何业务验证。 Moreover, the developer who's declaring it as public , is aware that concept of encapsulation can't be applied now (which is obviously not recommended). 而且,将其声明为public的开发人员知道,封装的概念现在不能应用(显然不建议这样做)。 So there is no harm in allowing access to parent's state using child instance. 因此,允许使用子实例访问父状态不会有任何危害。 Therefore, by using ((A)c).x , you can access x from class A . 因此,通过使用[ ((A)c).x ,您可以从类A访问x

Question 2: 问题2:

However, the parent class members are retained (although hidden). 但是,父类成员将保留(尽管隐藏)。 Why so? 为什么这样?

It's because Java does not allow you to use super.super.super.. arbitrarily to access any parent's behavior in the inheritance hierarchy. 这是因为Java不允许您随意使用super.super.super..来访问继承层次结构中任何父级的行为。 And the reasoning behind it is to preserve encapsulation. 其背后的原因是保留封装。 You can only access behavior of the immediate parent class using super keyword but not beyond that. 您只能使用super关键字访问直接父类的行为,但不能超过此行为。

To explain it more, let's say you have implementation of class B like: 为了进一步解释,假设您具有B类的实现:

class B extends A {
    public int x = 2;

    public void print1() {
        if(x >= 2) {
            System.out.println("Print from B");
        }
    }
}

Now if super.super was allowed, you can easily bypass validation in print1() method of class B and can invoke A 's print1() method from the instance of class C . 现在,如果允许使用super.super ,则可以轻松地在类B print1()方法中绕过验证,并可以从类C的实例调用A的print1()方法。

Question 3: 问题3:

However, the parent class members are retained (although hidden). 但是,父类成员将保留(尽管隐藏)。 Why so? 为什么这样?

As, mentioned earlier, to retain encapsulation. 如前所述,保留封装。

Question 4: 问题4:

And is there a way to call a method from class A in class C without creating an instance of A? 有没有一种方法可以在不创建A实例的情况下从C类中的A类调用方法?

So there is no way to access print1() from A using C 's instance. 因此,无法使用C的实例从A访问print1()

When you override a function which already implemented in base(parent) class, when you call that function within the child class, automatically the class in the child will call so in your case when you call print1() of a C object: 当您覆盖已经在base(parent)类中实现的函数时,在子类中调用该函数时,子类中的类将自动调用,因此在您情况下,当您调用C对象的print1()时:

C c = new C();
c.print1();

you will call the override function. 您将调用覆盖功能。 However if in any part of your program you feel that you need to call the top parent class for any case (which cannot be fulfilled by super ) then you should reconsider your design strategy; 但是,如果在程序的任何部分中您觉得在任何情况下都需要调用顶级父类( super无法实现),则应重新考虑您的设计策略; maybe you shouldn't override the print1() or even you shouldn't put print1() in the base class in the first place 也许您不应该覆盖print1() ,甚至不应该将print1()首先放在基类中

In object oriented programming, only methods have the ability to be overriden. 在面向对象的编程中,只有方法才具有被覆盖的能力。 You cannot override a data member. 您不能覆盖数据成员。 This explains why you get the outputs. 这解释了为什么获得输出。

print1() method of A is overriden in C , and c is an object of C . A print1()方法在C被覆盖,并且cC的对象。 So, it will call the print1() method of C . 因此,它将调用Cprint1()方法。 On the other hand, the variable x is not overriden. 另一方面,变量x不被覆盖。

To call the superclass method, you can do: 要调用超类方法,您可以执行以下操作:

super.print1();

Here , Same named Instance Variable is used in all the Classes to confuse the compiler, but you should know that Only Member functions can be overridden not member variables. 在这里,所有类都使用了同名实例变量来混淆编译器,但是您应该知道,只有成员函数才能被覆盖,而成员变量不能被覆盖。

So, All the three x will be stored separately in memory. 因此,所有三个x将分别存储在内存中。 but public void print1() will be overridden by its child class. 但是public void print1()将被其子类覆盖。 So, 所以,

  1. ((A)c).print1(); this piece of Code will Call the overridden method defined in class C. doesn't matter in which class you cast the Object of Class C . 这段代码将调用C类中定义的重写方法。无论在哪个类中强制转换C类的Object都无关紧要。

But in Second Case ((A)c).x will print the value from Class A . 但是在第二种情况下((A)c).x将打印A类的值。

  1. ((A)c).x :- this will reference to the variable x belongs to Class A not to Class C . ((A)c).x :-这将引用变量x属于Class A而不是Class C because variables never overridden in Inheritence. 因为变量从未在继承中被覆盖。

((A)c).print1(); ((A)c).print1(); Actually it is similar to the c.print1(); 实际上,它类似于c.print1();。 . It doesn't matter that you typecast it with Class A, it will still call print1() function of the Class C. 不管您使用Class A进行类型转换,它仍将调用Class C的print1()函数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM