[Java]상속

정석용·2023년 4월 7일
0

Java

목록 보기
6/15

상속(inheritance)

상속이란?

기존의 클래스에 기능을 추가하거나 재정의하여 새로운 클래스를 정의하는 것을 의미합니다.
캡슐화, 추상화와 더불어 객체 지향 프로그래밍을 구성하는 중요한 특징 중 하나 입니다.

상속을 이용하면 기존에 정의되어 있는 클래스의 모든 필드와 메소드를 물려받아, 새로운 클래스를 생성할 수 있습니다. 이때 기존에 정의되어 있던 클래스를 부모 클래스 또는 상위 클래스, 기초 클래스라고도 한다.
상속을 통해 새롭게 작성되는 클래스를 자식 클래스 또는 하위 클래스, 파생 클래스라고도 한다.

상속의 장점

  • 기존에 작성된 클래스를 재활용할 수 있다.
  • 자식 클래스 설계 시 중복되는 멤버를 미리 부모 클래스에 작성해 놓으면, 자식 클래스에서는 해당 멤버를 작성하지 않아도 된다.
  • 클래스 간의 계층적 관계를 구성함으로써 다형성의 문법적 토대를 마련.

자식 클래스

부모 클래스의 모든 특성을 물려받아 새롭게 작성된 클래스를 의미

부모 클래스에 새로운 필드를 하나 추가하면, 자식 클래스에도 자동으로 해당 필드가 추가된 것처럼 동작한다
자식 클래스에는 부모 클래스의 필드와 메소드만이 상속되며, 생성자와 초기화 블록은 상속되지 않는다.
또한,부모 클래스의 접근 제어가 private이나 default로 설정된 멤버는 자식 클래스에서 상속받지만 접근할 수는 없다.

EX.

class 자식클래스 이름 extend 부모클래스 이름


부모 클래스와 자식 클래스 간의 관계를 나타낸 그림이다.

Object 클래스

자바에서 모든 클래스의 부모 클래스가 되는 클래스
따라서 자바의 모든 클래스는 자동으로 Object 클래스의 모든 필드와 메소드를 상속받게 됩니다.

자바의 모든 클래스는 별도로 extends 키워드를 사용하여 Object 클래스의 상속을 명시하지 않아도 Object 클래스의 모든 멤버를 자유롭게 사용할 수 있다.
자바의 모든 객체에서 toString()이나 clone()과 같은 메소드를 바로 사용할 수 있는 이유가 해당 메소드들이 Object 클래스의 메소드이기 때문이다.

Super

Super 키워드

부모 클래스부터 상속받은 필드나 메소드를 자식 클래스에서 참조하는데 사용하는 참조 변수이다.

인스턴브 변수의 이름과 지역 변수의 이름이 같을 경우 인스턴스 변수 앞에 this 키워드를 사용하여 구분할 수 있다.
이와 마찬가지로 부모 클래스의 멤버와 자식 클래스의 멤버 이름이 같을 경우 super 키워드를 사용하여 구별할 수 있다.
이렇게 자바에서는 super 참조 변수를 사용하여 부모 클래스의 멤버에 접근할 수 있다.this와 마찬가지로 super 참조 변수를 사용할 수 있는 대상도 인스턴스 메소드뿐이며, 클래스 메소드에서는 사용할 수 없다.

EX.

class Parent { int a = 10; }
 
class Child extends Parent {
    void display() {
        System.out.println(a);
        System.out.println(this.a);
        System.out.println(super.a);
    }
}
 
public class Inheritance02 {
    public static void main(String[] args) {
        Child ch = new Child();
        ch.display();
    }
}

실행결과.

10
10
10

위에 예제에서 int형 변수 num은 부모 클래스인 Parent 클래스에서만 선언되어 있다. 따라서 지역 변수와 this 참조 변수 그리고 super 참조 변수 모두 같은 값을 출력한다.

Super()

this() 메소드가 같은 클래스의 다른 생성자를 호출할 때 사용된다면, super() 메소드는 부모 클래스의 생성자를 호출할 때 사용된다.

자식 클래스의 인스턴스를 생성하면, 해당 인스턴스에는 자식 클래스의 고유 멤버뿐만 아니라 부모 클래스의 모든 멤버까지고 포함되어 있다.
따라서 부모 클래스의 멤버를 초기화하기 위해서는 자식 클래스의 생성자에서 부모 클래스의 생성자까지 호출해야만 한다.
이러한 부모 클래스의 생성자 호출은 모든 클래스의 부모 클래스인 Object 클래스의 생성자까지 계속 거슬러 올라가 수행한다.

따라서 자바 컴파일러는 부모 클래스의 생성자를 명시적으로 호출하지 않는 모든 자식 클래스의 생성자 첫 줄에 자동으로 다음과 같은 명령문을 추가하여, 부모 클래스의 멤버를 초기화 할 수 있도록 해준다.
super();

메소드 오버라이딩(method overriding)

상속 관계에 있는 부모 클래스에서 이미 정의된 메소드를 자식 클래스에서 같은 시그니쳐를 갖는 메소드로 다시 정의하는 것이라고 할 수 있다.

앞서 배운 오버로딩이란 서로 다른 시그니처를 갖는 여러 메소드를 하나의 이름으로 정의하는 것이다.

자바에서 자식 클래스는 부모 클래스의 private 멤버를 제외한 모든 메소드를 상속 받는다. 이렇게 상속받은 메소드는 그대로 사용해도 되고, 필요한 동작을 위해 재정의하여 사용할 수도 있다.
즉, 메소드 오버라이딩이란 상속받은 부모 클래스의 메소드를 재정의하여 사용하는 것을 의미한다.

메소드 오버라이딩의 조건

  • 오버라이딩이란 메소드의 동작만을 재정의하는 것이므로, 메소드의 선언부는 기존 메소드와 완전히 같아야 한다. 하지만 메소드의 반환 타입은 부모 클래스의 반환 타입으로 타입 변환할 수 있는 타입이라면 변경할 수 있다.
  • 부모 클래스의 메소드보다 접근 제어자를 더 좁은 범위로 변경할 수 없다.
  • 부모 클래스의 메소드보다 더 큰 범위의 예외를 선언할 수 없다.

EX.

class Parent {
    void display() { System.out.println("부모 클래스의 display() 메소드입니다."); }
}
class Child extends Parent {
    void display() { System.out.println("자식 클래스의 display() 메소드입니다."); }
}
 
public class Inheritance05 {
    public static void main(String[] args) {
        Parent pa = new Parent();
        pa.display();
        Child ch = new Child();
        ch.display();
        Parent pc = new Child();
        pc.display(); // Child cp = new Parent();
    }
}

실행결과

부모 클래스의 display() 메소드입니다.
자식 클래스의 display() 메소드입니다.
자식 클래스의 display() 메소드입니다.

위의 예제에세 세 번째와 같은 인스턴스의 참조가 허용되는 이유는 바로 자바에서의 다형성 때문이다.

오버로딩과 오버라이딩

차이점
오버로딩은 새로운 메소드를 정의하는 것이고 오버라이딩은 상속받은 기존의 메소드를 재정의 하는 것이다.

추상 클래스

추상 메소드

자식 클래스에서 반드시 오버라이딩해야만 사용할 수 있는 메소드

자바에서 추상 메소드를 선언하여 사용하는 목적은 추상 메소드가 포함된 클래스를 상속받는 자식 클래스가 반드시 추상 메소드를 구현하도록 하기 위함이며, 자신에게 필요한 부분만을 재정의하여 사용함으로써 생산성이 향상되고 배포 등이 쉬워진다.

abstract 반환타입 메소드이름();

선언부만 존재하고 구현부가 업다는 의미로 선언부 끝에 바로 세미콜론을 추가.

추상 클래스

하나 이상의 추상 메소드를 포함하는 클래스를 가리켜 추상 클래스라고 한다.

abstract class 클래스이름 {
    ...
    abstract 반환타입 메소드이름();
    ...
}

이러한 추상 클래스는 동작이 정의되어 있지 않은 추상 메소드를 포함하고 있으므로, 인스턴스를 생서할 수 없다.
추상 클래스는 먼저 상속을 통해 자식 클래스에서 추상 클래스의 모든 추상 메소드를 오버라이딩하고 나서야 비로소 자식 클래스의 인스턴스를 생성할 수 있다.

추상 메소드의 사용 목적
자바에서 추상 메소드를 선언하여 사용하는 목적은 추상 메소드가 포함된 클래스를 상속받는 자식 클래스가 반드시 추상 메소드를 구현하도록 하기 위함이다.
만약 일반 메소드로 구현한다면 사용자에 따라 해당 메소드를 구현할 수도 있고, 안 할 수도 있다. 하지만 추상 메소드가 포함된 추상 클래스를 상속받은 모든 자식 클래스는 추상 메소드를 구현해야만 인스턴스를 생성할 수 있으므로, 반드시 구현하게 된다.

메서드 디스패치(Method Dispatch)

어떤 메서드를 호출할지 결정하여 실제로 실행시키는 과정이다. 자바는 런타임 시 객체를 생성하고, 컴파일 시에는 생성할 객체 타입에 대한 정보만 보유한다. 이에따라 이 과정은 static과 dynamic이 있다.

static dispatch

컴파일 시점에서, 컴파일러가 특정 메소드를 호출할 것이라고 명확하게 알고 있는 경우이다.(정적)

  • 컴파일 시 생성된 바이트 코드에도 이 정보가 그대로 남아있다.
  • 런타임(실행 시점)이 되지 않아도 미리 결정하는 개념이다.
  • 함수를 오버로딩하여 사용하는 경우 인자의 타입이나 리턴타입 등에 따라 어떤 메서드를 호출할지 알 수 있는 경우

Dynamic Dispatch

정적 디스패치와 반대로 컴파일러가 어떤 메서드를 호출하는지 모르는 경우이다. 동적 디스패치는 호출할 메서드를 런타임 시점에서 결정한다. (동적)

  • 인터페이스나 추상 클래스에 정의된 추상 메소드를 호출하는 경우.
  • 인터페이스나 추상 클래스로 선언하고 구현/상속 받은 하위 클래스의 인스턴스를 생성.
  • 컴파일러가 알고 있는 타입에 대한 정보를 토대로 런타임 시 해당 타입의 객체를 생성하고 메서드를 호출.

Double Dispatch

Dynamic Dispatch를 두 번 하는 것을 의미한다.

일반적으로 OOP는 객체가 스스로 행위를 수행하기 하지만, 경우에 따라(ex.확장성 고려, OCP 위배) 객체의 행위 수행을 외부 클래스에 위임. 이때 사용하는 디자인 패턴 종류는 전략패턴, 커맨드패턴, 방문자패턴이 있다.

final

final 키워드는 변수, 메서드, 또는 클래스에서 사용될 수 있다. 이 final 키워드는 어던 곳에 사용되냐에 따라 다른 의미를 가진다. 하지만 final 키워드를 붙이면 무언가를 제한한다는 의미를 가지는 것은 공통적인 성격이다.

변수
변수에서의 final 사용은 이 변수는 수정할 수 없다는 의미를 가진다. 수정될 수 없기 때문에 초기화 값은 필수적이다. 만약에 객체안의 변수라면 생성자, static 블럭을 통한 초기화까지는 허용한다.

수정 할 수 없다는 범위는 그 변수의 값에 한정한다. 즉, 다른 객체를 참조하거나 할 때 참조하는 객체의 내부의 값은 변경할 수 있다라는 의미이다.

인자(arguments)
final로 선언된 인자는 메소드 내에서 변경이 불가능하다. 따라서 다음과 같이 final int로 선언한 number는 읽을 수 있지만, number=2처럼 값을 변경하려고 하면 컴파일 에러가 발생한다.

클래스
클래스에 final을 붙이면 다른 클래스가 상속할 수 없는 클래스가 된다.

메소드
final 메소드는 Override가 안되도록 한다. 클래스를 상속하는 클래스에서 메소드를 재정의 할 수 없다.컴파일 에러 발생

+Effective final keyword
Java8에서 추가된 기능으로, final이 붙지 않은 변수의 값이 변경되지 않는다면 그 변수를 effectively final이라고 한다. finald이 붙이지 않았지만 컴파일러가 final로 취급하는 것이다.

래퍼런스
https://doompok.tistory.com/21
http://www.tcpschool.com/java/java_polymorphism_abstract
https://sabarada.tistory.com/148

profile
오늘도 성장중

0개의 댓글