다형성 활용
이전 파트에서 이해하지 못했던 다형적 참조를 어떤경우에 사용하는지 알 수 있다.
동물의 울음소리 문제로, 다형성을 사용하지 않은 코드이다.
package poly.ex1;
public class AnimalSoundMain {
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat = new Cat();
Cow cow = new Cow();
System.out.println("동물 소리 테스트 시작");
dog.sound();
System.out.println("동물 소리 테스트 종료");
System.out.println("동물 소리 테스트 시작");
cow.sound();
System.out.println("동물 소리 테스트 종료");
System.out.println("동물 소리 테스트 시작");
cat.sound();
System.out.println("동물 소리 테스트 종료");
}
}
// 코드가 중복된다.
이 부분의 중복을 제거하기 위해 메서드, 배열과 for문을 사용해 중복 제거를 시도해보겠다.
private static void soundCow(Cow cow) {
System.out.println("동물 소리 테스트 시작");
cow.sound();
System.out.println("동물 소리 테스트 종료");
}
soundCow 메서드의 매개변수는 Cow 전용 메서드가 되므로 Dog, Cat은 인수로 사용할 수 없다.
Caw[] cawArr = {cat, dog, caw}; //컴파일 오류 발생!
System.out.println("동물 소리 테스트 시작");
for (Caw caw : cawArr) {
cawArr.sound();
}
System.out.println("동물 소리 테스트 종료");
Arr엔 서로 다른 타입을 담을 수 없다. 또한 여기서 동물이 추가 된다면 더 많은 중복코드가 발생할 것이다.
이를 방지하기 위해 다형성을 활용한다.
다형성의 활용2
Dog, Cat, Cow의 상위 개념인 Animal 부모 클래스를 만들고 상속관계를 사용하면 코드 중복을 줄일 수 있다.

package poly.ex2;
public class AnimalPolyMain1 {
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat = new Cat();
Cow cow = new Cow();
Duck duck = new Duck();
soundAnimal(duck);
soundAnimal(dog);
soundAnimal(cat);
soundAnimal(cow);
}
private static void soundAnimal(Animal animal) {
System.out.println("동물 소리 테스트 시작");
animal.sound();
System.out.println("동물 소리 테스트 종료");
}
}

soundAnimal 메서드의 매개변수 Animal animal은 dog, cat, cow를 담고 있기때문에 각각의 인스턴스를 참조할 수 있다.
그 결과 코드 중복을 제거할 수 있었다.
개선된 코드
package poly.ex2;
public class AnimalPolyMain3 {
public static void main(String[] args) {
Animal[] animalArr = {new Dog(),new Cat(), new Cow(), new Pig()};
for (Animal animal : animalArr) {
soundAnimal(animal);
}
}
private static void soundAnimal(Animal animal) {
System.out.println("동물 소리 테스트 시작");
animal.sound();
System.out.println("동물 소리 테스트 시작");
}
}
package poly.ex2;
public class Animal {
public void sound() {
System.out.println("동물 울음 소리");
}
}
코드 중복을 많이 줄일 수 있었다. 그러나 이 코드엔 2가지 문제점이 남아있는데,
1. Animal 클래스를 생성할 수 있음
2. Animal 클래스의 sound( ) 메서드를 오버라이딩 하지 않을 가능성
이 문제를 해결하는 추상클래스가 있다.
추상 클래스
동물(Animal)과 같이 실제 생성되면 안되는 클래스를 추상 클래스라 한다. 동물은 추상적인 개념이기에 실체인 인스턴스가
존재하지 않는다. 대신에 상속을 목적으로 사용되고 부모 클래스 역할을 담당한다.
- 추상 클래스엔 abstract 키워드를 사용한다. ex) public abstract class Animal {......}
추상 메서드
- 자식 클래스가 반드시 오버라이딩 해야 하는 메서드를 부모 클래스에서 정의할 수 있다. 클래스와 마찬가지로 abstract 키워드를
사용한다. ex) public abstract void sound( );
- 추상 메서드는 바디가 없다. 따라서 자식 클래스에서 구현을 해야한다.
- 추상 메서드가 하나라도 있는 클래스는 추상 클래스로 선언해야 한다.
순수한 추상 클래스
package poly.ex4;
public abstract class AbstractAnimal {
public abstract void sound();
public abstract void move();
}

추상 클래스에 모든 메서드가 추상 메서드이다. 이 클래스는 단지 다형성을 위한 부모 타입으로 껍데기 역할만 하고 있는 셈이다.
- 인스턴스를 생성할 수 없다.
- 상속시 자식은 모든 메서드를 오버라이딩 해야한다.
- 주로 다형성을 위해 사용된다.
이 특징을 보면 순수 추상 클래스는 어떤 규격을 지켜서 구현해야 한다. 이것은 인터페이스와 같다.
(자바엔 순수 추상 클래스라는 용어가 없다. 순수 추상 클래스 == 인터페이스)
인터페이스
자바에서 순수 추상 클래스를 더욱 편리하게 사용할 수 있는 인터페이스 기능이 있다.
인터페이스의 특징
- 인터페이스의 멤버 변수는 public, static, final이 모두 포함되어 있다고 간주된다. 따라서 모든 필드는 상수로 설정이 된다.

나머지 내용은 순수한 추상 클래스와 유사하다. 순수 추상클래스가 인터페이스가 되었을 뿐이라고 생각하면 이해가 쉽다.
상속 vs 구현
부모 클래스를 상속 받을 때, 상속 받는다고 표현하지만 인터페이스를 상속받을 땐 인터페이스를 구현한다고 표현한다.
상속은 부모의 기능을 물려받는것이 목적인데, 인터페이스는 기능을 자식클래스에서 오버라이딩 후 직접 구현을 해야하기 때문에
상속이라고 표현하기 어렵다.
인터페이스를 사용해야하는 이유
- 제약 : 인터페이스는 구현하는 곳에서 인터페이스의 메서드를 반드시 구현해야하는 제약을 주는것이다. 순수 추상 클래스의 경우엔 추상 메서드가 아닌 메서드를 정의할 수 있다. 그렇게 되면 순수한 추상 클래스가 아니게 된다. 인터페이스는 이 문제를 원천 차단할 수 있다.
- 다중 구현: 자바에서는 하나의 부모만 상속받을 수 있는데, 인터페이스는 다중 구현(상속)이 가능하다.
인터페이스 - 다중 구현

다중 상속 그림이다. 이 그림대로라면 airplane의 move( ) 메서드와 Car의 move( ) 메서드 둘 중 어떤걸 사용해야할까? 애매한 상황이 발생한다. 이 문제를 다이아몬드 문제라고 한다. 그리고 다중상속은 계층구조가 매우 복잡해질 수 있어 자바에선 이를 허용하지 않는다.

위 그림은 다중 구현 그림이다. 다중상속처럼 생각해보면 둘 중 어떤 클래스의 methodCommon( ) 메서드를 호출해야할까?
둘 중 어떤것을 호출해도 상관없다. 왜냐면 추상메서드로 이루어져있기 때문에 Child에서 구현해주면 끝인 상황이다.

항상 오버라이딩 된 메서드가 우선순위라는걸 생각하면 이해하기 쉽다.
클래스와 인터페이스 활용

클래스와 인터페이스를 활용하면 위와 같이 메서드를 상속받을 수 있다.
package poly.ex6;
public class Bird extends AbstractAnimal implements Fly{
@Override
public void fly() {
System.out.println("새가 날아갑니다.");
}
@Override
public void sound() {
System.out.println("짹짹");
}
}
(코드 작성 예시)
크게 어려운 개념은 아닌 것 같다. 기초부분이라 그렇겠지만 메서드 호출이 어떤식으로 작동하는지 이해만 한다면 인터페이스까지의 내용은
어려운 점이 없었다. 객체지향 공부가 제일 재밌는것 같다
'Java' 카테고리의 다른 글
이것이자바다 - 배열 (1) | 2024.06.15 |
---|---|
다형성(3) - OCP (0) | 2024.06.13 |
다형성 (캐스팅, 메서드 오버라이딩) (1) | 2024.06.10 |
상속 (0) | 2024.06.07 |
final (0) | 2024.06.06 |