Подтвердить что ты не робот

Почему мы назначаем родительскую ссылку на дочерний объект в Java?

Я задаю довольно простой вопрос, но я немного смущен этим.

Предположим, что у меня есть класс Parent:

public class Parent {

    int name;
}

И еще один класс Child.java:

public class Child extends Parent{

    int salary;
}

И, наконец, мой класс Main.java

public class Main {

    public static void main(String[] args)
    {
        Parent parent = new Child();
        parent.name= "abcd";
    }
}

Если я создаю дочерний объект, например

Child child = new Child():

Затем объект child может обращаться к переменным name and salary.

Мой вопрос:

Parent parent = new Child();

предоставляет доступ только к переменной name класса Parent. Итак, каково точное использование этой строки?

 Parent parent = new Child();

А также, когда он использует динамический полиморфизм, то почему переменная дочернего класса недоступна после этого

Parent parent = new Child();
4b9b3361

Ответ 1

Во-первых, уточнение терминологии: мы присваиваем объект Child переменной типа Parent. Parent является ссылкой на объект, который является подтипом Parent, a Child.

Это полезно только в более сложном примере. Представьте, что вы добавляете getEmployeeDetails в класс Parent:

public String getEmployeeDetails() {
    return "Name: " + name;
}

Мы могли бы переопределить этот метод в Child, чтобы предоставить более подробную информацию:

@Override
public String getEmployeeDetails() {
    return "Name: " + name + " Salary: " + salary;
}

Теперь вы можете написать одну строку кода, которая получает любую информацию, будь то объект Parent или Child:

parent.getEmployeeDetails();

Следующий код:

Parent parent = new Parent();
parent.name = 1;
Child child = new Child();
child.name = 2;
child.salary = 2000;
Parent[] employees = new Parent[] { parent, child };
for (Parent employee : employees) {
    employee.getEmployeeDetails();
}

Результат:

Name: 1
Name: 2 Salary: 2000

Мы использовали Child как Parent. У него было специализированное поведение, уникальное для класса Child, но когда мы назвали getEmployeeDetails(), мы могли бы игнорировать разницу и сосредоточиться на том, как Parent и Child схожи. Это называется подтип полиморфизма.

В обновленном вопросе спрашивается, почему Child.salary недоступен, если объект Child хранится в ссылке Parent. Ответ - это пересечение "полиморфизма" и "статической типизации". Поскольку Java статически типизируется во время компиляции, вы получаете определенные гарантии от компилятора, но вы вынуждены следовать правилам в обмен или код не будет компилироваться. Здесь соответствующая гарантия заключается в том, что каждый экземпляр подтипа (например, Child) может использоваться как экземпляр его супертипа (например, Parent). Например, вам гарантировано, что при доступе к employee.getEmployeeDetails или employee.name метод или поле определяется для любого непустого объекта, который может быть назначен переменной employee типа Parent. Чтобы сделать эту гарантию, компилятор учитывает только этот статический тип (в основном, тип ссылки на переменные, Parent) при принятии решения о том, к чему вы можете получить доступ. Таким образом, вы не можете получить доступ к элементам, определенным в типе среды выполнения, Child.

Если вы действительно хотите использовать Child как Parent, это простое ограничение для жизни, и ваш код будет использоваться для Parent и всех его подтипов. Если это неприемлемо, введите тип ссылки Child.

Ответ 2

Он позволяет получить доступ ко всем подклассам через общий родительский интерфейс. Это полезно для запуска общих операций, доступных во всех подклассах. Требуется лучший пример:

public class Shape
{
  private int x, y;
  public void draw();
}

public class Rectangle extends Shape
{ 
  public void draw();
  public void doRectangleAction();
}

Теперь, если у вас есть:

List<Shape> myShapes = new ArrayList<Shape>();

Вы можете ссылаться на каждый элемент в списке как на фигуру, вам не нужно беспокоиться, если это прямоугольник или какой-либо другой тип, например, сказать "Круг". Вы можете относиться к ним одинаково; вы можете нарисовать их все. Вы не можете вызвать doRectangleAction, потому что вы не знаете, является ли Shape действительно прямоугольником.

Это торговля, которую вы делаете между обработкой объектов в общем виде и, в частности, с особым обращением.

Действительно, я думаю, вам нужно больше узнать о ООП. Хорошая книга должна помочь: http://www.amazon.com/Design-Patterns-Explained-Perspective-Object-Oriented/dp/0201715945

Ответ 3

Если вы назначаете родительский тип подклассу, это означает, что вы соглашаетесь использовать общие функции родительского класса.

Это дает вам свободу абстрагироваться от разных реализаций подкласса. В результате вы ограничиваете себя родительскими функциями.

Однако этот тип присвоения называется upcasting.

Parent parent = new Child();  

Противоположность - сокращение.

Child child = (Child)parent;

Итак, если вы создаете экземпляр Child и опускаете его на Parent, вы можете использовать атрибут type name. Если вы создаете экземпляр Parent, вы можете сделать то же самое, что и в предыдущем случае, но вы не можете использовать salary, потому что в Parent нет такого атрибута. Вернитесь к предыдущему случаю, который может использовать salary, но только если downcasting на Child.

Более подробное объяснение

Ответ 4

При компиляции вашей программы опорная переменная базового класса получает память, а компилятор проверяет все методы этого класса. Поэтому он проверяет все методы базового класса, но не методы дочернего класса. Теперь во время выполнения, когда объект создан, могут выполняться только проверенные методы. Если функция переопределена в дочернем классе, эта функция запускается. Другие функции дочернего класса не выполняются. Coz-компилятор не распознал их во время компиляции.

Ответ 5

Предположим, вы хотели бы иметь массив экземпляров родительского класса и набор дочерних классов Child1, Child2, Child3, расширяющих Parent. Бывают ситуации, когда вас интересует только реализация родительского класса, которая является более общей и не заботится о более конкретных вещах, введенных дочерними классами.

Ответ 6

Эта ситуация возникает, когда у вас есть несколько реализаций. Позволь мне объяснить. Предположим, у вас есть несколько алгоритмов сортировки, и вы хотите выбрать во время исполнения тот, который нужно реализовать, или вы хотите дать кому-то еще возможность добавить его реализацию. Чтобы решить эту проблему, вы обычно создаете абстрактный класс (Родитель) и имеете разную реализацию (Ребенок). Если вы пишете:

Child c = new Child();

вы привязываете свою реализацию к классу Child, и вы больше не можете ее изменять. В противном случае, если вы используете:

Parent p = new Child();

пока Child extends Parent вы можете изменить его в будущем, не изменяя код.

То же самое можно сделать с помощью интерфейсов: Parent больше не является классом, а интерфейсом java.

В общем случае вы можете использовать этот подход в шаблоне DAO, где вы хотите иметь несколько зависимых от DB реализаций. Вы можете взглянуть на FactoryPatter или AbstractFactory Pattern. Надеюсь, это поможет вам.

Ответ 7

Это просто.

Parent parent = new Child();

В этом случае тип объекта Parent. Ant Parent имеет только одно свойство. Это name.

Child child = new Child();

И в этом случае тип объекта Child. Ant Child имеет два свойства. Это name и salary.

Дело в том, что нет необходимости инициализировать не конечное поле сразу после объявления. Обычно это выполняется во время выполнения, потому что часто вы не можете точно знать, какая именно реализация вам понадобится. Например, представьте, что у вас есть иерархия классов с классом Transport во главе. И три подкласса: Car, Helicopter и Boat. И есть еще один класс Tour, который имеет поле Transport. То есть:

class Tour {
   Transport transport;
}  

Пока пользователь не заказал поездку и не выбрал определенный тип транспорта, вы не можете инициализировать это поле. Это первое.

Во-вторых, предположим, что все эти классы должны иметь метод go(), но с другой реализацией. Вы можете определить базовую реализацию по умолчанию в суперклассе Transport и собственные уникальные реализации в каждом подклассе. С помощью этой инициализации Transport tran; tran = new Car(); вы можете вызвать метод tran.go() и получить результат, не беспокоясь о конкретной реализации. Метод itll вызывает переопределенный метод из определенного подкласса.

Кроме того, вы можете использовать экземпляр подкласса везде, где используется экземпляр суперкласса. Например, вы хотите предоставить возможность арендовать ваш транспорт. Если вы не используете полиморфизм, вам нужно написать много методов для каждого случая: rentCar(Car car), rentBoat(Boat boat) и т.д. В то же время полиморфизм позволяет создать один универсальный метод rent(Transport transport). Вы можете передать в него объект любого подкласса Transport. Кроме того, если со временем ваша логика будет увеличиваться, и вам нужно будет создать еще один класс в иерархии? При использовании полиморфизма вам ничего не нужно менять. Просто добавьте класс Transport и передайте новый класс в метод:

public class Airplane extends Transport {
    //implementation
}

и rent(new Airplane()). И new Airplane().go() во втором случае.

Ответ 8

Вы объявляете родителя как родителя, поэтому java будет предоставлять только методы и атрибуты класса Parent.

Child child = new Child();

должен работать. Или

Parent child = new Child();
((Child)child).salary = 1;