简体   繁体   中英

Java abstract class + inheritance — scope or name resolution

I went to 'refresh' my Java, only to realize that apparently I don't understand basic concepts! Here's a simple one I can't figure out:

public abstract class Robot {

    private String model = "NONAME";

    public Robot() {
        System.out.println("Making a generic " + model + " robot, type: " + this.getClass());
    }

    public String getModel() {
        return model;
    }
}

OK, and the subclass:

public class Terminator extends Robot {
    private String model;

    public Terminator(String model) {
        super();
        System.out.println("Making a " + model + " terminator, type: " + this.getClass());
        this.model = model;
    }
}

And then I run a simple example, expecting "T1000" to be printed:

    Robot r1 = new Terminator("T1000");
    System.out.println(r1.getModel());

No dice! "NONAME" is printed. Before that, I get this output from constructors:

  • Making a generic NONAME robot, type: class com.akarpov.tutorial.Terminator
  • Making a T1000 terminator, type: class com.akarpov.tutorial.Terminator

So, OK, I see that Java picks up the fact that runtime instance of my class is Terminator, which is what 'new' is told to make. And, obviously, the Terminator instance does keep the copy of the model == "T1000". But inspecting the r1 object in a debugger (IntelliJ) I see two variables named 'model', at different addresses (obviously), with different strings. And, obviously, as the output suggests, the getModel in the abstract class picks up the default value defined in the Robot class, not the one passed to the Terminator's constructor (and retained inside the object).

What is it I don't understand about abstract classes and inheritance, and how would I go about having a default value AND default behavior (ie getModel), which picks up the specific data (ie "T1000") in a subclass? Thanks! And my apologies if this has been aswered many times before - I looked but nothing jumped at me.

Your issue is with the private modifier... The model variable exists twice separately in the two classes. Private means only visible to that class. You may want to use a setter method.

Oh gosh I got it right after posting. My mistake was in declaring another String model in the Terminator, which led to hiding the model in Robot -- hence the two copies. Removing it solved the problem. Argh!

The issue you are having is that you are indeed creating two variables. Now, with the code you have in place, when you call r1.getModel() you will get the original base class model .

If you want to be able to set model from a subclass you have a few options. You can go the direction that you started by declaring a new String model , but you must then go on to override the getModel() method from the superclass so that your subclass will look to its own model instead of the superclass model .

public class Terminator extends Robot {
    private String model;

    public Terminator(String model) {
        super();
        System.out.println("Making a " + model + " terminator, type: " + this.getClass());
        this.model = model;
    }

    @Override
    public String getModel(){
    return model;
    }
}

Another option would be to create a public of protected setter method in the superclass for model .

public abstract class Robot{

    private String model = "NONAME";

    public Robot() {
        System.out.println("Making a generic " + model + " robot, type: " + this.getClass());
    }

    public String getModel() {
        return model;
    }

    protected void setModel(String str){
        this.model = str;
    }
}

And then you would just have your Terminator object call setModel(model) before it called getModel() and you would have the desired output.

Let's walk through this line of code

Robot r1 = new Terminator("T1000");

So this calls the Terminator(String) constructor. The first thing the constructor does is call the superclass constructor explicitly. It would have done it automatically, but you've written super() explicitly and that's fine. The superclass constructor does 1 thing:

System.out.println("Making a generic " + model + " robot, type: " + this.getClass());

Ok, so it prints out "Making a generic NONAME robot, type: Terminator" because that is what the method sees. It has no local reference to any "model" variable, so it uses the instance variable defined within the Robot class. Then control is given back to the Terminator constructor, which proceeds to print out

System.out.println("Making a " + model + " terminator, type: " + this.getClass());

But this time it works how you expect because the model variable was passed by the class calling the method, so it shadows your instance variable. Therefore its value is "T1000". Hopefully that makes sense

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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