Java에서 자식 개체에 부모 참조를 할당하는 이유는 무엇입니까?


84

나는 아주 간단한 질문을하고 있지만 이것에 약간 혼란스러워합니다.

클래스가 있다고 가정합니다 Parent.

public class Parent {

    int name;
}

그리고 다른 수업이 있습니다 Child.

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();

nameParent 클래스의 변수 만 액세스 할 수 있습니다 . 그래서이 선의 정확한 사용은 무엇입니까 ??

 Parent parent = new Child();

또한 동적 다형성을 사용하는 경우이 작업을 수행 한 후 자식 클래스의 변수에 액세스 할 수없는 이유

Parent parent = new Child();

1
여기에서 동물의 예를 읽으십시오 : en.wikipedia.org/wiki/…
mrswadge

귀하의 예에서는 실제로 도움이되지 않습니다. 그러나 부모 참조로 무언가를 수행하는 방법이 있다고 생각하십시오. 예를 들어 Main 클래스 어딘가에 있습니다. 부모 개체 참조를 가져 와서 관련 작업을 수행합니다. 부모 개체 만 수행하더라도 Child 개체와 같은 Parent의 하위 클래스도 허용하는 것이 좋습니다. 또한 더 흥미로운 것을 위해 동적 바인딩 을 살펴보십시오 .
Andrei Bârsan

이것이 name올바른 유형입니까?
Roman C

답변:


53

첫째, 용어 설명 : Child객체를 유형의 변수에 할당합니다 Parent. , a Parent의 하위 유형이되는 객체에 대한 참조 입니다.ParentChild

더 복잡한 예에서만 유용합니다. getEmployeeDetailsParent 클래스에 추가한다고 상상해보십시오 .

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()우리는 방법에 차이 초점을 무시할 수 ParentChild유사하다. 이를 하위 유형 다형성 이라고 합니다.

업데이트 된 질문은 객체가 참조에 저장 Child.salary될 때 액세스 할 수없는 이유를 묻습니다 . 대답은 "다형성"과 "정적 타이핑"의 교차점입니다. Java는 컴파일 시간에 정적으로 형식화되기 때문에 컴파일러로부터 특정 보장을 받지만 교환 규칙을 따르지 않으면 코드가 컴파일되지 않습니다. 여기서 관련 보장은 하위 유형의 모든 인스턴스 (예 :)가 상위 유형 (예 :)의 인스턴스로 사용될 수 있다는 것 입니다. 예를 들어, 액세스 하거나 메소드 또는 필드가 유형 의 변수 에 할당 될 수있는 널이 아닌 객체에 정의되어 있음을 보장합니다.ChildParentChildParentemployee.getEmployeeDetailsemployee.nameemployeeParent. 이를 보장하기 위해 컴파일러 Parent는 액세스 할 수있는 항목을 결정할 때 해당 정적 유형 (기본적으로 변수 참조 유형) 만 고려 합니다. 따라서 개체의 런타임 유형에 정의 된 멤버에 액세스 할 수 없습니다 Child.

당신이 진정 Child으로 a 를 사용하고 싶을 때 Parent이것은 함께 살기 쉬운 제한이며 당신의 코드는 Parent모든 하위 유형에 대해 사용할 수 있습니다 . 허용되지 않는 경우 참조 유형을 작성하십시오 Child.


22

프로그램을 컴파일 할 때 기본 클래스의 참조 변수가 메모리를 가져오고 컴파일러는 해당 클래스의 모든 메서드를 확인합니다. 따라서 모든 기본 클래스 메서드를 확인하지만 자식 클래스 메서드는 확인하지 않습니다. 이제 개체가 생성 될 때 런타임에 확인 된 메서드 만 실행할 수 있습니다. 함수가 실행되는 자식 클래스에서 메서드가 재정의 된 경우. 컴파일러가 컴파일 타임에 인식하지 않았기 때문에 자식 클래스의 다른 함수는 실행되지 않습니다.


1
이 대답은 의미가 있습니다! 감사합니다
user7098526

13

공통 상위 인터페이스를 통해 모든 하위 클래스에 액세스 할 수 있습니다. 이는 모든 하위 클래스에서 사용 가능한 공통 작업을 실행하는 데 유용합니다. 더 나은 예가 필요합니다.

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>();

목록의 모든 항목을 Shape로 참조 할 수 있습니다. Rectangle 또는 Circle과 같은 다른 유형인지 걱정할 필요가 없습니다. 그들 모두를 똑같이 대할 수 있습니다. 모두 그릴 수 있습니다. Shape가 실제로 직사각형인지 모르기 때문에 doRectangleAction을 호출 할 수 없습니다.

이것은 일반적인 방식으로 물건을 취급하는 것과 구체적으로 취급하는 것 사이의 거래입니다.

정말 OOP에 대해 더 읽어야한다고 생각합니다. 좋은 책이 도움이 될 것입니다 : http://www.amazon.com/Design-Patterns-Explained-Perspective-Object-Oriented/dp/0201715945


2
인용 된 예에서 "추상 클래스"또는 "인터페이스"를 사용하고 있습니까? 단순한 클래스는 구현되지 않은 메서드를 가질 수 없기 때문입니다.
HQuser

1
@HQuser-잘 발견되었습니다. 질문의 맥락에서 다형성에 대해 더 많이 설명하고 싶은 것 같지만 확실히 수정이 필요합니다.
Vivek

11

상위 유형을 하위 클래스에 할당하면 상위 클래스의 공통 기능을 사용하는 데 동의하는 것입니다.

다른 서브 클래스 구현에서 자유롭게 추상화 할 수 있습니다. 결과적으로 상위 기능으로 제한됩니다.

그러나 이러한 유형의 할당을 업 캐스팅이라고합니다.

Parent parent = new Child();  

그 반대는 다운 캐스팅입니다.

Child child = (Child)parent;

따라서 인스턴스를 생성 Child하고 다운 캐스트하면 Parent해당 유형 속성을 사용할 수 있습니다 name. 인스턴스를 생성 Parent하면 이전 사례와 동일하게 할 수 있지만 salary.NET Framework에 이러한 속성이 없기 때문에 사용할 수 없습니다 Parent. 사용할 수 salary있지만로 다운 캐스팅하는 경우에만 이전 케이스로 돌아갑니다 Child.

더 자세한 설명이 있습니다


그래서 나는 Parent parent = new Parent () 할 수 있습니다. 왜 그럴까요? PLZ는 나를 도와줍니다.
Narendra Pal

사실이지만 여전히 원하는 이유를 설명하지 않습니다.
John Watts

그 이유는 무엇을하고 싶은지에 따라 다릅니다.
Roman C

할당 Parent하면 사용할 수 있습니다 Parent(가능한 경우) Child를 사용할 수 있습니다 Child.
Roman C

1
Child child = (Child) parent; 그러면 ClassCastException이 발생합니다. 부모 개체를 자식 참조 변수로 다운 캐스트 할 수 없습니다.
Muhammad Salman Farooq

7

간단 해.

Parent parent = new Child();

이 경우 개체의 유형은입니다 Parent. Ant Parent에는 하나의 속성 만 있습니다. 그것은이다 name.

Child child = new Child();

이 경우 개체의 유형은입니다 Child. Ant Child에는 두 가지 속성이 있습니다. 그들은 namesalary.

사실 선언에서 즉시 non-final 필드를 초기화 할 필요가 없습니다. 일반적으로 이것은 정확히 어떤 구현이 필요한지 정확히 알 수 없기 때문에 런타임에 수행됩니다. 예를 들어 Transport머리에 클래스가있는 클래스 계층 구조가 있다고 가정합니다 . 그리고 세 개의 하위 클래스 : Car, HelicopterBoat. 그리고 Tourfield가있는 또 다른 클래스 가 있습니다 Transport. 그건:

class Tour {
   Transport transport;
}  

사용자가 여행을 예약하지 않았고 특정 유형의 교통 수단을 선택하지 않은 한이 필드를 초기화 할 수 없습니다. 첫 번째입니다.

둘째, 이러한 모든 클래스에는 메서드가 go()있지만 구현이 달라야 한다고 가정합니다 . 기본적으로 수퍼 클래스에서 기본 구현을 정의하고 Transport각 하위 클래스에서 고유 한 구현을 정의 할 수 있습니다 . 이 초기화 Transport tran; tran = new Car();를 사용하면 tran.go()특정 구현에 대해 걱정하지 않고 메서드를 호출하고 결과를 얻을 수 있습니다. 특정 하위 클래스에서 재정의 된 메서드를 호출합니다.

또한 수퍼 클래스의 인스턴스가 사용되는 모든 곳에서 서브 클래스의 인스턴스를 사용할 수 있습니다. 예를 들어, 당신은 당신의 교통 수단을 임대 할 기회를 제공하고자합니다. : 당신은 다형성을 사용하지 않는 경우, 각각의 경우에 대한 방법을 많이 작성해야 rentCar(Car car), rentBoat(Boat boat)등등합니다. 동시에 다형성을 통해 하나의 보편적 인 방법을 만들 수 있습니다 rent(Transport transport). 의 모든 하위 클래스의 객체를 전달할 수 있습니다 Transport. 또한 시간이 지남에 따라 논리가 증가하고 계층 구조에 다른 클래스를 만들어야하는 경우? 다형성을 사용할 때 아무것도 변경할 필요가 없습니다. 클래스를 확장 Transport하고 새 클래스를 메서드에 전달하면됩니다.

public class Airplane extends Transport {
    //implementation
}

rent(new Airplane()). 그리고 new Airplane().go()두 번째 경우.


내가 알고 있지만 내 질문은 왜 그렇게해야합니까? 부모 참조를 얻기 위해 Parent p = new Parent ()를 할 수 있다면.
Narendra Pal

이것은 다형성 입니다. 매우 유용합니다. 다형성은 프로그램을보다 유연하고 확장 가능하게 만듭니다. 이것이 정확히 얼마나 유용 할 수 있습니까? 업데이트 된 답변 에서 내 예를 참조하십시오 .
Kapelchik

1
교통 수단의 훌륭한 예 ... 쉽게 이해됩니다. 감사합니다!
박찬우

2

이 상황은 여러 구현이있을 때 발생합니다. 설명하겠습니다. 몇 가지 정렬 알고리즘이 있고 런타임에 구현할 알고리즘을 선택하거나 다른 사람에게 그의 구현을 추가 할 수있는 기능을 제공하려고한다고 가정합니다. 이 문제를 해결하기 위해 일반적으로 추상 클래스 (Parent)를 만들고 다른 구현 (Child)을 사용합니다. 당신이 쓰는 경우 :

Child c = new Child();

구현을 Child 클래스에 바인딩하면 더 이상 변경할 수 없습니다. 그렇지 않으면 다음을 사용하는 경우 :

Parent p = new Child();

Child가 Parent를 확장하는 한 나중에 코드를 수정하지 않고 변경할 수 있습니다.

인터페이스를 사용하여 동일한 작업을 수행 할 수 있습니다. 부모는 더 이상 클래스가 아니라 자바 인터페이스입니다.

일반적으로 여러 DB 종속 구현을 원하는 DAO 패턴에서이 접근 방식을 사용할 수 있습니다. FactoryPatter 또는 AbstractFactory Pattern을 살펴볼 수 있습니다. 이것이 당신을 도울 수 있기를 바랍니다.


1

부모 클래스의 인스턴스 배열과 부모를 확장하는 자식 클래스 Child1, Child2, Child3 집합을 원한다고 가정 해 보겠습니다. 보다 일반적인 부모 클래스 구현에만 관심이 있고 자식 클래스에 의해 도입 된 더 구체적인 내용 에는 관심이없는 상황이 있습니다.


1

위의 모든 설명은 OOP (Object Oriented Programming)를 처음 접하는 사람들에게는 너무 기술적이라고 생각합니다. 몇 년 전, (Jr Java 개발자로서) 이것에 대해 머리를 감싸는 데 시간이 걸렸고 실제로 우리가 실제로 호출하는 실제 클래스를 숨기기 위해 부모 클래스 또는 인터페이스를 사용하는 이유를 이해하지 못했습니다.

  1. 즉각적인 이유는 복잡성을 숨기고 호출자가 자주 변경할 필요가 없도록하기위한 것입니다 (비전문가의 용어로 해킹 및 도난 당함). 이것은 특히 당신의 목표가 버그 생성을 피하는 것이라면 많은 의미가 있습니다. 그리고 코드를 더 많이 수정할수록 그 중 일부가 당신에게 영향을 미칠 가능성이 높아집니다. 반면에 코드 만 확장하면 한 번에 한 가지에만 집중하고 이전 코드가 변경되지 않거나 조금만 변경되기 때문에 버그가 발생할 가능성이 훨씬 적습니다. 의료계 직원이 프로필을 만들 수있는 간단한 응용 프로그램이 있다고 가정 해보십시오. 단순화를 위해 일반의, 외과의 및 간호사 만 있다고 가정 해 보겠습니다 (실제로는 더 많은 특정 직업이 있습니다). 각 직업에 대해 몇 가지 일반 정보를 저장하고 일부는 해당 전문가에게만 저장하려고합니다. 예를 들어, 외과 의사는 firstName, lastName, yearsOfExperience와 같은 일반 필드를 일반 필드로 가질 수 있지만 특정 필드 (예 : "Bone Surgery", "Eye Surgery"등과 유사한 내용을 가진 List와 같은 목록 인스턴스 변수에 저장된 전문화)도 가질 수 있습니다. 간호사는 그 중 어떤 것도 가지고 있지 않지만 익숙한 절차를 나열 할 수 있으며 GeneralPractioners는 자신의 세부 사항을 가지고 있습니다. 결과적으로 특정 프로필을 저장하는 방법. 그러나 ProfileManager 클래스가 이러한 차이점에 대해 알기를 원하지 않습니다. 응용 프로그램이 물리 치료사, 심장 전문의, 종양 전문의 등과 같은 더 많은 의료 직업을 포함하도록 기능을 확장함에 따라 시간이 지남에 따라 필연적으로 변경되고 증가하기 때문입니다. ProfileManger가 원하는 것은 누구의 프로필을 저장하든 상관없이 save ()라고 말하면됩니다. 따라서 인터페이스, 추상 클래스 또는 부모 클래스 (일반 의료 직원 생성을 허용하려는 경우)를 숨기는 것이 일반적입니다. 이 경우 Parent 클래스를 선택하고 MedicalEmployee라고합니다. 내부적으로는이를 확장하는 위의 특정 클래스를 참조 할 수 있습니다. ProfileManager가 myMedicalEmployee.save ()를 호출 할 때 save () 메서드는 원래 프로필을 만드는 데 사용 된 올바른 클래스 유형으로 다형성 (다양성)으로 확인됩니다 (예 : Nurse 및 save () 메서드 호출). 수업. 또는 부모 클래스 (일반 의료 직원 생성을 허용하려는 경우). 이 경우 Parent 클래스를 선택하고 MedicalEmployee라고합니다. 내부적으로는이를 확장하는 위의 특정 클래스를 참조 할 수 있습니다. ProfileManager가 myMedicalEmployee.save ()를 호출 할 때 save () 메서드는 원래 프로필을 만드는 데 사용 된 올바른 클래스 유형으로 다형성 (다양성)으로 확인됩니다 (예 : Nurse 및 save () 메서드 호출). 수업. 또는 부모 클래스 (일반 의료 직원 생성을 허용하려는 경우). 이 경우 Parent 클래스를 선택하고 MedicalEmployee라고합니다. 내부적으로는이를 확장하는 위의 특정 클래스를 참조 할 수 있습니다. ProfileManager가 myMedicalEmployee.save ()를 호출 할 때 save () 메서드는 원래 프로필을 만드는 데 사용 된 올바른 클래스 유형으로 다형성 (다양성)으로 확인됩니다 (예 : Nurse 및 save () 메서드 호출). 수업.

  2. 대부분의 경우 런타임에 어떤 구현이 필요한지 알지 못합니다. 위의 예에서 일반의, 외과의 또는 간호사가 프로필을 생성할지 여부를 알 수 없습니다. 그러나 완료되면 해당 프로필을 저장해야한다는 것을 알고 있습니다. MedicalEmployee.profile ()이 정확히 수행합니다. MedicalEmployee의 각 특정 유형 (일반의, 외과 의사, 간호사,

  3. 위의 (1)과 (2)의 결과는 이제 새로운 의료 직업을 추가하고, 각각의 새 클래스에서 save ()를 구현하여 MedicalEmployee에서 save () 메서드를 재정의 할 수 있으며, 다음에서 ProfileManager를 수정할 필요가 없습니다. 모두.


1

나는 이것이 매우 오래된 스레드라는 것을 알고 있지만 한 번 같은 의심을 보았습니다.

따라서의 개념은 Parent parent = new Child();자바의 초기 및 후기 바인딩과 관련이 있습니다.

private, static 및 final 메서드의 바인딩은 재정의 할 수없고 일반적인 메서드 호출과 오버로드 된 메서드가 초기 바인딩의 예이므로 컴파일시 발생합니다.

예를 고려하십시오.

class Vehicle
{
    int value = 100;
    void start() {
        System.out.println("Vehicle Started");
    }

    static void stop() {
        System.out.println("Vehicle Stopped");
    }
}

class Car extends Vehicle {

    int value = 1000;

    @Override
    void start() {
        System.out.println("Car Started");
    }

    static void stop() {
        System.out.println("Car Stopped");
    }

    public static void main(String args[]) {

        // Car extends Vehicle
        Vehicle vehicle = new Car();
        System.out.println(vehicle.value);
        vehicle.start();
        vehicle.stop();
    }
}

출력 : 100

자동차 시작

차량 정지

이것은 stop()정적 메서드이고 재정의 할 수 없기 때문에 발생 합니다. 따라서 바인딩은 stop()컴파일 타임 에 발생하며 start()비 정적은 자식 클래스에서 재정의됩니다. 따라서 객체의 유형에 대한 정보는 런타임에서만 (늦게 바인딩) 사용할 수 있으므로 start()Car 클래스 의 메서드가 호출됩니다.

또한이 코드에서는 변수 초기화가 후기 바인딩에 포함되지 않기 때문에 출력으로 vehicle.value제공합니다 100. 메소드 재정의는 자바가 런타임 다형성을 지원하는 방법 중 하나입니다 .

  • 오버라이드 된 메소드가 수퍼 클래스 참조를 통해 호출되면 Java는 호출이 발생할 때 참조되는 객체의 유형에 따라 해당 메소드의 버전 (수퍼 클래스 / 서브 클래스)을 실행합니다. 따라서이 결정은 런타임에 이루어집니다.
  • 런타임시 실행될 재정의 된 메서드 버전을 결정하는 것은 참조되는 개체의 유형 (참조 변수 유형이 아님)에 따라 다릅니다.

이 답변 Parent parent = new Child();이 중요한 부분과 위의 참조를 사용하여 자식 클래스 변수에 액세스 할 수없는 이유에 대한 답변을 바랍니다 .


-1

예를 들어 우리는

class Employee

{

int getsalary()

{return 0;}

String getDesignation()

{

returndefault”;

}

}

class Manager extends Employee

{

int getsalary()

{

return 20000;

}

String getDesignation()

{

return “Manager”

}

}

class SoftwareEngineer extends Employee

{

int getsalary()

{

return 20000;

}

String getDesignation()

{

return “Manager”

}

}

이제 모든 직원 (예 : 소프트웨어 엔진, 관리자 등)의 급여 및 지정을 설정하거나 받으려면

Employee의 배열을 가져 와서 두 메소드를 모두 호출합니다. getsalary (), getDesignation

Employee arr[]=new Employee[10];

arr[1]=new SoftwareEngieneer();

arr[2]=new Manager();

arr[n]=…….

for(int i;i>arr.length;i++)

{

System.out.println(arr[i].getDesignation+””+arr[i].getSalary())

}

이제 다른 유형의 직원을 가질 수 있기 때문에 일종의 느슨한 결합입니다. 예 : 소프트웨어 엔지니어, 관리자, HR, 식료품 저장실 직원

따라서 다른 직원 객체에 관계없이 객체를 부모 참조에 제공 할 수 있습니다.


-3

parent를 Parent로 선언하므로 java는 Parent 클래스의 메서드와 속성 만 제공합니다.

Child child = new Child();

작동해야합니다. 또는

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

1
물론 이것은 작동합니다. OP Parent parent = new Child();는 유용하고 의미있는 컨텍스트를 요청했습니다 .
Baz

2
다른 답변과 동일한 설명이므로 왜 이것을 반대합니까? 나는 일하지 않는다고 말하지 않는다. Child c = new Parant ()라고 말하면 Child가 아닌 Parent의 인스턴스가 있기 때문에 ando 속성 급여가 없습니다. 속성 급여를 원하면 Child 인스턴스를 생성하거나 Parent를 Child로 캐스팅해야합니다 ....
Thargor
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.