- 자바 상속의 특징
- super 키워드
- 메소드 오버라이딩
- 다이나믹 메소드 디스패치 (Dynamic Method Dispatch)
- 추상 클래스
- final 키워드
- Object 클래스
상속이란
기반이 되는 상위 클래스의 특성을 하위 클래스에게 적용하고, 거기에 더해 필요한 특성을 추가 확장하는 방식을 말한다.
상속의 목적은 기존 기능의 확장과 코드의 재사용이다.
상속의 장점
하위클래스에 상위 클래스를 extend 만 하여도 코드가 자동적으로 재사용된다는 것이며, 확장성을 위해 변경하여야 할 부분의 로직만 overiding을 사용하여 재정의도 가능하다.
상속 시 단점
상속을 사용하여 코드를 재사용하느 경우에는 기존 상위 클래스가 어떻게 작성되었는지 고려해야 한다.
상속의 특성상 상위 클래스와 하위 클래스가 결합상태로 묶여, 추후 코드의 수정으로 상위클래스의 변경 여파가 하위클래스에 전파될 수 있는 상황이 발생한다.
상속을 사용하는 경우
프로젝트 구조 자체가 상속을 위해 설계된 경우
is-a 관계가 명확한 경우 (동물 -> 포유류 -> 고래 등 상위 분류와 하위분류가 명확한 경우)
이미 존재하는 클래스의 메서드와 필드를 재사용할 수 있기때문에 사용한다. 하지만 단순히 재사용만을 위해 상속을 사용하게 되면 oop 의 목적과는 맞지 않는 클래스가 될 수 있다.
자바 상속의 특징
다중상속을 지원하지 않는다.
모든 클래스는 Object 클래스의 자식 클래스이다.
자바의 상속 구조
단일 상속 구조
public class 상위클래스 {
}
public class 하위클래스 extends 상위클래스 {
}
계층적 상속 구조
public class 상위클래스 {
}
public class 하위클래스A extends 상위클래스 {
}
public class 하위클래스B extends 상위클래스 {
}
자바는 다중 상속을 허용하지 않는다.
다중 상속을 허용하지 않는 이유는 하위 클래스의 코드를 복잡하게 만들기 떄문이다.
다중상속을 하려면 인터페이스를 이용한 다중 상속이 있다.
public class 상위클래스 {
}
public interface 인터페이스{
}
public class 하위클래스 extends 상위클래스 implements 인터페이스 {
}
Super 키워드
super 키워드는 자식클래스에서 부모 클래스를 가리키는 키워드이다.
super 키워드를 통해 자식클래스는 부모 클래스의 필드나 메서드를 호출할 수 있다.
super() 메서드는 부모클래스의 생성자를 호출하는 메서드이다.
자식클래스의 생성자에는 기본적으로 super() 를 호출해야 하는데 기입하지 않더라도 컴파일러가 기본으로 추가한다.
class Parent {
public Parent() {
System.out.println("부모 클래스 생성자");
}
}
class Child extends Parent {
public Child() {
// super(); 내부적으로 동작한다.
System.out.println("자식 클래스 생성자");
}
}
class Main {
public static void main(String[] args) {
Parent p = new Parent();
System.out.println(">>");
Child c = new Child();
}
}
// 출력내용
부모 클래스 생성자 // 얘도 알고보면 앞에 Object의 생성자가 호출 되겠지.
>>
부모 클래스 생성자다. // 부모클래스의 생성자가 호출되고 자식 클래스의 생성자가 호출된다.
자식 클래스 생성자다.
자식클래스의 기본생성자를 만들때 부모 클래스의 기본 생성자가 없으면 컴파일 에러가 생긴다.
class Parent { // 기본 생성자가 없는 부모 클래스
String name;
public Parent(String name) {
this.name = name;
}
}
class Child extends Parent {
public Child() { // 이 경우 부모 클래스에 기본 생성자가 없기 때문에
// super(); // 컴파일 에러가 난다.
}
public Child() {
super("super class"); // 부모 클래스의 생성자가 정확히 호출 되므로 에러가 없다.
}
}
메서드 오버 라이딩
오버라이딩은 부모클래스로부터 상속받은 메서드를 자식 클래스에서 재정의 하는 것이다.
class Parent {
public void say() {
System.out.println("I`m parent.");
}
}
class Child extends Parent {
@Override
public void say() {
System.out.println("I`m child.");
}
}
class Main {
public static void main(String[] args) {
Parent parent = new Parent();
Child child = new Child();
parent.say();
child.say();
}
}
@Override 어노테이션은 없어도 되지만 개발자에게 부모클래스에 있는 메서드라는것을 명시하기 위해 쓰는 것이다.
부모클래스에 없는 메서드에 어노테이션을 붙일 경우 컴파일 오류가 난다.
다이내믹 메서드 디스패치 (Dynamic Method Dispatch)
메서드 디스패치란 메소드를 어떻게 호출할 지를 정해서 호출하는 것이다.
정적 메소드 디스패치 (static method dispatch) 는 메서드가 어떻게 실행될지가 컴파일 타임에 결정된다.
스태틱 메소드 디스패치 (Static Method Dispatch)
스태틱 메소드 디스패치는 구현 클래스를 통해 컴파일 시점에서 컴파일러가 어떤 메소드를 호출할지 명확하게 알고 있는 경우
컴파일 시 생성된 바이트코드에도 정보가 남아있으며 애플리케이션 실행 전에 호출할 메소드 결정
대표적으로, 메소드를 오버로딩(Overloading)하면 매개변수 타입과 개수에 따라 어떤 메소드를 호출할지 알 수 있는 경우(메소드 시그니처 변경)
상위 클래스가 있더라도 하위 클래스(구현 클래스)로 선언을 하고 하위 클래스의 인스턴스를 생성
다이나믹 메소드 디스패치 (Dynamic Method Dispatch)
다이나믹 메소드 디스패치는 인터페이스나 추상 클래스에 정의된 추상 메소드를 호출하는 경우로, 호출되는 메소드가 런타임 시 동적으로 결정되는 것
인터페이스 또는 추상 클래스로 선언하고 구현/상속 받은 하위 클래스의 인스턴스를 생성
컴파일러가 알고 있는 타입에 대한 정보를 토대로 런타임 시 해당 타입의 객체를 생성하고 메소드를 호출
public interface Animal {
String method();
}
public class Dog implements Animal {
@Override
public String method() { . . . }
public void bark() { . . . }
}
public class Cat implements Animal {
@Override
public String method() { . . . }
public void meow() { . . . }
}
// 예시 1
public static void main(String[] args) {
// 다이나믹 메소드 디스패치
Animal animal = new Dog();
System.out.println(animal.method());
}
// 예시 2
public class Example {
private Animal animal;
public Example(Animal animal) {
this.animal = animal;
}
public void print() {
// 다이나믹 메소드 디스패치
System.out.println(animal.method());
}
}
- 런타임 전에는 객체 생성이 되지 않기 때문에 Animal animal = new Dog();를 해도, 컴파일러는 Dog가 생성됨을 알 수 없으므로 Animal이 정의한 method() 메소드만 접근 가능
- Example 클래스의 생성자 인자로 넘어오는 매개값의 타입이 Dog인지 Cat인지(혹은 그 외)는 실행 시 확인 가능. 실행 시 동적으로 확인해서 다이나믹 디스패치
더블 디스패치 (Double Dispatch)
다이나믹 메소드 디스패치가 2번 발생하는 것
디자인 패턴 중 방문자 패턴 (Visitor Pattern) 과 밀접한 관계
방문자 패턴 (Visitor Pattern)
일반적으로 OOP는 객체가 스스로 행위를 수행하게 하지만, 경우에 따라(ex. 확장성 고려, OCP 위배) 객체의 행위 수행을 외부 클래스에 위임
이 때 사용하는 디자인 패턴 종류는 전략 패턴, 커맨드 패턴, 방문자 패턴
- 전략 패턴 (Strategy Pattern)
- 하나의 객체가 여러 동작을 하게 하는 패턴 (1:N)
- 커맨드 패턴 (Command Pattern)
- 하나의 객체가 하나의 동작(+보조 동작)을 하게 하는 패턴 (1:1)
- 방문자 패턴 (Visitor Pattern)
- 여러 객체에 대해 각 객체의 동작들을 지정하는 패턴 (N:N)
추상클래스
추상(abstract)이란 실체 간에 공통되는 특성을 추출한 것
추상 클래스는 객체를 직접 생성할 수 있는 실체 클래스의 공통적인 특성(필드, 메소드)을 추출해서 선언한 클래스
추상 클래스는 인스턴스 생성 불가능
Animal animal = new Animal(); // (X)
Animal animal = new Dog(); // (O)
클래스 선언 시 abstract 키워드 추가
public abstract class 클래스 {
// 필드
// 생성자
// 메소드
}
- 추상 클래스를 상속받은 자식 객체가 생성될 때, 내부에서 super 메소드가 호출되어 추상 클래스 객체를 생성하기 때문에 생성자 필요
추상 메소드 오버라이딩
추상 클래스는 자식 클래스의 공통 기능을 추출해 구현한 클래스이지만, 경우에 따라 자식 클래스마다 구현 내용이 달라질 수 있기 때문에 필요 시 추상 메소드를 작성
추상 메소드는 추상 클래스에서만 선언 가능하며, 메소드 선언부만 있는 형태
자식 클래스가 반드시 실행 내용을 구현하게 하기 위해 추상 메소드 선언
public abstract class Animal {
public abstract void sound();
}
구현
public class Dog {
@Override
public void sound() {
System.out.println("bark");
}
}
public class Cat {
@Override
public void sound() {
System.out.println("meow");
}
}
final 키워드
메소드에 붙는 경우 - 오버라이딩 불가
class Parent {
final void run() { // 메소드에 final이 붙는 경우.
System.out.println("run");
}
}
class Child extends Parent {
@Override // 오버라이딩을 못함, 컴파일 에러.
void run() {
}
}
클래스에 붙는 경우 - 상속 불가
final class Parent { // 클래스에 final이 붙는 경우.
}
class Child extends Parent { // 상속받을 수 없음, 컴파일 에러.
}
Object 클래스
자바에서 모든 클래스들의 상위 클래스이다. 내가 임의로 만든 클래스도 Object 클래스를 상속받고 있다. extends Object를 써넣지 않았는데도 어떻게 이게 되는 걸까? 이것은 컴파일러가 컴파일 타임에 쓱 끼워 넣어 준다.
내가 만든 클래스는 다른 클래스를 상속받고 있는데, 다중 상속이 되지 않는 자바인데 어떻게 Object 상속 부분을 끼워 넣어줄까? 이것은 컴파일러가 똑똑하게 가상 상위 클래스를 찾아 거기에 Object 상속을 슥 끼워넣어 준다.
이렇게 해서 직접적으로나 간접적으로나 모든 클래스는 Object 클래스를 상속받는다.
'Java > Java 기본 및 이론' 카테고리의 다른 글
자바 primitive 와 Reference type 차이 및 사용 (0) | 2024.06.07 |
---|---|
[Java] 자바 클래스 와 객체 및 키워드 (0) | 2021.01.17 |
[Java] 자바 기본 제어문 - 선택문, 반복문 (0) | 2021.01.11 |
[Java] junit5 설명 및 기본 사용 - junit4 와 5 비교 (0) | 2021.01.11 |
[Java] 자바의 연산자 (0) | 2021.01.07 |