java - 상속(inheritance)

yunzivv·2025년 3월 12일

JAVA 기초

목록 보기
12/23

상속

클래스 게시물과 이어지는 내용 이전글. java-클래스


상속이란 기존 클래스의 기능과 속성을 새로운 클래스가 물려받는 것이다. 클래스를 만들고 이와 같은 성질을 가진 클래스가 많이 필요할 때, 상속의 개념을 적용한다.

기존의 클래스를 부모 클래스, 슈퍼 클래스, 상위 클래스라고 하고 새로운 클래스를 자식 클래스, 서브 클래스 또는 하위 클래스라고 부른다.


1. 효과


  1. 기존에 작성된 부모 클래스 바디를 재활용 할 수 있다. (코드의 재활용)
    • 하나의 부모 클래스는 여러개의 자식 클래스가 상속 받을 수 있다.
    • 자식 클래스들의 중복되는 기능을 부모 클래스에서 구현하면 자식 클래스에서는 생략할 수 있다.
  2. 자식 클래스는 부모 클래스의 멤버 뿐만 아니라 자신의 멤버를 추가하여 확장할 수 있다.
  3. 코드를 공통적으로 관리하기 때문에 추가, 수정 등 유지보수에 용이하다.

2. extends


키워드 extends를 사용해 자식 클래스는 부모 클래스의 멤버를 상속받는다.

  • 작성 예시
class 부모클래스 {
	int 속성1;
  	String 속성2;
  	void 기능1 () {
  		System.out.println("기능1 실행");
    }
}
  
class 자식클래스 extends 부모클래스 {
	// 멤버가 없음
}

위 코드를 보면 자식 클래스 바디에는 멤버가 없다. 하지만 extends키워드로 부모 클래스의 멤버를 상속 받았기 때문에 자식 클래스는 부모 클래스의 멤버를 사용할 수 있다. 아래와 코드는 위 코드와 이어진다.

자식클래스 자식1 = new 자식클래스();
자식1.속성1 = 10;
System.out.println(자식1.속성1); // 출력 : 10 
자식1.기능1(); // 출력 : 기능1 실행

부모 클래스의 멤버를 물리적으로 복사하여 사용할 수 있는 게 아니라 부모와 연결되어 부모의 멤버를 사용할 수 있는 것이다.

2-1. 복잡한 상속

상속은 한 번만 가능한 게 아니다. 부모 > 자식(과 동시에 부모) > 자식··· 의 형태로 구성할 수도 있으며, 이러한 상태를 복잡한 상속이라고 한다.

class 동물 {
  	void 수면 () {
  		System.out.println("zzz");
    }
}
  
class 고양이 extends 동물 { // 동물 클래스를 상속 받음
}

class 샴 extends 고양이 { // 동물 클래스를 상속 받은 고양이 클래스를 상속받음
}

2-2. AB 테스트

상속 관계를 구상할 때는 AB테스트를 통해 올바른 상속 관계를 구상할 수 있다. class 고양이 extends 동물 {} 에 AB테스트를 적용해보자.

A(고양이) is(extends) B(동물) -> 통과

하지만 이 테스트의 결과가 참인지 거짓인지는 사람만 알 수 있다. 프로그램은 모른다.


3. 추상 메서드(abstract method)


abstract 메서드라고도 한다. 부모 클래스에서 메서드를 정의만 하고(추상적) 이를 각각의 자식 메서드에서 구현(구체적)한다.
상위 클래스에서 하위 클래스의 메서드를 잃지 않기 위해 추상 메서드를 가지기도 한다.

abstract class abClass { 
    abstract void method1(); 
}

3-1. 특징

  1. 추상 메서드를 하나 이상 가진 클래스는 abstract 키워드를 클래스명 앞에 붙인다.
  2. 추상 클래스를 상속받는 자식 클래스는 추상메서드를 재정의(오버라이딩)할 의무를 가진다.
  • 재정의 하지 않으면 오류가 발생한다.
  • 추상 메서드와 오버라이딩 메서드는 같은 리턴타입과 메서드 시그니처를 가진다.
  1. 메서드의 선언부만 작성하고 구현부는 없는 상태다. (중괄호를 작성하지 않아도 됨)
  2. 인스턴스화 할 수 없다.

추상메서드의 용도


4. 인터페이스(interface)


추상클래스는 abstract메서드(추상메서드)를 한개 이상 가진 클래스이다. 추상 메서드와 구현 메서드를 동시에 가질 수 있다.

클래스 내부의 모든 메서드가 abstract메서드로 구성이 된 클래스는 인터페이스다.

4-1. 특징

  1. 내부의 메서드가 모두 추상 메서드로 구성되어있다.
  2. 인터페이스 내부 메서드에 abstract를 붙이지 않아도 된다. (이미 interface 키워드를 사용했기 때문에)
  3. 다중 상속이 가능하다.
  4. 생성자 생성이 불가하다.
  5. 인스턴스 생성이 불가하다.
  6. 인터페이스를 상속받는 클래스는 구현부를 상속 받지 못했기 때문에 메서드 오버라이딩이 필수이다.

4-2. implements

인터페이스를 상속 받기 위해서는 extends 대신 implements 키워드를 사용한다.
대신 인터페이스를 impliments 하면 하위 클래스에서 메서드 오버라이딩할 때는 public 키워드를 붙여줘야한다.

4-3. 다중 상속

인터페이스는 다중 상속과 다중 구현이 가능하다.

만약 추상 클래스 다중 상속이 가능하다고 가정해보자.

abstract class a { // 추상 클래스1
    void m(); 
}
abstract class b { // 추상 클래스2
    void m() {
    }
}

class c extends a, b { // 하위 클래스
}

위와 같은 클래스 3개를 만들고 c는 a와 b를 상속받는다.

c c1 = new c();
c.m();

c의 인스턴스를 만들고 메서드 m을 호출한다. 그럼 어떤 메서드를 실행해야 할까?
이러한 문제 때문에 추상 클래스는 다중 상속을 지원하지 않는다.

하지만 인터페이스는 다중 상속이가능하다.
왜냐하면 메서드 오버라이딩이 필수이기 때문에 인터페이스를 상속받은 클래스는 메서드를 재정의하여 모호한 상황이 발생하지 않는다.

추상 클래스 한 개와 인터페이스 여러개를 상속 받는 것도 가능하다.

class d extends abClass implements inClass, in2Class {
}

4-4. 추상클래스와 인터페이스 비교

  1. 공통점
    • 객체를 생성할 수 없다. (인스턴스화 불가능)
    • 추상메서드를 포함한다.
    • 하위 클래스는 추상 메서드를 반드시 오버라이딩(재정의) 할 의무가 있다.
  2. 차이점
차이점추상 클래스인터페이스
목적상속 받아서 기능 확장하위클래스의 동일한 기능 실행 보장
메서드동시에 구상 메서드 포함 가능오직 추상 메서드만 가능
상속 키워드extends(확장)implements(구현)
다중상속 여부불가능가능



의문


1. abstract 메서드의 리턴 타입

  1. abstract 메서드는 구현이 되어 있지 않다. -> abstract 메서드에서 실제 return을 할 수 없다.
  2. abstract 메서드를 상속받는 하위 오버라이딩 메서드는 abstract 메서드와 메서드 시그니처가 같아야한다. 메서드 시그니처에 리턴타입은 포함되지 않는다.
  3. 메서드 시그니처에 포함되지 않고 실제 리턴을 하지 않는 abstract 메서드의 리턴타입은 언제나 void이거나, 사실 없어도 무방하지 않을까?

라는 생각이 들었다. 아래의 코드처럼 말이다.

고양이 a = new 검은고양이();
a.울다();

abstract class 고양이 {
    abstract void 야옹(); // abstract 메서드 리턴 타입 : void
}
class 검은고양이 impliments 고양이 {
	String 야옹() { //
    	return "야옹"; // 오버라이딩 메서드 리턴 타입 : String
    }
}

1-2. 해답
결론부터 말하자면 두 리턴타입은 다를 수도 있다. Java 5.0 이전에는 메서드를 재정의할 때 매개변수와 반환 유형이 모두 정확히 일치해야 했지만 Java 5.0 이후는 다른 리턴타입을 가질 수 있다.

하지만 기본적으로 abstract 메서드의 리턴타입은 오버라이딩 메서드와 같은 리턴타입을 갖고 있어야한다.
일단 리턴타입이 void이거나 기본형인 경우 동일해야한다. abstract 메서드의 리턴타입은 void이고 오버라이딩 메서드의 리턴타입은 String일 수 없다는 것이다.

Java가 허용하는 안에서 두 리턴타입은 다를 수 있다. 두 리턴타입이 다를 수 있는 경우를 정리한다.


리턴타입이 다른 경우

리턴타입이 하위 타입인 경우 리턴타입이 다를 수 있다.
예를 들어 (상위) A > B > C (하위) 클래스 3개가 있다고 가정할 때, abstract 메서드의 리턴타입이 A라면 오버라이딩 메서드는 A, B, C중 한가지 즉, 하위유형을 반환할 수 있다. 이를 공변 반환(Covariant Return)이라고도 한다.

가벼운 궁금증으로 시작했는데 이를 해결하기 위해 해외 사이트까지 변역해서 찾아봤다. 리턴 타입이 어떤 경우에 필요한 지 모르는 단계라 직접 코드를 작성하면서 깨닫지 못하는 게 좀 아쉽다. 찾으면서 도움이 되었던 사이트를 첨부한다. (첫번째 하이퍼링크는 강사님도 추천한 사이트라서 자주 사용하는 것이 좋을 듯)

참고 자료
메서드 오버라이딩 반환 타입
java 튜토리얼



2. interface의 존재 이유
interface 클래스는 상속 받아도 어짜피 내부의 메서드를 무조건 재정의 해야한다. 그럼 중복 코드가 오히려 는다고 볼 수 있지 않은가? interface 클래스는 왜 필요한 걸까.

아래 코드와 함께 보자.

고양이 a = new 검은고양이();

interface 고양이 {
	void 야옹();
    void 식빵();
    void 하악();
}

class 검은고양이 implements 고양이 {
	void 야옹(){
    	System.out.println("야옹");
    }
    void 식빵(){
    	System.out.println("식빵 굽기");
    }
    void 하악(){
    	System.out.println("캬아아악!!");
    }
}

검은고양이 클래스는 interface 클래스(고양이 클래스)를 implements(구현)하기 때문에 고양이 클래스의 메서드를 모두 오버라이딩 해야한다.

객체지향언어는 중복 코드를 줄이고 코드를 재사용하는 목적이 강하다. interface 클래스는 객체지향언어의 목적에 맞지 않는 것 같다.

2-2. 해답
interface를 사용하면 중복 코드가 많아질 수도 있지만 다형성을 활용할 수 있다. 또 interface는 다중 상속이 가능하고 interface로 하위 클래스에 메서드 오버라이딩을 강제할 수 있다.

  • interface의 장점
    • 메서드의 틀을 미리 만들어 두기 때문에 개발자 간의 의사소통이 용이해진다.
    • 다형성을 활용해서 코드 재사용성과 유지보수성이 좋아진다.
    • 설계도를 제공해서 오버라이딩을 강제하기 때문에 표준화가 가능해진다.
    • 다중 구현이 가능해서 유연성이 높아진다.

이렇게 상속의 개념과 종류를 정리하고 공부하면서 생겼던 궁금증까지 정리를 마쳤다... 처음 보는 키워드가 많이 나와 헷갈리기도 하지만 완벽하게 숙지했다는 생각이 들어서 뿌듯하다.

0개의 댓글