상속

용씨·2023년 2월 3일
0

객체지향

목록 보기
1/1

자바에서 상속은 중요한 개념이다. 우리가 흔히 쓰는 ArrayListHashSet, HashMap 등은 상속받는 클래스인 '자식 클래스'이기에 상속의 개념을 잘 알아둬야 클래스를 제대로 사용할 수 있다.

https://data-flair.training/blogs/collection-framework-in-java/

개념

객체 지향 프로그래밍(OOP: Object Oriented Programming)에서 크게 3요소로 꼽는 캡슐화, 상속, 다형성 세 가지 중 상속을 일컫는다. 상속(inheritance)은 객체들 간의 관계를 구축하는 방법이다.

게임 캐릭터를 예로 들어보겠다.

class 궁수 {
	체력
    점프
    앞으로 가기
    뒤로 가기
    앉기
    아이템 먹기
    활 쏘기
}

class 마법사 {
	체력
    점프
    앞으로 가기
    뒤로 가기
    앉기
    아이템 먹기
    마법 쓰기
}

궁수 Class와 마법사 Class에는 체력, 점프 등 공통점이 있다. 게임 캐릭터에게 모두 필수적인 요소들이다. 그렇다면 공통점들을 뽑아서 캐릭터 Class를 만들어보자.

class 캐릭터 {
	체력
    점프
    앞으로 가기
    뒤로 가기
    앉기
    아이템 먹기
}

그리고 궁수 Class와 마법사 Class에게는 캐릭터 Class를 상속하게 한다.

class 궁수 extends 캐릭터{
    활 쏘기
}

class 마법사 extends 캐릭터{
    마법 쓰기
}

이렇게 캐릭터 Class를 재사용해서 중복되는 코드를 줄여 개발 시간을 단축시킨다.
또한 클래스의 수정을 최소화할 수 있다. 캐릭터 Class를 수정하면 궁수 Class와 마법사 Class에 수정 효과를 가져온다.

사용 예시

메소드 오버라이딩, 생성자, 타입 변환을 알아보자.

메소드 오버라이딩(Overriding)

자식 클래스는 extends 키워드를 사용해 부모 클래스를 상속받는다. 이 키워드를 선언함으로써 부모 클래스의 필드와 메서드를 사용할 수 있다. 여기서 부모 클래스의 필드와 메서드에 대해 잠깐 고민해보자. 자식 클래스는 이것들을 그대로 사용하던가? 메이플 게임을 해봤다면 알겠지만 캐릭터들의 점프는 동일하지 않다. 이중점프 하는 캐릭터도 있고 아예 날아가는 캐릭터도 있다. 이처럼 하위 클래스는 부모 클래스의 메소드의 기본 동작방법을 변경할 수 있다. 이것을 메소드 오버라이딩(overriding)이라고 한다.

메소드 오버라이딩(overriding)을 하면 부모 메소드는 숨겨지고 자식 메소드가 우선적으로 사용된다.

메소드 오버라이딩을 하려면 규칙을 지켜야 한다.
첫번째, 부모 메소드의 선언부인 리턴 타입, 메소드 이름, 매개변수가 동일해야 한다.

class 부모 {
	void talk(String message) {
    	System.out.println("부모: " + message);  
    }

class 자식 extends 부모 {
	@Override
	void talk(String message) {
    	System.out.println("자식: " + message);  
    }
}

리턴타입인 void와 메소드 이름인 talk, 매개변수 String message가 동일하다.
@Override는 컴파일 단계에서 정확히 오버라이딩이 되었는지 체크해주는 어노테이션이다.

두번째, 접근 제한을 더 강하게 오버라이딩 할 수 없다.
부모 메소드의 접근 제어자가 public 일 때, 자식 메소드를 private으로 선언할 수 없다는 것이다.

세번째, 새로운 예외를 throws 할 수 없다.

생성자

자바에서 자식 객체를 생성하면 부모 객체가 먼저 생성된 다음에 자식 객체가 생성된다. 모든 객체는 생성자를 호출해야만 생성된다. 부모 생성자를 따로 넣지 않으면 컴파일러가 부모의 기본 생성자를 자동으로 추가해준다. 부모 생성자는 자식 생성자의 첫 줄에 넣어야 한다.

class 자식 {
	자식 () {
    	super(); // 부모 생성자 호출
    }
}

부모 생성자 호출 시 주의할 점이 있다. 부모 클래스에 기본 생성자가 없고 매개변수를 갖는 생성자만 있으면 개발자가 super(매개값)을 직접 넣어줘야 한다.

class 부모 {
	부모 (int age) {
    	System.out.println("부모: " + age);  
    }

class 자식 extends 부모 {
	자식 () {
    	// 아무것도 안쓰는 거 불가능 
        super(50); // 필수로 넣어줘야 함
    }
}

타입 변환

부모 ⇆ 자식 타입 변환에는 자동 타입 변환과 강제 타입 변환이 있다. 각각의 조건을 만족해야 타입 변환이 가능하다.

자동 타입 변환

자동 타입 변환은 A a = new B(); 또는 B b = new B(); A a = b; 식으로 가능하다.

  • A: 부모 타입
  • B: 자식 타입

자식은 부모의 필드와 메소드를 상속받았기 때문에 해당 필드와 메소드를 사용할 수 있다. 그래서 자동으로 타입이 변환될 수 있는 것이다.

A는 B의 바로 위의 부모가 아니어도 된다. 상속 계층에서 B의 상위 타입이기만 하면 된다.

비록 변수 a는 자식 객체(B)를 참조하지만 변수 a가 접근가능한 멤버는 A 타입의 멤버로 한정된다. 그러나 자식 클래스에서 오바리이딩한 메소드가 있다면 부모 메소드 대신 자식 메소드가 호출된다. 이것은 다형성과 관련이 있다.

강제 타입 변환

자동 타입 변환에서는 자식 객체가 부모 타입 변수로 변환이 되었다. 하지만 자식 객체가 쓸 수 있는 멤버는 부모의 멤버뿐이었다. 자식 객체가 자식에 선언된 필드나 메소드를 사용하고 싶을 때, 강제 타입 변환으로 다시 자식 타입으로 변환될 수 있다.

A a = new B(); // 자동 타입 변환
B b = (B) a; // 강제 타입 변환

강제 타입 변환은 B b = (B) a;(B) 처럼 강제로 Casting 해야 한다.

상속 불가능 및 제약

  • final class는 상속할 수 없는 클래스이다.
  • final method는 오버라이딩할 수 없는 메소드이다.

protected 접근 제한자

protected는 상속과 관련이 있고, public과 default의 중간쯤에 해당하는 접근 제한을 한다.
필드, 생성자, 메소드가 protected로 선언되면 같은 패키지이거나, 자식 객체만 사용이 가능하다. 다른 패키지에 있으면 new 연산자를 사용해서 생성자를 직접 호출할 수 없으므로 super()를 사용해야 한다.

profile
아침형 인간이 목표

0개의 댓글