티스토리 뷰
자바의 상속에 대해 알아보겠습니다.
1. 자바 상속의 특징
상속이란
상속이란 상위 클래스에서 정의한 필드와 메서드를 하위 클래스에서도 동일하게 사용할 수 있게 물려받는 것이다.
상속을 사용하는 이유
코드를 재사용하기 편하고 클래스 간 계층구조를 분류하고 관리하기 쉬워진다.
자바 상속의 특징
- 서브클래스는 슈퍼클래스의 모든 멤버(필드, 메서드, 중첩 클래스)를 상속한다. 생성자는 멤버가 아니므로, 상속되지 않는다. 그러나 슈퍼클래스의 생성자는 서브클래스에서 실행될 수 있다. ex) super()
- 자손의 변경은 조상에 영향을 미치지 않는다.
- 자바는 단일상속만을 허용한다. (c++은 다중상속 허용)
- 명시적으로 슈퍼클래스를 지정해주지 않는다면, 모든 클래스의 암시적인 슈퍼클래스는 Object 클래스(모든 클래스의 조상)가 된다.
- 클래스들은 궁극적으로 최상위 클래스 Object에서 파생된 클래스에서 계속해서 파생될 수 있다.(상속의 횟수에 제한을 두지 않는다.) 따라서 모든 클래스는 Object의 자손이라고 볼 수 있다.
2. super 키워드
super
- super 키워드는 서브클래스에서 슈퍼클래스를 가리키기 위해 사용하는 키워드이다.
- super 키워드는 슈퍼클래스의 멤버에 접근하기 위해서 사용할 수 있고, 메서드 오버라이딩을 할 때 슈퍼클래스의 메서드를 호출해서 사용할 수도 있다. super를 사용해서 필드에 접근을 할 수도 있으나 권장되지 않는다.
- super 키워드는 멤버변수와 지역변수를 구분할 때 this 를 사용했듯 상속받은 멤버와 자신의 멤버를 구분할 때 사용한다.
슈퍼클래스 멤버에 접근
- 기본적으로 상속을 하면 슈퍼클래스의 멤버 중 private, default가 아닌 멤버들을 참조할 수 있다.
- 슈퍼클래스의 메서드와 동일한 시그니처를 가진 메서드를 선언한다면 이를 오버라이딩이라고 한다.(매개변수와 리턴 타입은 변경 불가)
- 오버라이딩 할 때는 컴파일러가 컴파일 타임에 체크할 수 있도록 @Override 애노테이션을 붙여주는 것이 좋다. (오타방지)
- 오버라이딩 된 메서드는 슈퍼클래스의 메서드를 가리키기 때문에 super라는 키워드로 호출을 해야 한다.
💡 LSP(리스코프 치환 원칙)
객체지향의 원칙 중에서 LSP 리스코프 치환 원칙(Liskov Substitution Principle)이 있는데, 오버라이딩을 하면서 이 원칙이 깨지도록 하면 안 된다. (LSP 원칙은 상위 타입의 객체를 하위 타입의 객체로 치환하더라도 상위 타입을 사용하는 다른 코드는 이전과 동일하게 동작해야 한다는 원칙이다.)
super()
- super() 키워드를 통해서 서브클래스는 부모 클래스의 생성자를 호출할 수 있다.
- 슈퍼클래스의 멤버는 슈퍼클래스의 생성자를 호출해서 초기화해야 한다.
- this()는 클래스의 다른 생성자를 호출하는 데 사용되지만, super()는 조상 클래스의 생성자를 호출하는 데 사용된다.
💡 조상 클래스의 생성자를 호출해야하는 이유
조상클래스의 생성자를 호출해야하는 이유는 자손 클래스의 멤버가 조상클래스의 멤버를 사용할 수도 있으므로 조상의 멤버들이 먼저 초기화되어 있어야 하기 때문이다. 때문에 무조건 Object 클래스를 제외한 모든 클래스의 생성자는 첫 줄에 반드시 자신의 다른 생성자 또는 조상의 생성자를 호출해야 한다. (넣지 않으면 컴파일러가 자동으로 추가한다.)
서브클래스에서 슈퍼클래스 생성자 호출
class PointTest {
public static void mian(String args[]) {
Point 3D p3 = new Point3D(1, 2, 3);
}
}
class Point {
int x, y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
String getLocation() {
return "x :" + x + ", y : " + y;
}
class Point3D extends Point {
int z;
Point3D(int x, int y, int z) {
// 생성자 첫 줄에서 다른 생성자를 호출하지 않기 때문에 컴파일러가 이 부분에 super();를 삽입한다.
// super()는 Point3D의 조상인 Point클래스의 기본 생성자인 Point()를 의미한다.
this.x = x;
this.y = y;
this.z = z;
}
String getLocation() { // 오버라이딩
retrun "x: " + x + ", y : " + y + ", z : " + z;
}
}
🚨 슈퍼클래스의 멤버는 슈퍼클래스의 생성자를 호출해서 초기화해야 한다.
super 클래스에 기본 생성자가 없는 경우에는 super()가 호출되면 에러가 난다.
따라서 위 코드에서 Point 클래스에 기본생성자가 없기 때문에 예외가 발생하게 되고,
이를 막기 위해선 super()가 Point(int x, int y) 를 호출하도록 super(x, y) 로 수정하면 된다.
따라서, 슈퍼클래스의 멤버는 슈퍼클래스의 생성자를 호출해서 초기화하도록 해야 한다.
메서드 오버라이딩 알아보기
3. 다이나믹 메서드 디스패치
다이나믹 메서드 디스패치는 자바의 테크닉 중 하나로, 런타임에 오버라이딩 된 메서드가 실행되는 것을 의미한다.
이는 참조 타입이 부모 타입인 경우에도 동작하므로 런타임 시점에 다형성을 만족하도록 할 수 있다.
public class Animal {
public void move() {
System.out.println("Animals can move");
}
}
public class Dog extends Animal {
@Override
public void move() {
System.out.println("Dogs can walk and run");
}
}
public class Main {
public static void main(String[] args) {
Animal a = new Animal(); // Animal 참조, Animal 객체
Animal b = new Dog(); // Animal 참조, Dog 객체
a.move(); // Animal 클래스에 정의된 메서드 실행
b.move(); // Dog 클래스에 정의된 메서드가 실행됨(다이나믹 메소드 디스패치)
}
}
3. 추상 클래스
추상 클래스는 abstract라는 키워드가 추가된 클래스이다. 이 클래스는 추상 메서드를 포함하고 있거나, 포함하지 않을 수 있다.
추상 클래스 자체는 인스턴스화를 할 수 없지만, 서브클래싱을 할 수는 있다. 클래스에 추상 메서드가 포함된 경우에는 반드시 그 클래스는 추상 클래스어야 한다.
인터페이스와의 비교
추상 클래스는 인터페이스와 비슷하지만 많은 점이 다릅니다.
비슷한 점
- 인스턴스화 할 수 없다.
- 메서드를 여러 개 혼합할 수 있다.
다른 점
- 추상 클래스는 필드를 정의할 수 있다. 접근제어자도 자유롭게 정의 가능하다. 반면 인터페이스에서 모든 필드는 public static final이다.
- 추상 클래스는 하나만 상속 가능하지만 인터페이스는 여러 개의 인터페이스를 구현할 수 있다.
추상 클래스와 인터페이스 어떤 것을 사용해야 할까?
추상 클래스
- 밀접하게 관련이 되어있는 여러 클래스 간의 코드 공유
- 추상 클래스를 확장하는 클래스 사이에 많은 공통 메서드, 필드가 존재하고, public 이외의 접근 제어자가 필요한 경우
- static, final 이 아닌 필드를 선언하려고 할 때, 상태를 액세스하고 수정할 수 있도록 하고 싶은 경우
인터페이스
- 관련 없는 클래스들이 인터페이스를 구현할 것으로 예상되는 경우(예, Comparable, Cloneable)
- 특정 데이터 타입의 동작을 정의하지만, 구현에는 신경 쓰고 싶지 않은 경우
- 타입의 다중 상속을 이용하려 하는 경우
💡 추상 클래스가 인터페이스를 상속하는 경우 꼭 모든 메서드를 구현하지 않아도 된다.
4. final 키워드
final이 사용될 수 있는 곳 - 클래스, 메서드, 멤버 변수, 지역변수
Java의 final 키워드는 변경 불가능의 의미를 가진다. 변수의 경우에는 읽기 전용이라고 생각하자.
메서드의 경우에는 오버라이드 불가를 나타낸다. 특히 생성자에서 호출되는 메서드는 final로 선언하는 편이 바람직하다. 왜냐하면 오버라이딩으로 동작을 변경할 수 있기 때문이다. (의도치 않는 변경을 예방할 수 있음)
클래스에 선언된 final 키워드는 서브클래싱 할 수 없다. 즉, 확장이 불가능하게 막는다.
참고
'Java' 카테고리의 다른 글
[Java] 오버라이딩(Overriding) (1) | 2022.08.24 |
---|---|
[Java] 자바는 왜 다중상속을 지원하지 않을까? (0) | 2022.08.24 |
[Java] 3. 연산자 (0) | 2022.08.06 |
[Java] 2. 자바 데이터 타입, 변수 그리고 배열 (0) | 2022.07.26 |
[Java] 1. JVM은 무엇이며 자바 코드는 어떻게 실행하는 것인가 (4) | 2022.07.21 |
댓글