본문 바로가기
programming/JAVA

[JAVA] 상속(Inheritance) 개념 정리

by buddev 2020. 4. 26.

자바의 상속(Inheritance) 에 관한 개념 정리입니다.

onenote 필기자료를 github 업로드를 위해 .md 파일로 다시 정리하고 있습니다.

혹시 잘못된 부분이 있다면 댓글 달아주시면 바로 수정하겠습니다! :)

+tistory의 .md파일 css가 약간 깨져서 최대한 깨지지 않게 작성했으나, 아직은 읽기 조금 불편하실 수 있습니다.

최대한 빨리 개선하겠습니다.


상속(Inheritance)

개념

class 설계 시 특정 class를 상속받아 그 class의 Data(변수)와 Method(기능)를 사용하는 것

타입만 가져가려면 interface 구현

타입 + 구현(속성, 메소드) 까지 하려면 class 상속 사용

 

  • Generalization : 추출된 class들의 공통적인 특성을 모아 super class로 정의할 수 있다.
  • Specialization : 비슷한 속성과 기능을 가지고 있는 다른 class를 상속 받아 새로운 class를 정의 할 수 있다.

특징

1. 선택적 상속 불가능 / All or Not 만 가능

  • 상속되는 클래스의 속성이나 기능을 선택적으로 상속받을 수 없다. 전부 다 받거나, 아예 안받거나 둘 중 하나만 가능하다.
  • 상속을 받게 되면 super클래스의 모든 속성과 기능을 상속받아 사용 할 수 있다.

1) 생성자는 상속되지 않는다.

 

2) super 클래스에서 private으로 정의된 member 변수는 상속은 가능하지만, 접근은 불가능하다 → 그래서 public으로 선언한 setter 또는 getter를 이용한다.

 

  • 상속 받은 기능 중 수정을 원하는 기능은 다시 재정의 할 수 있고(Overriding), 필요한 속성이나 기능은 추가하여 작성할 수 있다. 객체 생성의 경우에는 재사용만 가능하고 변경,추가는 불가능하다.

2. 단일 상속만 가능 (Single Inheritance)

  • 다중 상속의 경우 이런 단점이 있다.

1) 여러 조상들 중에서 이름이 같은 함수가 있을 경우, 문제의 소지가 있음.
-> 자바는 시스템에 문제가 생길 가능성을 아예 남겨두지 않으려고 하기 때문에 지원하지 않는다.

 

2) 너무 무거움. 필요 없는 부분 까지 받아야 한다.

 

  • 그러나 다중 상속의 장점 또한 많기 때문에, 자바는 interface 다중 구현을 제공한다.

3. 생성자는 상속되지 않는다

  • 왜 상속되지 않을까? -> 생성자는 반드시 클래스 이름과 동일하게 써야 하는데, 상속하면 클래스 이름이 달라지므로 클래스 이름과 동일하게 사용할 수 없어진다.
  • 그러나 재사용을 하기 위해서 호출은 가능하다(상속되는게 아니라 호출만 되는 것임! super(a,b,c) 이런 식으로). 이때 반드시 첫 줄에서만 가능하다.

Super : 현재 수행중인 객체의 상위 객체(super)의 reference를 가지고 있다.

  • super.멤버변수

상속받은 멤버 변수 호출 시, 현재 수행중인 객체의 멤버변수와 상속받은 super객체의 멤버변수 이름이 동일할 경우 사용한다.

 

  • super.method()

상속받은 메서드 호출 시, 현재 수행중인 객체의 메서드명과 상속받은 super객체의 메서드명이 동일할 경우 사용한다.

 

  • super(parameter_list)

parameter_list가 동일한 super 객체의 생성자를 호출한다.

(단, 부모 클래스의 생성자는 자식 클래스의 생성자의 1.첫 라인에서 2.super()로만 접근 가능하고, 3.this와 함께 사용 할 수 없다.)

 


상속 시 생성자 문제

class Solution {
    public static void main(String[] args) {
        Bus bus = new Bus("abc",1000,1000);
    }
}

class Car {
    String name;
    int number;
    public Car(String name, int number){
        this.name = name;
        this.number = number;
    }
}

class Bus extends Car {
    int fee;
    public Bus(String name, int number,int fee){
        this.name = name;
        this.number = number;
        this.fee = fee;
    }
}

 

이 코드는 실행 시 오류가 난다.

 

생성자의 기본 전제

사용자가 생성자를 만들지 않으면, 컴파일러가 default 생성자로 내용과 인자 모두가 자동으로 빈 public Car() {} 이런 생성자를 만들어준다.

그런데 만약 사용자가 생성자를 오버로딩해서 인자가 있는 생성자를 새로 만든다면, 컴파일러는 비어있는 생성자를 자동으로 만들어주지 않는다.

 

Car 클래스를 상속받은 Bus클래스의 생성자를 부르면 Bus 생성자는 사용자가 super();를 명시적으로 삽입하지 않아도 super();라는 내용을 자동으로 삽입한다.

즉, Car 클래스의 기본 생성자를 부르는 것이다.

 

그런데 지금 Car 클래스에서는 인자 있는 생성자가 이미 정의되어 있어서 인자 없는 기본 생성자가 자동으로 생성되지 않는다.그래서 컴퓨터 입장에서는 생성자가 없다고 생각해서 오류가 난다.

 

해결 방법

  1. 부모의 인자 없는 생성자가 불리지 않게 한다
class Bus extends Car{
    int fee;
    public Bus(String name, int number,int fee){
        super(name,number);
        this.fee = fee;
    }
}
  1. 부모 클래스에 인자 없는 생성자를 추가해 준다
class Car() {
    public Car() {}
    ...
}

Overriding

상속을 기반으로 super로부터 상속받은 기능 중 특정 기능을 재정의 하는 기법

sub 클래스에서 overriding 되는 메서드는

  1. 상속받은 super 클래스와 이름, 반환타입, 인자리스트가 반드시 같아야 한다.
  1. 접근제어자는 같거나 넓은 범위를 가져야 한다.

 


throws 처리

하위 클래스는

  1. 아무것도 throws 안하거나
  2. 상위 클래스에서 선언한 예외와 같거나
  3. 상위 클래스에서 선언한 예외의 하위 Exception거나
  4. 이 세가지 경우 말고 + Runtime 예외(Unchecked 예외) 또는 그 하위 예외의 경우에는 상위 클래스의 메서드와 관계없이 하위 클래스에서 throws 할 수 있다.

1. 상위 클래스의 메서드에서 throws를 선언했더라도, 하위 클래스는 예외를 throws 하지 않을 수 있다

상위 클래스나 인터페이스는 그를 구현하는 하위 클래스에 대해 제약을 거는 것이기 때문에, 상위 클래스에서 선언한 예외의 범위를 넘어서지만 않으면 된다.

class Parent {
    public void methodA() throws IOException, SQLException {
        ...
    }
}
// 1. 이렇게 throw가 아예 없어도 되고
class Child extends Parent {
        public void methodA() {
        ...
    }
}

// 2.부모의 throws 중 선택해서 선언해도 된다
class Child extends Parent {
    public void methodA() throws IOException {
        ...
    }
}

2. 상위 클래스의 throws와 같은 예외를 throws 할 수 있다

//상위 클래스의 예외와 똑같이 사용할 수 있다
class Child extends Parent {
     public void methodA() throws IOException, SQLException {
        ...
    }
}

단, 1번과 2번 규칙을 종합해보면 하위 클래스에서는 상위 클래스에서 사용한 throws 예외보다 더 상위의 예외를 throws 할 수 없다.

//이렇게 쓸 수 없다
class Child extends Parent {
    public void methodA() throws Exception {
        ...
    }
}

3. 상위 클래스에서 선언한 예외의 하위 예외

//ZipException과 XmlStreamReaderException은 IOException를 상속받은 하위 예외이므로 선언 가능
//SQLException은 Parent에서 선언되어 있기 때문에 마찬가지로 child에서도 선언 가능
class Child extends Parent {
        public void methodA() throws ZipException, XmlStreamReaderException, SQLException {
        ...
    }
}

4. RuntimeException과 그 하위 예외

//RuntimeException과 그 하위 예외들은 상위 클래스와 관계없이 하위 클래스에서 throws 가능
class Child extends Parent {
        public void methodA() throws RuntimeException {
        ...
    }
}

댓글