2013-07-06 3 views
2

Я пошел «обновлять» свою Java, только чтобы понять, что, по-видимому, я не понимаю базовых понятий! Вот простой один я не могу понять:Java abstract class + inheritance - разрешение области или имени

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, и подкласс:

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; 
    } 
} 

И тогда я бегу простой пример, ожидая «T1000» для печати:

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

Нет кубиков! Печатается «NONAME». До этого, я получаю этот выход из конструкторов:

  • Изготовление родовой NONAME робота, типа: класс com.akarpov.tutorial.Terminator
  • Создание T1000 терминатор, типа: класс com.akarpov.tutorial.Terminator

Итак, хорошо, я вижу, что Java понимает, что экземпляр экземпляра моего класса - это Terminator, что и сказано в «новом». И, очевидно, экземпляр Terminator хранит копию модели == «T1000». Но, проверяя объект r1 в отладчике (IntelliJ), я вижу две переменные с именем «модель» на разных адресах (очевидно) с разными строками. И, очевидно, как показывает вывод, getModel в абстрактном классе выбирает значение по умолчанию, определенное в классе Robot, а не тот, который передается конструктору Terminator (и сохраняется внутри объекта).

Что я не понимаю об абстрактных классах и наследовании, и как бы я получил значение по умолчанию и поведение по умолчанию (т.е. getModel), которое собирает конкретные данные (т. Е. «T1000») в подкласс? Спасибо! И мои извинения, если это было рассмотрено много раз раньше - я выглядел, но на меня не набросилось.

ответ

1

Проблема, с которой вы сталкиваетесь, состоит в том, что вы действительно создаете две переменные. Теперь, с кодом, который у вас есть, когда вы вызываете r1.getModel(), вы получите исходный базовый класс model.

Если вы хотите установить модель из подкласса, у вас есть несколько вариантов. Вы можете перейти к тому, что вы начали с объявления нового String model, но затем вы должны переопределить метод getModel() из суперкласса, чтобы ваш подкласс посмотрел на свой собственный model вместо суперкласса 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; 
    } 
} 

Другим вариантом было бы создать общедоступную защищенного метода сеттер в суперкласс для 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; 
    } 
} 

И тогда вы бы просто иметь свой Terminator объект вызова setModel(model), прежде чем он называется getModel(), и вы бы желаемый результат.

+0

Вы правы, конечно - это то, что я понял через 2 минуты после публикации. Я уже принял ответ, хотя, иначе я бы забрал твое! – alexakarpov

2

Ваша проблема с частным модификатором ... Параметр модели существует два раза отдельно в двух классах. Частное означает только видимое для этого класса. Вы можете использовать метод setter.

+0

Я понял это через 2 минуты после публикации, когда я пошел и поговорил с моим супругом об этом - проблема была не с частной (я действительно пытался защитить), а с тем, что я * скрыл * модель внутри Robot другим модель внутри Terminator! Удаление этой переменной из Terminator разрешило дело. – alexakarpov

+0

... подумайте об этом, вы более верны, чем я думал сначала, потому что я действительно пренебрег тем, что рядовые не наследуются. – alexakarpov

1

О, черт возьми, я получил его сразу после публикации. Моя ошибка заключалась в объявлении еще одной модели String в Terminator, которая привела к скрытию модели в Robot - отсюда и две копии. Снятие решения проблемы. Argh!

+0

Просто удаление объявления переменной не могло решить проблему. Вам также нужно добавить конструктор String в базовый класс, делегировать его из подкласса и удалить 'this.model = model' в конструкторе подкласса. –

+0

Я думаю, что вы в целом правы - но у базового класса Robot было поле модели, инициализированное «NONAME» по умолчанию в моем случае. Итак, если мое понимание правильное, удаление поля модели из Terminator сделало модель суперкласса единственной и единственной - и это единственная модель в разрешенном подклассе. – alexakarpov

+1

Частные члены не наследуются, поэтому это будет работать только в том случае, если вы объявили 'Terminator' статическим вложенным классом внутри' Robot'. Или, конечно же, если вы подняли уровень доступа 'model' тем временем. –

0

Давайте пройдем через эту строку кода

Robot r1 = new Terminator("T1000"); 

Так что это вызывает конструктор Terminator (String). Первое, что делает конструктор, явно вызывает конструктор суперкласса. Это было бы сделано автоматически, но вы написали super() явно, и все в порядке. Конструктор суперкласса делает 1 вещь:

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

Ok, так что он печатает «Создание общего NONAME робот, типа: Терминатор», потому что это то, что метод видит. Он не имеет локальной ссылки на любую «модельную» переменную, поэтому он использует переменную экземпляра, определенную в классе Robot.Затем управление возвращается конструктору Terminator, который продолжается распечатать

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

Но на этот раз он работает, как вы ожидаете, потому что переменная модель была принят классом, вызывающего метод, поэтому тень переменного экземпляра. Поэтому его значение равно «T1000». Надеюсь, это имеет смысл.

+0

Да, вы правы. Это объясняет, почему в конструкторе Terminator я вижу «T1000»; но мой вопрос скорее заключался в том, что абстрактный класс с общим «getModel» поведением каким-то образом «делил состояние» с подклассом, который не работал из-за того, что я назначил «T1000» переменной экземпляра подкласса, тогда как метод суперкласса мог см. его собственную - отдельную переменную экземпляра. Тем не менее, принимая ваш ответ за указание локального экземпляра, который я также пропустил! – alexakarpov