상속과 다형성은 객체 지향 프로그래밍의 핵심 개념들이다. 다른 객체 지향 프로그래밍의 요소들에는 어떤 것이 있는지에 대한 설명은 여기서 확인할 수 있다.
이 글에서는 상속과 다형성을 중점적으로 설명해 보겠다.
1. 상속(Inheritance)
상속은 아래처럼 Animal이라는 큰 부모 클래스의 속성과 메서드를 물려받아 재사용하고 확장할 수 있게 해 준다.
이 상속을 자식 클래스가 되는 클래스에서 extends를 이용해 선언할 수 있다.
이때, 부모 클래스의 public, protected는 속성과 메서드는 상속되지만 private은 상속되지 않는다.
// 부모 클래스
class Animal {
void eat() {
System.out.println("This animal eats food.");
}
}
// 자식 클래스
class Dog extends Animal {
void bark() {
System.out.println("The dog barks.");
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat(); // 부모 클래스의 메서드 호출
dog.bark(); // 자식 클래스의 메서드 호출
}
}
여기서는 Dog 객체는 부모의 메서드인 eat()과 자신의 메서드인 bark()를 모두 사용할 수 있다.
메서드 오버라이딩
자식 클래스는 부모 클래스의 메서드를 재정의(Override)할 수 있다. 메서드를 재정의 한다는 것은 자식 클래스에서 부모 클래스의 메서드 구현을 변경하는 것을 의미하고 이는 다형성을 구현하는 방법이다.
class Animal {
void eat() {
System.out.println("This animal eats food.");
}
}
class Dog extends Animal {
@Override
void eat() {
System.out.println("The dog eats kibble.");
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat(); // "The dog eats kibble." 출력
}
}
여기서는 부모에 있던 eat() 메서드를 재정의하였기 때문에 Dog 객체에서 eat()을 사용하면 "This animal eats food."가 출력되는 것이 아니라 "The dog eats kibble."이 출력된다.
Super
super 키워드를 사용하면 자식 클래스에서 부모 클래스에 접근하여 부모 클래스의 생성자나 메서드를 호출할 수 있다.
class Dog extends Animal {
@Override
void eat() {
super.eat(); // 부모 클래스의 eat() 메서드 호출
System.out.println("The dog eats kibble.");
}
}
상속의 제한
- 단일 상속: 자바는 단일 상속만 지원한다. 즉, 한 클래스는 하나의 부모 클래스만 가질 수 있다.
- final 키워드: 클래스나 메서드에 final 키워드를 사용하면 상속이 불가능하다.
상속의 장점
- 코드 재사용성: 기존 클래스의 코드를 재사용하여 새로운 클래스를 쉽게 작성할 수 있다.
- 유지보수 용이: 공통 기능을 부모 클래스에 두고 이를 상속받아 사용함으로써 유지보수가 용이해진다.
- 계층적 분류: 객체를 계층적으로 구성하여 더 명확하고 조직적인 코드를 작성할 수 있다.
2. 다형성(Polymorphism)
다형성은 같은 인터페이스를 구현(implements)하는 다양한 객체가 동일한 방식으로 동작하도록 하는 것이다. 다형성은 주로 메서드 호출 시 다양한 객체 타입을 사용할 수 있게 하여 코드의 유연성과 확장성을 높이게 해 준다.
타입의 다형성
자바에서는 부모 클래스 타입의 객체로 자식 클래스의 객체를 참조할 수 있다. 이를 통해 동일한 메서드 호출이 다르게 동작할 수 있다.
class Animal {
void makeSound() {
System.out.println("Some generic animal sound");
}
}
class Dog extends Animal {
@Override
void makeSound() {
System.out.println("Bark");
}
}
class Cat extends Animal {
@Override
void makeSound() {
System.out.println("Meow");
}
}
public class Main {
public static void main(String[] args) {
Animal myAnimal = new Animal();
Animal myDog = new Dog();
Animal myCat = new Cat();
myAnimal.makeSound(); // "Some generic animal sound"
myDog.makeSound(); // "Bark"
myCat.makeSound(); // "Meow"
}
}
만약, 여기서 Dog myDog = new Animal();처럼 부모 클래스 객체를 자식 클래스 타입에 할당하려고 시도하면 컴파일 오류가 발생한다.
interface를 통한 다형성
interface는 다형성을 구현하는 또 다른 방법이다. 여러 클래스가 동일한 인터페이스를 구현(implements)함으로써 다형성을 제공할 수 있다.
interface Animal {
void makeSound();
}
class Dog implements Animal {
@Override
public void makeSound() {
System.out.println("Bark");
}
}
class Cat implements Animal {
@Override
public void makeSound() {
System.out.println("Meow");
}
}
public class Main {
public static void main(String[] args) {
Animal myDog = new Dog();
Animal myCat = new Cat();
myDog.makeSound(); // "Bark"
myCat.makeSound(); // "Meow"
}
}
여기서 interface는 뼈대만 제공하고 구체적인 구현은 interface를 implements 한 클래스 내에서 이루어진다.
interface에 대한 자세한 설명은 여기서 확인할 수 있다.
동적 바인딩 (Dynamic Binding)
자바의 다형성은 런타임 시에 어떤 메서드가 호출될지 결정되는데, 이를 동적 바인딩 또는 늦은 바인딩(late binding)이라고 한다.
class Animal {
void makeSound() {
System.out.println("Some generic animal sound");
}
}
class Dog extends Animal {
@Override
void makeSound() {
System.out.println("Bark");
}
}
public class Main {
public static void main(String[] args) {
Animal myAnimal = new Dog(); // Animal 타입 변수에 Dog 객체 할당
myAnimal.makeSound(); // "Bark" 출력, Dog 클래스의 메서드 호출
}
}
'프로그래밍 언어 > JAVA' 카테고리의 다른 글
Enum - 2 (0) | 2024.07.08 |
---|---|
예외 처리 (0) | 2024.02.05 |
Static / Non-Static method (0) | 2024.01.29 |
Enum - 1 (feat. final) (0) | 2023.11.08 |
Stream (0) | 2023.11.04 |