28

In the below code snippet, the result is really confusing.

public class TestInheritance {
    public static void main(String[] args) {
        new Son();
        /*
        Father father = new Son();
        System.out.println(father); //[1]I know the result is "I'm Son" here
        */
    }
}

class Father {
    public String x = "Father";

    @Override
    public String toString() {
       return "I'm Father";
    }

    public Father() {
        System.out.println(this);//[2]It is called in Father constructor
        System.out.println(this.x);
    }
}

class Son extends Father {
    public String x = "Son";

    @Override
    public String toString() {
        return "I'm Son";
    }
}

The result is

I'm Son
Father

Why is "this" pointing to Son in the Father constructor, but "this.x" is pointing to "x" field in Father. How is the "this" keyword working?

I know about the polymorphic concept, but won't there be different between [1] and [2]? What's going on in memory when new Son() is triggered?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Garnett
  • 403
  • 1
  • 6
  • 14
  • 13
    You can't override a field. Weird shit happens if you try. – user2357112 Apr 14 '14 at 08:34
  • 11
    This kind of situations show why measures like TIOBE are completely meaningless. This kind of stupid, simple, and basic (asked muuuultiple times across the internet, SO included) receives ten upvotes in two minutes, and my simple three-lines answer receives six upvotes in five minutes. Measuring the popularity of a language based on this kind of traffic is completely meaningless, as the quality of the traffic shows. – Manu343726 Apr 14 '14 at 08:45
  • 1
    I'm sure that if I post exactly the same question in C++ instead of Java, I will receive a docen of downvotes in a couple of minutes and thousands of duplicate flags. – Manu343726 Apr 14 '14 at 08:47
  • 1
    What kind of popularity measure are you going by? It sounds like you'd expect C++ to be more popular because the community behind it doesn't talk or promote novices in their community from asking "stupid" questions. Popularity is what TIOBE measures, and if this is some barometer for that, it's doing a good job. – Nick Klauer Apr 14 '14 at 12:08
  • 2
    @Nick the question itself is a perfectly reasonable question if you are a novice. What I mean is that SO is dessigned as a knowledge base, with good questions and good answers. So some questions hav many upvotes reflecting the importance of the topic. But this is a basic question about polymorphism, a question which has been answered directly or indirectly many times before on the internet and SO in particular. – Manu343726 Apr 14 '14 at 14:25
  • 3
    @Nick What I expect with this kind of basic question is people providing links to other references (inside SO, I.E. duplicates) to not duplicate knowledge on the SO network, instead of up voting the question massively. As I said, I'm sure the C++ people would do exactly that if I post the question. – Manu343726 Apr 14 '14 at 14:28
  • 1
    @Manu343726: do you have a duplicate candidate? – Peter Mortensen Apr 14 '14 at 16:14
  • 1
    @Manu343726 I don't think this question was "stupid, simple and basic" (and you contradict yourself by saying it's "perfectly reasonable"). Whether it's a duplicate, that's on you to show. The code above raises very specific issues (field hiding, and accessing instance fields in constructors), while still being applicable to a broad range of situations, and I doubt another question has an answer which covers these issues. – Silly Freak Apr 14 '14 at 21:23

7 Answers7

23

All member functions are polymorphic in Java by default. That means when you call this.toString() Java uses dynamic binding to resolve the call, calling the child version. When you access the member x, you access the member of your current scope (the father) because members are not polymorphic.

Manu343726
  • 13,969
  • 4
  • 40
  • 75
  • 1
    Hi @Manu, I've edit the question, could you take a look again? Actually I know how polymorphic works, but I'm not clear about what's happening in memory when _new Son()_ is executed, for example which area is "this" pointing to? what's the difference of "this" in Son and Father? – Garnett Apr 14 '14 at 09:06
  • 1
    There is no difference, is the same this. This is a reference (pointer) to the object which you are working with. When you instance a child, the ctor of the base is called first, so the code of the father constructor is executed. Since Java methods are polymorphic, if you call a method in the base class **which is overrided in the derived class (like to string in your example)** the version of the derived class is called. – Manu343726 Apr 14 '14 at 09:11
  • @Garnett remember: This points to the object. But when you are in the scope of the base, it doesn't know if its dynamic type is of a derived type instead of that type (father in this case). So if you are in the scope of the father, this acts like it was a father only, so x is the x of the father – Manu343726 Apr 14 '14 at 09:13
  • @Garnett try that code in a language which is not polymorphic by default (like C# or C++): You will see the base class accessing things of the base class only, so the output will be "I'm father. Father" (Supposing you are not using an existing olymorphic function, like toString() in C#. Write another one. – Manu343726 Apr 14 '14 at 09:17
  • But @Manu, I think there are two objects, one is the instance of Son, the other is instance of Father, right? So, how can they use the same this? What it would be in memory? – Garnett Apr 14 '14 at 09:21
  • No, they are the same object, Thats the point. Think about its memory layout as an array where the things of father are placed first, followed by the things of child. In all cases, this points to the beggining of that array, but if you are on father you can only reach things of father (the beggining of the array), and when you are on the code of child you could access the hole array. – Manu343726 Apr 14 '14 at 09:34
  • The point of polymorphism is that, even if in father you cannot access the second part of the array, the system knows the function is overrided and resolves the call correctly, calling the version of the child. Also note that in your case you are using the same name for the member x in both classes. That means in the derived class, even if you have access "to the hole array", x refers to the x of the child, hiding the x of the father. There are two different fields, with the same name, but naming rules say the same name in a derived class hides a name of a base class. – Manu343726 Apr 14 '14 at 09:37
  • You say **if you are on father you can only reach things of father (the beggining of the array)**, and "System.out.println(this);" is executed in Father constructor, isn't it the case of _on father_? if so, the result would be Father, if not, which case is _on father_? – Garnett Apr 14 '14 at 10:30
  • 1
    Println( this ) is a shorthand of printlm( this.toString() ). And toString() is a polymorphic function you overrided on child. So the child version is resolved because thats the real type of the object (even in the code of the base class you cannot know that. Thats the point of polymorphism – Manu343726 Apr 14 '14 at 10:33
13

Two things are going on here, let's look at them:

First of all, you are creating two different fields. Taking a look at a (very isolated) chunks of the bytecode, you see this:

class Father {
  public java.lang.String x;

  // Method descriptor #17 ()V
  // Stack: 2, Locals: 1
  public Father();
        ...
    10  getstatic java.lang.System.out : java.io.PrintStream [23]
    13  aload_0 [this]
    14  invokevirtual java.io.PrintStream.println(java.lang.Object) : void [29]
    17  getstatic java.lang.System.out : java.io.PrintStream [23]
    20  aload_0 [this]
    21  getfield Father.x : java.lang.String [21]
    24  invokevirtual java.io.PrintStream.println(java.lang.String) : void [35]
    27  return
}

class Son extends Father {

  // Field descriptor #6 Ljava/lang/String;
  public java.lang.String x;
}

Important are lines 13, 20 and 21; the others represent the System.out.println(); itself, or the implicit return;. aload_0 loads the this reference, getfield retrieves a field value from an object, in this case, from this. What you see here is that the field name is qualified: Father.x. In the one line in Son, you can see there is a separate field. But Son.x is never used; only Father.x is.

Now, what if we remove Son.x and instead add this constructor:

public Son() {
    x = "Son";
}

First a look at the bytecode:

class Son extends Father {
  // Field descriptor #6 Ljava/lang/String;
  public java.lang.String x;

  // Method descriptor #8 ()V
  // Stack: 2, Locals: 1
  Son();
     0  aload_0 [this]
     1  invokespecial Father() [10]
     4  aload_0 [this]
     5  ldc <String "Son"> [12]
     7  putfield Son.x : java.lang.String [13]
    10  return
}

Lines 4, 5 and 7 look good: this and "Son" are loaded, and the field is set with putfield. Why Son.x? because the JVM can find the inherited field. But it's important to note that even though the field is referenced as Son.x, the field found by the JVM is actually Father.x.

So does it give the right output? Unfortunately, no:

I'm Son
Father

The reason is the order of statements. Lines 0 and 1 in the bytecode are the implicit super(); call, so the order of statements is like this:

System.out.println(this);
System.out.println(this.x);
x = "Son";

Of course it's gonna print "Father". To get rid of that, a few things could be done.

Probably the cleanest is: don't print in the constructor! As long as the constructor hasn't finished, the object is not fully initialized. You are working on the assumption that, since the printlns are the last statements in your constructor, your object is complete. As you have experienced, this is not true when you have subclasses, because the superclass constructor will always finish before your subclass has a chance to initialize the object.

Some see this as a flaw in the concept of constructors itself; and some languages don't even use constructors in this sense. You could use an init() method instead. In ordinary methods, you have the advantage of polymorphism, so you can call init() on a Father reference, and Son.init() is invoked; whereas, new Father() always creates a Father object. (of course, in Java you still need to call the right constructor at some point).

But I think what you need is something like this:

class Father {
    public String x;

    public Father() {
        init();
        System.out.println(this);//[2]It is called in Father constructor
        System.out.println(this.x);
    }

    protected void init() {
        x = "Father";
    }

    @Override
    public String toString() {
        return "I'm Father";
    }
}

class Son extends Father {
    @Override
    protected void init() {
        //you could do super.init(); here in cases where it's possibly not redundant
        x = "Son";
    }

    @Override
    public String toString() {
        return "I'm Son";
    }
}

I don't have a name for it, but try it out. It will print

I'm Son
Son

So what's going on here? Your topmost constructor (that of Father) calls an init() method, which is overridden in a subclass. As all constructor call super(); first, they are effectively executed superclass to subclass. So if the topmost constructor's first call is init(); then all of the init happens before any constructor code. If your init method fully initializes the object, then all constructors can work with an initialized object. And since init() is polymorphic, it can even initialize the object when there are subclasses, unlike with the constructor.

Note that init() is protected: subclasses will be able to call and override it, but classes in other package won't be able to call it. That's a slight improvement over public and should be considered for x too.

Silly Freak
  • 4,061
  • 1
  • 36
  • 58
7

As other stated, you cannot override fields, you can only hide them. See JLS 8.3. Field Declarations

If the class declares a field with a certain name, then the declaration of that field is said to hide any and all accessible declarations of fields with the same name in superclasses, and superinterfaces of the class.

In this respect, hiding of fields differs from hiding of methods (§8.4.8.3), for there is no distinction drawn between static and non-static fields in field hiding whereas a distinction is drawn between static and non-static methods in method hiding.

A hidden field can be accessed by using a qualified name (§6.5.6.2) if it is static, or by using a field access expression that contains the keyword super (§15.11.2) or a cast to a superclass type.

In this respect, hiding of fields is similar to hiding of methods.

A class inherits from its direct superclass and direct superinterfaces all the non-private fields of the superclass and superinterfaces that are both accessible to code in the class and not hidden by a declaration in the class.

You can access Father's hidden fields from Son's scope using super keyword, but the opposite is impossible since Father class is not aware of its subclasses.

Community
  • 1
  • 1
ssssteffff
  • 964
  • 4
  • 16
6

While methods can be overridden, attributes can be hidden.

In your case, the attribute x is hidden: in your Son class, you can't access the Father's x value unless you use the super keyword. The Father class doesn't know about the Son's x attribute.

In the opposit, the toString() method is overriden: the implementation that will always be called is the one of the instantiated class (unless it does not override it), i.e. in your case Son, whatever the variable's type (Object, Father...).

sp00m
  • 47,968
  • 31
  • 142
  • 252
2

This is a behaviour done specially to have access to private members. So this.x looks at the variable X which is declared for Father, but when you pass this as a parameter to System.out.println in a method in Father - it looks at the method to call depending on the type of the parameter - in your case Son.

So how do you call the super classes method? Using super.toString(), etc.

From Father it cannot access the x variable of Son.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
tgkprog
  • 4,493
  • 4
  • 41
  • 70
2

Polymorphic method invocations apply only to instance methods. You can always refer to an object with a more general reference variable type ( a superclass or interface ), but at runtime, the ONLY things that are dynamically selected based on the actual object (rather than the reference type) are instance methods NOT STATIC METHODS. NOT VARIABLES. Only overridden instance methods are dynamically invoked based on the real object’s type.

So variable x has not polymorphic behaviour because IT WILL NOT BE SELECTED DYNAMICALLY AT RUNTIME.

Explaining your code :

System.out.println(this);

The Object type is Son so toString() method's Overridden Son version will be invoked.

System.out.println(this.x);

Object type is not in picture here, this.x is in Father class so x variable's Father version will be printed.

See more at: Polymorphism in java

Not a bug
  • 4,286
  • 2
  • 40
  • 80
1

This is commonly referred to as shadowing. Note your class declarations:

class Father {
    public String x = "Father";

and

class Son extends Father {
    public String x = "Son";

This creates 2 distinct variables named x when you create an instance of Son. One x belongs to the Father superclass, and the second x belongs to the Son subclass. Based on the output, we can see that when in the Father scope, this accesses the Father's x instance variable. So the behavior is not related to "what this points to"; it's a result of how the runtime searches for instance variables. It only goes up the class hierarchy to search for variables. A class can only reference variables from itself and its parent classes; it can't access variables from its child classes directly because it doesn't know anything about its children.

To obtain the polymorphic behavior you want, you should only declare x in Father:

class Father {
    public String x;

    public Father() {
        this.x = "Father"
    }

and

class Son extends Father {
    public Son() {
        this.x = "Son"
    }

This article discussed the behavior you're experiencing exactly: http://www.xyzws.com/Javafaq/what-is-variable-hiding-and-shadowing/15.

jpmc26
  • 28,463
  • 14
  • 94
  • 146