✅ 1. 다형성
(1) 다형성이란?
- 다형성(ploymorphism)은 하위 클래스 객체를 생성하고 상위 클래스 타입으로 받는 것을 의미한다.
- 쉽게말해, 객체를 생성할 때 해당 객체의 타입으로 받는 것이 일반적이다.
- 하지만, 자바에서 제공하는 다형성을 활용해 해당 객체 타입이 아닌, 상위클래스 타입으로 받아서 유연하게 이용 할 수 있다.
- 일반적인 방식: Dog dog = new Dog();
- 다형성 적용 방식: Animal dog = new Dog(); - 상위 클래스로 참조 타입을 받을 수 있음.
(2) 다형성 쓰는 이유
public class Animal {
public void sound() {
System.out.println("동물소리");
}
}
public class Cat extends Animal {
@Override
public void sound() {
System.out.println("고양이 소리");
}
}
public class Dog extends Animal {
@Override
public void sound() {
System.out.println("강아지 소리");
}
}
public class Main {
public static void main(String[] args) {
Animal cat = new Cat();
Animal dog = new Dog();
dog.sound(); // "강아지 소리" 출력
cat.sound(); // "고양이 소리" 출력
}
}
- 상위클래스 참조변수가 가리키는 객체의 실제 유형에 따라 수행되는 메서드가 결정이 되며, 이것은 동적 바인딩이라한다.
- 유연함: Animal 상위 클래스를 상속 받는 Dog, Cat은 Main 클래스에서 Animal 타입 하나로 통일되게 받을 수 있다.
- 유지보수: 상위 클래스 타입으로 받을 수 있어, 여러 하위 클래스를 묶어서 이용할 수도 있다.
✅ 2. 다형성의 한계
// Animal: 상위클래스
public class Animal {
}
// Cat: 하위클래스
public class Cat extends Animal {
public void run() {
System.out.println("고양이 걸음");
}
}
// Main
public class Main {
public static void main(String[] args) {
Animal cat = new Cat();
cat.run(); ❌ 컴파일 에러 (Animal엔 run이 없으니까)
}
}
- Animal을 상속받는 Cat을 new Cat()으로 객체생성을 하게 되면 Animal의 모든 필드와 메서드를 이용할 수 있게 된다.
- 즉, Cat은 Animal을 포함하고 있으므로, Animal 타입으로 Cat 객체를 받을 수 있다는 의미이다. ( 다형성 )
- 하지만, 문제는 코드에서 보면 cat.run() 또한 Cat 객체로 만들었기 때문에 사용이 가능해야 하는데 안된다.
- 이유는, 코드 문맥상에서 문제가 발생할 수 있다고 판단이 되기 때문에 컴파일 과정에서 오류를 내뱉는 것이다.
- 근데 사실은 원래 컴파일러가 아니면 JVM 상에서는 문제없이 호출이 된다. 이것이 한계이다.
(1) 컴파일 시점에서 막아둔 이유
Animal a = new Cat();
a.run(); // ❌ 컴파일 에러 (Animal엔 run이 없으니까)
Animal a = getRandomAnimal(); // Dog? Cat? 모르지
a.run(); // ❌ 만약 Dog이면 run() 없어서 진짜로 런타임 에러 발생
- 자바는 컴파일 타임에 타입 안정성을 매우 중시한다.
- Animal 타입으로 뭘 받을지 모르는 상황과 해당 클래스에 run() 메서드가 있을지 없을지 모르기 때문에 오류를 뱉는다.
- 즉, 참조 타입을 상위클래스로 받는 과정은 타입만 바뀌는거지 아직 하위클래스와 상위클래스 모두를 가지고 있는 상태이다.
- 문제는 컴파일 시점에 위와 같은 문맥상 발생하는 문제를 막기 위해 하위클래스에만 있는 메서드는 호출이 불가능하게 만듬
(2) 정리
- 다형성에서 하위클래스만 가지고 있는 메서드는 사실은 호출이 가능하지만, 컴파일 시점에 막아둠
- 컴파일 시점에 문맥상 안전하지 않다는 이유로 막아둔 것임.
- 즉, 하위 클래스만 가진 메서드는 참조 변수를 하위 클래스 타입으로 바꿔야 호출이 가능하다는 의미임. ( 업/다운 캐스팅 )
- 오버라이딩된 메서드는 가능함. 이유는, 상위클래스에 정의가 되어있다는 필수적인 규칙이 존재하기 때문
(3) 두 줄 정리
- 객체는 “내가 속한 클래스 + 부모 클래스”의 모든 정보를 가지고 있고, 다형성은 컴파일 시점에 ‘안전한 호출’을 위해 부모 타입으로 제한하지만, 런타임에는 실제 객체 메서드를 실행한다는 것!
- 다형성은 진짜진짜 단순히 코드의 유연성과 유지보수 때문에 만들어졌지만, 문제는 자바 문맥 구조상의 한계로 제한을 두게 된 것이며, 이 때문에 다형성이 어렵게 느껴진 것 이라고 생각이 든다.
자바 메서드 호출 원리
- Heap에 생성된 객체 인스턴스는 필드(인스턴스 변수) 데이터만 가지고 있고, 메서드는 가지고 있지 않는다.
- 실제 메서드는 MethodArea(클래스 영역)에 들어가 있는 정보에 메서드 코드의 위치를 참조하게 된다.
- 즉, Heap 내부에는 MethodArea의 메서드를 참조값만을 가지고 있는 것이다.
'공부 > ☕Java' 카테고리의 다른 글
| [Java] 제네릭(Generic) 문법 정리: 클래스, 메서드, 제한까지 (0) | 2025.05.23 |
|---|---|
| [Java] 익명 클래스(Anonymous Class) (1) | 2025.05.22 |
| [Java] 추상화 - 추상클래스, 추상메서드, 인터페이스 (0) | 2025.05.20 |
| [Java] Enum 쉽게 이해하기 (1) | 2025.05.05 |
| [Java] JVM과 자바 실행 원리 (1) | 2025.04.14 |