4/19 상속, 오버라이딩, 다형성

박세현·2024년 4월 19일

JAVA

목록 보기
9/22
post-thumbnail

상속

1. 상속이란?

  • 상위 클래스의 모든 필드(변수)와 메소드를 물려받아, 자식 클래스를 정의하는 것을 의미
  • 상속은 우리가 일반적으로 알 듯 무엇인가를 물려받는다는 의미이다
  • 일례로 부모가 자식에게 물려주는 재산을 상속이라고 하고, 상속받은 재산은 자신의 것으로 사용할 수 있다
  • 객체 지향 프로그램에서도 마찬가지로 B클래스는 A클래스를 상속받으면 B클래스는 A클래스의 메서드를 사용할 수 있습니다.

참고)

  • 부모 클래스(parent class) = 상위 클래스(super class) = 기초 클래스(base class)
  • 자식 클래스(child class) = 하위 클래스(sub class) = 파생 클래스(derived class)



2. 클래스 상속 문법

class 자식 클래스 extends 부모클래스 {

}



3. What You Can Do in a SubClass

  • private 접근 제어자로 된 필드를 제외한 상속된 필드들은 직접적으로 사용이 가능합니다.

  • superClass에서 선언된 필드와 같은 이름으로 subClass에서 필드로 정의할 수 있습니다. (Hiding it, not recommended)

  • 새로운 필드와 메소드를를 subClass에서 추가할 수 있습니다. 이건 superClass에는 적용이 되지 않습니다.

  • 상속된 메소드는 직접 사용할 수 있고 superClass에서 정의한 메소드를 subClass에서 재정의할 수 있습니다. 이때 동일한 Signature를 가지게됩니다. (Overriding it)

  • subClass에서 constructor()를 작성할 수 있고 super keyword를 통해 superClass의 constructor()를 불러올 수 있습니다.

  • superClass의 static method와 동일한 이름을 subClass의 instance method 이름으로 할 수 없습니다.

  • superClass의 instance method와 동일한 이름을 subClass의 static method 이름으로 할 수 없습니다.




4. 클래스의 상속

예시)
C -> B -> A
C : B자원과 A자원 모두 공유 가능

ㄴ 기본생성자 정의

ㄴ B가 A를 상속받음
ㄴ 기본생성자 정의

ㄴ C가 B를 상속받음
ㄴ 기본생성자 정의


C( ) : C 생성자 호출
ㄴ 기본생성자 정의 시점 : 기본생성자의 구현내용은 이미 객체가 만들어지고 난 뒤의 시점
ㄴ 음? 나는 C 생성자만 출력했는데 왜 A, B 기본생성자의 구현내용도 같이 출력이 됬지?
super(); : 기본생성자 처럼 내가 따로 정의 안했을 시 컴파일러가 모든 생성자함수의 첫줄에 항상 추가, 상위 클래스의 기본생성자를 가리킴
ㄴ 코드 해석
C() : C 생성자 호출
-> super( ) : C의 기본생성자 내부에서 super호출 = 상위클래스의 기본 생성자 = B()

B() : B생성자 호출
-> super() : B의 기본생성자 내부에서 super호출 = 상위클래스의 기본 생성자 = A()

A() : A 생성자 호출
=> 그래서 A, B, C 기본생성자의 구현내용이 모두 출력됨

ㄴ 생성자 함수 : 스택영역이다
ㄴ C, B, A = 생성자 함수 : 객체를 만드는 역할
ㄴ스택구조 : 먼저호출된게 마지막에 나온다
ㄴ C호출 B호출 A호출 -> A객체 생성 B객체생성 C객체생성
ㄴ 객체가 만들어 지면 이제 사라진다ㅏ

ㄴ 힙영역
ㄴ A, B, C객체가 소유하고 있는 자원


ㄴ B생성자 함수 호출
ㄴ B에서 A객체의 자원 호출


ㄴ B : A, B 객체의 자원 소유

ㄴ C : A, B, C 객체의 자원 소유




5. super

  • 상위 클래스 객체의 주소값
  • 상위 클래스의 생성자 함수를 클래스 내부에서 호출할 때 사용

  • 모든 생성자함수의 첫줄에 항상 추가되어 있음
  • 실제로 정의 안해도 매우 중요해서 컴파일러가 기본적으로 추가함
    -> 매개변수 유무 상관없이 기본super()만 추가 해 줌
  • 기본생성자 처럼 내가 따로 정의 안했을 시 컴파일러가 모든 생성자함수의 첫줄에 항상 추가


1) 변수 : 상위 클래스 객체의 주소값

참고)
this가 함수의 지역변수 : 현재 클래스에 생성되어있는 객체의 주소값이 담겨있음
ㄴ 왜? 객체의 자원을 접근하기 위해서


예시)

class Parent {
    String parentVariable = "부모 클래스 변수";

    void parentMethod() {
        System.out.println("부모 클래스의 메서드");
    }
}
-------------------------------------------------------------------------------
class Child extends Parent {
    String childVariable = "자식 클래스 변수";

    void childMethod() {
        System.out.println("자식 클래스의 메서드");
    }

    void accessParent() {
        // super 키워드를 사용하여 부모 클래스의 변수와 메서드에 접근할 수 있습니다.
        System.out.println(super.parentVariable); // 부모 클래스의 변수 출력
        super.parentMethod(); // 부모 클래스의 메서드 호출
    }
}
-------------------------------------------------------------------------------
public class SuperExample {
    public static void main(String[] args) {
        Child child = new Child();
        child.accessParent(); // 자식 클래스에서 부모 클래스 멤버에 접근하는 메서드 호출
    }
}
-------------------------------------↓출력↓-------------------------------------
부모 클래스 변수
부모 클래스의 메서드


2) 함수(메서드) : 상위 클래스의 생성자 함수를 클래스 내부에서 호출할 때 사용

참고)
this(...) : 현재 클래스의 생성자 함수를 클래스 내부에서 호출할 때 사용


예시)

class Parent {
    Parent() {
        System.out.println("부모 클래스의 생성자");
    }
}
-------------------------------------------------------------------------------
class Child extends Parent {
    Child() {
        // 부모 클래스의 생성자 호출
        super(); // 이 줄에서 super()를 호출하여 부모 클래스의 생성자를 호출합니다.
        System.out.println("자식 클래스의 생성자");
    }
}
-------------------------------------------------------------------------------
public class SuperConstructorExample {
    public static void main(String[] args) {
        Child child = new Child();
    }
}

-------------------------------------↓출력↓-------------------------------------
부모 클래스의 생성자
자식 클래스의 생성자


예시) 컴파일러는 기본 super()만 추가해준다

package exam03;

public class Student {
    protected int id;
    protected String name;

    public Student(int id, String name) {
        this.id = id;
        this.name = name;
    }
}
-------------------------------------------------------------------------------
package exam03;

public class HighSchoolStudent extends Student {

    public HighSchoolStudent() {
        super(1000, "이이름");
    }
}
-------------------------------------------------------------------------------
package exam03;

public class Ex06 {
    public static void main(String[] args) {
        HighSchoolStudent highSchoolStudent = new HighSchoolStudent();
        System.out.println(highSchoolStudent.id);
        System.out.println(highSchoolStudent.name);
    }
}


-------------------------------------↓출력↓-------------------------------------
1000
이이름

ㄴ super() : 상위클래스의 기본생성자참조
ㄴ 컴파일러는 매개변수가 있든 말든 기본=super()만 추가해 줌
ㄴ 상위 클래스에 있는 기본생성자 함수에는 매개변수가 있는데 하위클래스에는 매개변수 없어서 에러 뜸
ㄴ 문제해결 방법
-> ① 상위 클래스에 기본 생성자를 추가해줌
-> ② 하위클래스 super()에 매개변수를 넣어주기



① 상위 클래스에 기본 생성자를 추가해줌

ㄴ 기본생성자는 생성자함수가 없을 때만 추가해줌
ㄴ 상위클래스에 이미 생성자함수가 한개 있기 때문에 컴파일러가 자동으로 추가 안해줘서 내가 따로 써줘야 함
ㄴ 따로 써줌으로써 오류 해결


② 하위클래스 super()에 매개변수를 넣어주기





메서드 재정의(오버라이딩)

  • 상속 관계에서 부모 클래스의 메서드를 자식 클래스에서 다시 정의하는 것
  • 하위클래스에 상위클래스와 동일한 메서드가 있으면 하위클래스의 메서드가 우선


1. 메서드 재정의 조건

  • 상속시에만 가능

    • 상속은 객체간에만 가능
      ㄴ 즉, 정적인 메서드는 상속이 안된다(객체 생성과 상관없는 메서드니까)
      ㄴ 왜냐면 부모 클래스의 객체 자원을 공유하기 위해 상속을 하는건데 정적인 자원은 객체가 없으니까
      ㄴ 애초부터 쓸 수 있으니 굳이 상속을 할 필요도 없다
  • 인스턴스 메서드에서만 가능

  • 하위클래스에 상위클래스와 동일한 메서드가 있으면 하위클래스 메서드가 우선

  • 부모 클래스의 메서드와 자식 클래스의 메서드의 이름, 매개변수 유형, 반환 유형이 동일해야 함
    ㄴ 그렇지 않다면 재정의한 메서드를 기존 메서드와 다른 메서드로 인식함

  • 접근 제어자는 부모 클래스의 메서드보다 더 넓은 범위로 변경가능
    ㄴ 단, private은 접근 자체가 안되므로 불가
    ex) 부모 클래스의 메서드가 protected로 선언되었다면 자식 클래스에서는 protected 또는 public으로 재정의할 수 있다

  • 자식 클래스에서 재정의된 메서드는 부모 클래스의 메서드보다 더 구체적인 예외를 선언할 수 있다
    ex) 부모 클래스의 메서드가 throws IOException을 선언했다면 자식 클래스에서는 throws FileNotFoundException을 선언할 수 있습니다.




2. 묵시적 형변환과 메서드 재정의

  • 묵시적 형변환 : 작은 자료형 -> 큰 자료형으로 강제 형변환
  • 메서드 재정의 : 하위클래스에 상위클래스와 동일한 메서드가 있으면 하위클래스의 메서드가 우선


1) 애노테이션(Annotation) : 주석, 주해

  • 형식: @이름
  • 정보 전달

  • @Override : 재정의된 메서드임을 컴파일러에게 알려주는 정보



super.move(); : Animal이라는 객체의 move()라는 메서드를 호출



ㄴ 실수로 함수명 잘못정의한거 알려줌

@Override 단축키
마우스 우클릭



2) 메서드재정의시 접근 제어자 변경 범위

  • 좁은 범위 -> 넓은 범위 변경 가능 : private은 접근 자체가 안되므로 불가
  • 넓은 범위 변경 -> 좁은 범위 는 불가



3. 가상메서드

  • 부모 클래스에서 선언되었으나 자식 클래스에서 재정의된 메서드를 가리키는 것을 의미

  • 자바에서 모든 메서드는 기본적으로 가상 메서드
  • 만약 부모 클래스에 정의된 메서드를 자식 클래스에서 재정의(override)한다면, 그 메서드는 가상 메서드가 됨
    -> 이것은 프로그램이 실행될 때 메서드가 호출될 때마다 실제 객체의 타입을 따라가는 것을 의미
    -> 즉, 컴파일 시간이 아니라 실행 시간에 어떤 메서드가 호출될지 결정
    -> 이것이 자바의 다형성을 구현하는 방식 중 하나입니다.
    -> 간단히 말해서, 가상 메서드는 상속 관계에서 부모 클래스의 메서드를 자식 클래스에서 재정의할 때 사용되며,
    실제 객체의 타입에 따라 실행 시에 동적으로 결정


① 가상메서드테이블

  • 가상메서드(키)를 진짜메서드(주소값)로 갈 수 있게끔 매핑해주는 역할
    -> 가상메서드테이블이 있기 때문에 상속 및 메서드 재정의가 가능

  • 정적메서드는 가상메서드테이블 과정 없이 클래스명으로 바로 접근 가능
  • 인스턴스메서드는 가상메서드테이블 과정을 거쳐 접근 가능
    ㄴ 인스턴스 메서드는 주소가 전부 매핑이된다
  • 가상메서드테이블은 힙영역이다

예시)

ㄴ 주소가 각각 매핑되어 있지만 접근한 메소드는 같음
ㄴ 현재 Human에 정의 된 move()가 없기 때문에 상속받은 Animal.move() 접근함

ㄴ Human에 move()가 정의되었기 때문에 Animal.move()가 아닌 Human.move()주소값을 통해 접근함

ㄴ 모두 같은 메서드를 참조한다

ㄴ Animal, Tiger, Human 에 있는 move()는 서로 다른 move()이다

정적메서드는 가상메서드테이블 과정 없이 클래스명으로 바로 접근 가능
인스턴스메서드는 가상메서드테이블 과정을 거쳐 접근 가능
메서드 여러개 필요 없으니 하나의 자원 move()만 접근




✨다형성

  • 하위 클래스의 객체 -> 상위 클래스의 자료형으로 자동 형변환
  • 다만 접근할 수 있는 자원의 양은 하위클래스의 양 만큼 한정
  • C -> A, C-> B

ㄴ 주스는 동일하지만 양이 줄어듬

A a = c;
ㄴ A라는 클래스에 c를 담는다
-> C클래스의 자료형이 A클래스의 자료형으로 바뀜(자료형 바뀜)❓


ㄴ 같은 말이다
ㄴ A클래스에 C객체를 담았다
ㄴ C객체를 만들고 내용물을 A에 담았다



예시)

ㄴ 다형성 그림

ㄴ Animal 클래스 정의

ㄴ Bild 클래스 정의

ㄴ Human 클래스 정의

ㄴ Tiger 클래스 정의

ㄴ 객체 생성 -> 호출

Human human = new Human(); -> Animal human = new Human();로 바꿔도 ㅇㅋ(다형성)

ㄴ 배열
ㄴ ❓출력값이 이해가 안되넹
ㄴ 자료형은 Animal(상위클래스)이지만 출력값은 본체객체(하위클래스)꺼가 나옴
ㄴ 본체 객체가 기준


ㄴ 선언과 동시에 초기화
ㄴ 향상된 for문



상위클래스 : 일반적인 개념
하위클래스 : 구체적인 개념

ㄴ 상위클래스가 더 작고 하위클래스가 더 크다
ㄴ 왜?
ㄴ 일반적인 개념은 공통된 내용이니까 작아야함

예시)

  • 일반적인 개념
    애니멀 클래스 : 동물 : 움직인다
  • 구체적인 개념
    휴먼 클래스
    새 클래스
    호랑이 클래스


1. 다운 캐스팅과 instanceof

1) 다운 캐스팅

  • 상위 클래스에서 하위클래스로 묵시적 형변환

예시)



2) instanceof 연산자

  • 객체의 생성된 출처를 체크해보는 연산자

  • 출처를 체크해보고 같은 출처이면 형변환 해줌

  • 같은 출처여도 상위 -> 하위로 형변환시 출처가 불명확하다 인식하여 instanceof로 출처 체크해야 형변환이 됨

  • 변수 instanceof 클래스명


예시)
c instanceof C : c라고 하는 참조변수가 가리키는 객체의 출처가 C클래스로 부터 만든 객체인가?
(객체의 포함관계)

c instanceof C
ㄴ c라는 참조변수 : 생성된 객체의 주소값
ㄴ 즉 c가 가리키는 객체가 C라는 클래스로부터 만들어진 객체인지 체크
c instanceof B c instanceof A
ㄴ C라는 객체 안에는 A, B객체가 포함되어 있음
ㄴ 포함된 자원이면 다 참이다

예시) 하위 -> 상위 객체로 형변환 시 출처 상관없이 instanceof로 출처 체크하고 형변환 해줘야 함

참고) instance
클래스라고 하는 객체 정의(존재x) -> new -> 힙공간에 존재 : 생성된 객체(인스턴스 객체)

  • 생성과정이 있으면 instance 객체라고 한다
    • 클래스는 객체 생성의 설계도에 불과함, 생성과정(new...)을 통해야지 힙 영역에 찐 객체가 됨(인스턴스 객체)
  • instance == 객체
profile
귤귤

0개의 댓글