상속(inheritance)이란 기존의 클래스에 기능을 추가하거나 재정의하여 새로운 클래스를 정의하는 것을 의미한다.
명칭
기존 클래스 : 부모 클래스(parent class), 상위 클래스(super class) 또는 기초 클래스(base class)
새로운 클래스 : 자식 클래스(child class), 하위 클래스(sub class), 파생 클래스(derived class)
장점
- 기존에 작성된 클래스를 재활용할 수 있다.
- 자식 클래스 설계 시 중복되는 멤버를 미리 부모 클래스에 작성해 놓으면, 자식 클래스에서는 해당 멤버를 작성하지 않아도된다.
- 클래스 간의 계층적 관계를 구성함으로써 다형성의 문법적 토대를 마련한다.
특징
- 자식 클래스에는 부모 클래스의 필드와 메소드만이 상속되며, 생성자와 초기화 블록은 상속되지 않는다.
- 부모 클래스의 접근 제어가 private이나 default로 설정된 멤버는 자식 클래스에서 상속은받지만 접근은 할 수 없다. (사실상 상속을 받지 못하는 것과 같다고 볼 수도 있다.)
- 자바에서 클래스는 단 한 개의 클래스만을 상속받는 단일 상속만이 가능하다.
class Parent {
private int a = 10; // private 필드
public int b = 20; // public 필드
}
class Child extends Parent {
public int c = 30; // public 필드
void display() {
// System.out.println(a); // 상속받은 private 필드 참조 => Error
System.out.println(b); // 상속받은 public 필드 참조
출력 => 20
System.out.println(c); // 자식 클래스에서 선언한 public 필드 참조
출력 => 30
}
}
public class Inheritance01 {
public static void main(String[] args) {
Child ch = new Child();
ch.display();
}
자바에서 Object 클래스는 모든 클래스의 부모 클래스이다.
따라서 자바의 모든 클래스는 자동으로 Object 클래스의 모든 필드와 메소드를 상속받게 된다.
💎 자바의 모든 클래스는 별도로 extends 키워드를 사용하여 Object 클래스의 상속을 명시하지 않아도 Object 클래스의 모든 멤버를 자유롭게 사용할 수 있다. 따라서 자바의 모든 객체에서 toString()이나 clone()과 같은 메소드를 바로 사용할 수 있는 이유가 해당 메소드들이 Object 클래스의 메소드이기 때문이다.
super 키워드는 부모 클래스로부터 상속받은 필드나 메소드를 자식 클래스에서 참조하는 데 사용하는 참조 변수이다.
💎 특징
this와 마찬가지로 super 참조 변수를 사용할 수 있는 대상도 인스턴스 메소드뿐이며, 클래스 메소드에서는 사용할 수 없다.
예제)
class Parent {
int a = 10;
}
class Child extends Parent {
int a = 20;
void display() {
System.out.println(a); // 출력) 20
System.out.println(this.a); // 출력) 20
System.out.println(super.a); // 출력) 10
}
}
public class Inheritance03 {
public static void main(String[] args) {
Child ch = new Child();
ch.display();
}
}
this() 메소드가 같은 클래스의 다른 생성자를 호출할 때 사용된다면, super() 메소드는 부모 클래스의 생성자를 호출할 때 사용된다.
💎 특징
- 자바 컴파일러는 컴파일 시 클래스에 생성자가 하나도 정의되어 있지 않아야만, 자동으로 기본 생성자를 추가해 준다.
만약 부모 클래스에 매개변수를 가지는 생성자를 하나라도 선언했다면, 부모 클래스에는 기본 생성자가 자동으로 추가되지 않을 것이다.
- 매개변수를 가지는 생성자를 선언해야 할 경우에는 되도록이면 다음과 같이 기본 생성자까지 명시적으로 선언하는 것이 좋다. super()
예제)
class Parent {
int a;
Parent() { a = 10; }
Parent(int n) { a = n; }
}
class Child extends Parent {
int b;
Child() {
① //super(40);
b = 20;
}
void display() {
System.out.println(a);
System.out.println(b);
}
}
public class Inheritance04 {
public static void main(String[] args) {
Child ch = new Child();
ch.display();
}
}
//
위의 예제를 그냥 실행하면, 자바 컴파일러는 주석 처리된 ①번 라인에 자동으로 super(); 구문을 삽입할 것아다.
따라서 변수 a는 10으로 초기화된다.
하지만 ①번 라인의 주석 처리를 해제하고 실행하면, 부모 클래스인 Parent 클래스는 두 번째 생성자에 의해 초기화될 것이다.
따라서 변수 a는 40으로 초기화된다.
//
메소드 오버라이딩이란 상속받은 부모 클래스의 메소드를 재정의하여 사용하는 것을 의미한다.
💎 오버라이딩의 조건
- 오버라이딩이란 메소드의 동작만을 재정의하는 것이므로, 메소드의 선언부는 기존 메소드와 완전히 같아야 한다.
하지만 메소드의 반환 타입은 부모 클래스의 반환 타입으로 타입 변환할 수 있는 타입이라면 변경할 수 있다.
- 부모 클래스의 메소드보다 접근 제어자를 더 좁은 범위로 변경할 수 없다.
- 부모 클래스의 메소드보다 더 큰 범위의 예외를 선언할 수 없다.
예제)
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() 메소드입니다.
💎 위의 예제에서 세 번째와 같은 인스턴스의 참조가 허용되는 이유는 바로 자바에서의 다형성(polymorphism) 때문이다.