2

Всем привет. Изучаю ООП вместе с Java и наткнулся на вот такие грабли: у меня есть класс Animal. В нем есть всего лишь одна переменная name. При создании объекта Animal вызывается конструктор, который выводит на экран сообщение "Новое животное создано. Его имя name.". Далее я создал класс Cat который унаследован от Animal и я хочу, чтобы когда я делал так Animal cat = new Cat("Кошка"); у меня на экран выводилось сообщение "Была создана новая кошка. Ее имя name". Я пытаюсь сделать это в конструкторе класса Cat, но он ругается, говорит, мол можно только так

public Cat(String name) {
        super(name);
    }

Но я так не хочу. Можно ли как то переопределить конструктор в дочернем классе? Или это бред и нужно действовать по другому? Полный код ниже.

public class Main {
    public static void main(String[] args) {
        Animal animal = new Animal("Лео");
        Animal cat = new Cat("Кошка");
    }
}

public class Animal {
    private String name;

    public Animal(String name) {
        this.name = name;
        System.out.println("Новое животное создано. Его имя: " + this.name);
    }
}

public class Cat extends Animal {

    public Cat(String name) {
        super(name);
    }
}
  • добавьте в класс Animal конструктор без аргументов, тогда можно будет переопределить не вызывая конструктор из родительского класса – Artem Aug 19 '16 at 13:58
  • @ArtemKonovalov Тогда я не смогу давать имена животным при создании – Ivan Blohin Aug 19 '16 at 14:16
  • @IvanBlohin Тогда я не смогу давать имена животным при создании на чем основано это утверждение? – Алексей Шиманский Aug 19 '16 at 14:18
  • тогда как вы хотите создать кошку без имени? – Artem Aug 19 '16 at 14:18
  • @ArtemKonovalov исправил формулировку вопроса. И скажите, за одно, пожалуйста, чем отличается Animal cat = new Cat("Кошка"); от Cat cat2 = new Cat("Кошка 2"); ? Результат вывода одинаков – Ivan Blohin Aug 19 '16 at 14:24
  • Cat cat=new Cat("cat") и Animal cat=new Cat("cat") отличаются следующем. В первом случая создается объект типа Cat и ему присваивается ссылка на тот же тип. т.е. по этой ссылке будут поступны методы и поля объявленные и в классе Cat и в классе Animal. Во втором случае тоже создается объект типа Cat но он уже будет доступен через ссылку типа Animal. соответсвенно доступность методов и полей будет другая. – Artem Aug 19 '16 at 14:45

4 Answers4

6

Дело в том, что конструктор по умолчанию (который без параметров) у дочернего класса автоматически вызывает аналогичный конструктор у родительского класса.

Т.е. две записи

public class Person {

    public Person() {
    }
}

public class Employee extends Person {

    public Employee() {
    }
}

и

public class Person {

   public Person() {
   }
}


public class Employee extends Person {

   public Employee() {
       super();
   }
}

будут идентичны.

Однако есть еще правила, связанный с конструкторами.

первое правило:

Если в классе не указано ни одного конструктора, то компилятор сгенерирует конструктор по умолчанию за вас...т.е. если написать так:

public class Person {
}

то все равно в итоге на выходе получим

public class Person {

    public Person() {  }
}

Но (!!!), однако, второе правило:

если сделать хотя бы один конструктор с параметрами, то тогда компилятор конструктор по умолчанию не сгенерирует.

Отсюда вывод:

  • В родительском классе у вас есть конструктор с параметрами (не дефолтный), а значит компилятор дефолтный конструктор сам не сгенерирует.
  • Класс наследник с дефолтным конструктором будет искать дефолтный конструктор в родителе, но его там нет (см. первый пункт почему)
  • Соответсвенно, для того, чтобы что-то сделать в дефолтном конструкторе наследника нужно определить дефолтный конструктор родителя

грубо говоря

class Animal {
    public String name;

    public Animal() {}

    public Animal(String name) {
        this.name = name;
        System.out.println("Новое животное создано. Его имя: " + this.name);
    }
}

class Cat extends Animal {

    public Cat(){
        System.out.print("Была создана новая кошара.");
    }

    public Cat(String name) {
        this();
        System.out.println(" Имя кошары " + name);
    }
}
  • 1
    Ого как все интересно. Спасибо большое, теперь понятнее, буду вникать. А отличие Animal cat = new Cat("Кошка"); от Cat cat2 = new Cat("Кошка 2"); не подскажете? Мне чуть ниже ответили и как я понял, то если я пишу первый вариант, то мне доступно все, что я написал в Cat и Animal, а если второй, то только то, что я написал в Cat, а переменные и методы класса Animal уже не доступны? – Ivan Blohin Aug 19 '16 at 14:48
  • @IvanBlohin, не так понял – Grundy Aug 19 '16 at 14:49
  • А зачем protected? пускай приватное остается если с ним все работы через методы Animal – Grundy Aug 19 '16 at 14:49
  • @Grundy если только так... хотя если будет по-другому, тогда нужно super будет вызывать. А если super вызвать, то нельзя this вызвать....... А-а-а-а-а-а, пристрелите меня)) – Алексей Шиманский Aug 19 '16 at 15:00
0

Что мешает дописать в конструкторе класса потомка вывод того что было создано:

public Cat(String name){
    super(name);
    System.out.println("создана кошка!");
}

Как вариант, можно сделать Animal абстрактным и определить в нем абстрактный метод getName() тогда можно обойтись без всяких переопределений коснтруктора.

    public Animal() {
        System.out.println("создана "+getName());
    }

    protected abstract String getName();
Artem
  • 14,967
0

Как вы уже поняли из предыдущих ответов, переопределять родительский конструктор нельзя. Вызывая конструктор вы вызываете цепочку конструкторов родителей. Вообще рекомендуется для задания значения полей объектов использовать методы, так называемыt сеттеры. То есть void setName(String name). Если вам нужно обязательно при создании объекта присвоить имя. То лучше в конструкторе вызывать метод setName. Однако, при наследовании данный метод будет иметь доступ именно к родительской переменной. Вообще Animal лучше сделать абстрактным классом или интерфейсом.

Mr.Ki.D.
  • 503
  • 6
  • 11
-1

"А отличие Animal cat = new Cat("Кошка"); от Cat cat2 = new Cat("Кошка 2"); " - если вы изучаете ООП, то это тема уже Полиморфизма. Вкратце, классы , как вы помните это структуры (типы данных) с методами.Как говорит Брюс Эккель в Философия java - "..Везде, где видите класс понимайте тип и наоборот" Создавая классы вы создаете свои типы данных. Вы создали 2 типа данных Animal и Cat. Первая переменная cat типа Animal, а вторая cat2 типа Cat. Но так как класс Cat является потомком(extends) класса Animal, мы можем использовать полиморфизм. Вкратце, полиморфизм это - "возможность работать с несколькими типами так, как будто это один и тот же тип и в то же время поведение каждого типа будет уникальным в зависимости от его реализации." Пример, У вас могут быть различный потомки Animal - Dog, Fox, Wulf, Bair и тд и тп, и все их можно объявить и обработать как Animal. Например создать массив Animal animals[] и внутри хранить экземпляры и Dog и Fox и чего угодно, что является (extends) Animal.

Mr.Ki.D.
  • 503
  • 6
  • 11
  • написал сюда, т.к. ответ не влез в комментарий – Mr.Ki.D. Aug 19 '16 at 15:19
  • Ага! Вон оно как. 5 минут назад наткнулся на канал Якова Файна, смотрю ролик на эту тему, спасибо) Слышал раньше о полиморфизме, но думал, что не скоро наткнусь, а оно сплошь и рядом.. – Ivan Blohin Aug 19 '16 at 15:44
  • На самом деле не зацикливайтесь на теории без практики. Не любил всегда эти примеры абстрактные Animal. Применение наследования(расширения или реализации) уже дело скорее архитектуры. Врядли вы будете проектировать класс Animal. Также могу вам посоветовать книгу Философия Java. must read для java программиста. – Mr.Ki.D. Aug 19 '16 at 16:02