본문 바로가기
자바

자바 실전개념편 3(상속, 다형성)

by 더하리 2024. 6. 2.

안녕하세요:) 오늘은 김영한 강사님의

실전 자바 기본편 강의 요약 정리 3편입니다!

 


목차
09. 상속
10. 다형성

 

 

09. 상속

상속관계

상속은 객체 지향 프로그래밍의 핵심 요소 중 하나!

기존 클래스의 필드와 메서드를 새로운 클래스에서 재사용하게 해준다. 기존 클래스의 속성과 기능을 그대로 물려받는 것!

+ extends 키워드 사용 +대상은 하나만 선택 가능

 

용어

부모 클래스(슈퍼 클래스): 상속을 통해 자신의 필드와 메서드를 다른 클래스에 제공하는 클래스

 자식 클래스 (서브 클래스): 부모 클래스로부터 필드와 메서드를 상속받는 클래스

 

코드로 살펴보자

public class Car {

    public void move() {
        System.out.println("차를 이동합니다.");
    }
}
public class ElectricCar extends Car {

    public void charge() {
        System.out.println("충전합니다.");
    }
}
public class GasCar extends Car {

    public void fillUp() {
        System.out.println("기름을 주유합니다.");
    }

}
public class CarMain {

    public static void main(String[] args) {
        ElectricCar electricCar = new ElectricCar();
        electricCar.move();
        electricCar.charge();

        GasCar gasCar = new GasCar();
        gasCar.move();
        gasCar.fillUp();
    }
}
/* 실행결과
차를 이동합니다. 
충전합니다. 
차를 이동합니다. 
기름을 주유합니다.*/

 

상속 구조도

 

 

위 코드에서 전기차와 가솔린차가 Car 를 상속 받은 덕분에 electricCar.move() , gasCar.move() 를 사용할 수 있다.

자바는 다중 상속을 지원하지 않아 extend 대상은 하나만 선택할 수 있다. → 단일 상속

 

 

 

상속과 메모리 구조

왼쪽: 상속관계의 객체 생성 / 오른쪽: 상속관계의 객체 호출

 

● 상속 관계의 객체를 생성하면 그 내부에는 부모와 자식이 모두 생성된다.

상속 관계의 객체를 호출할 때, 대상 타입을 정해야 한다. 이때 호출자의 타입을 통해 대상 타입을 찾는다.

현재 타입에서 기능을 찾지 못하면 상위 부모 타입으로 기능을 찾아서 실행한다. 

 

+상속은 기능 추가와 클래스 확장에 용이

 

 

 

상속과 메서드 오버라이딩

메서드 오버라이딩(Overriding)은 부모에게서 상속 받은 기능을 자식이 재정의 하는 것이다.

메서드 이름은 같지만 새로운 기능을 사용하고 싶을 때 사용한다!

 

가령, move()에 기본적으로 "차를 이동합니다." 로 되어 있지만 전기차의 경우 "전기차를 빠르게 이동합니다"라고 하고 싶다면 메서드 오버라이딩을 아래와 같이 쓸 수 있다.  

public class ElectricCar extends Car {

    @Override
    public void move() {
        System.out.println("전기차를 빠르게 이동합니다.");
    }

    public void charge() {
        System.out.println("충전합니다.");
    }
}

이때 @ 이 붙은 부분을 애노테이션이라 부르고 프로그램이 읽을 수 있는 특별한 주석이라고 생각하면 된다.

 

 

이전에 메서드 오버로드를 배운 적이 있다. 차이를 알아보자

 

오버로딩(Overloading)과 오버라이딩(Overriding)

메서드 오버로딩: 메서드 이름이 같고 매개변수(파라미터)가 다른 메서드를 여러개 정의하는 것 (같은 이름의 메서드를 여러개 정의했다는 의미)

메서드 오버라이딩: 메서드 오버라이딩은 하위 클래스에서 상위 클래스의 메서드를 재정의하는 과정(메서드 재정의)

 

오버라이딩 조건

  메서드 이름 =

  매개변수(파라미터) 타입, 순서, 개수 =

  접근제어자 제한: 하위 클래스가 상위클래스의 접근제어자보다 제한적이면 안됨

  static, final, private 키워드가 붙은 메서드는 오버라이딩 X 

   (static은 클래스 레벨에서 작동하므로 인스턴스 레벨에서 사용하는 오버라이딩 의미x, final은 재정의 금지, private은 해당 클래스에서만 사용 가능)

  생성자는 오버라이딩 불가능

   예외: 오버라이딩 메서드는 상위 클래스의 메서드보다 더 많은 체크 예외를 throws 로 선언할 수 없다. 

     하지만 더 적거나 같은 수의 예외, 또는 하위 타입의 예외는 선언할 수 있다.

 

 

super - 부모참조

오버라이딩을 하였거나 (부모, 자식 클래스 속)필드의 이름이 동일할때  부모 클래스 기능을 사용하고 싶다면?

 : super 를 사용! e.g. super.value

 

super - 생성자

상속 관계의 인스턴스를 생성하면 메모리 내부에는 자식과 부모 클래스가 각각 다 만들어진다

따라서! 자식 클래스의 생성자에서 부모 클래스의 생성자를 반드시 호출해야 한다.(규칙) 
상속 관계에서 부모의 생성자를 호출할 때는 super(...) 를 사용하면 된다.

ClassA(최상위클래스)

public class ClassA {

    public ClassA() {
        System.out.println("ClassA 생성자");
    }
}

 

ClassB

public class ClassB extends ClassA {

    public ClassB(int a) {
        this(a, 0); //기본 생성자 생략 가능
        System.out.println("ClassB 생성자 a=" + a);
    }

    public ClassB(int a, int b) {
        super(); //기본 생성자 생략 가능
        System.out.println("ClassB 생성자 a=" + a + " b=" + b);
    }
}

 

ClassC

public class ClassC extends ClassB {

    public ClassC() {
        super(10, 20);
        System.out.println("ClassC 생성자");
    }
}

 

Super2Main

public class Super2Main {

    public static void main(String[] args) {
        //ClassC classC = new ClassC();
        ClassB classB = new ClassB(100);
    }
}
/*실행결과
ClassA 생성자
ClassB 생성자 a=10 b=20
ClassC 생성자
*/

 

정리

  상속 관계의 생성자 호출은 부모에서 자식 순서로 실행된다. (부모의 데이터를 먼저 초기화→ 자식 데이터 초기화)

   자식 클래스의 생성자 첫줄에 반드시 super(...) 를 호출해야 한다. (기본 생성자 생략가능)

 

+코드의 첫줄에 this(...) 를 사용하더라도 반드시 한번은 super(...) 를 호출해야 한다.


10. 다형성

객체 지향 프로그래밍의 대표적인 특징은 캡슐화, 상속, 다형성이다.

프로그래밍에서 다형성은 한 객체가 여러 타입의 객체로 취급될 수 있는 능력을 의미한다!

 

다형성을 이해하기 위해서는 2가지 핵심 이론을 알아야 한다.

● 다형적 참조

메서드 오버라이딩

 

다형적 참조

지금까지는 항상 같은 타입에 참조를 대입하여 한 가지 형태만 참조할 수 있었다. But,

Parent parent = new Parent()
Child child = new Child()

 

부모는 자식을 담을 수 있다

Parent 타입의 변수는 자신을 물론, 자식 타입까지 참조할 수 있다.(손자가 있다면 손자도)

Parent poly = new Parent()
Parent poly = new Child() //부모 타입의 변수가 자식 인스턴스를 참조
Parent poly = new Grandson()

 

코드로 확인해보면 

Parent

public class Parent {

    public void parentMethod() {
        System.out.println("Parent.parentMethod");
    }
}

 

Child

public class Child extends Parent {

    public void childMethod() {
        System.out.println("Child.childMethod");
    }
}

 

Polymain

public class PolyMain {

    public static void main(String[] args) {
        //부모 변수가 부모 인스턴스 참조
        System.out.println("Parent -> Parent");
        Parent parent = new Parent();
        parent.parentMethod();

        //자식 변수가 자식 인스턴스 참조
        System.out.println("Child -> Child");
        Child child = new Child();
        child.parentMethod();
        child.childMethod();

        //부모 변수가 자식 인스턴스 참조(다형적 참조)
        System.out.println("Parent -> Child");
        Parent poly = new Child();
        poly.parentMethod();

        //Child child1 = new Parent(); //자식은 부모를 담을 수 없다.

        //자식의 기능은 호출할 수 없다. 컴파일 오류 발생
        //poly.childMethod();

    }
}

/*실행결과
Parent -> Parent 
Parent.parentMethod
Child -> Child 
Parent.parentMethod 
Child.childMethod
Parent -> Child 
Parent.parentMethod
*/

 

다형적 참조의 한계는 위의 그림처럼 부모는 자식의 메서드를 호출할 수 없다는 점이다.

호출자인 poly 는 Parent 타입이다. 따라서 Parent 클래스부터 시작해서 필요한 기능을 찾는데 상속 관계는 자식 방향으로 찾아 내려갈 수는 없기 때문에 오류가 발생한다.

 

Parent poly = new Child()

이렇게 참조한 상태에서 Child 속 childMethod()를 호출하려면 어떻게 해야할까?

>> 캐스팅 필요!

 

다형성과 캐스팅

다운캐스팅: 자식 타입으로 변경

Parent poly = new Child()
Child child = (Child) poly //Parent poly

괄호에 타입을 지정하면 참조 대상을 특정 타입으로 변경할 수 있다.

캐스팅: 특정타입으로 변경하는 것 ("cast"는 금속이나 다른 물질을 녹여서 특정한 형태나 모양으로 만드는 
과정을 의미)

 

위는 (Child) 를 사용해서 일시적으로 자식 타입인 Child 타입으로 변경하여 child.childMethod() 를 호출할 수 있게 되었다!

 

 

 

 


 

오늘도 자바의 중요한 개념들에 대해 

정리해보는 시간을 가져보았습니다:)

앞으로도 열심히 기록하겠습니다

 

모두 화이팅🙌