티스토리 뷰
추상클래스 와 인터페이스는 언제 사용하면 될까요?
추상클래스, 인터페이스는 혼자서는 인스턴스화 될 수 없으면 자바에서 상속을 위해 사용할 수 있는 기법입니다.
그럼 언제 추상클래스를 사용하고 인터페이스를 사용할까요?
자바8+ 이상부터는 인터페이스의 큰 변화가 생겼지만 (java 8 interface change) 여기서는 논외로 두겠습니다.
우선 추상 클래스 와 인터페이스의 스펙상 차이점을 알아보겠습니다.
추상클래스
- 단일 상속
- 추상 메서드와 구현 메서드 두 가지 모두를 가질 수 있음
- 접근제어자 public,private,protected 메서드,변수를 가질 수 있음
인터페이스
- 다중 상속
- 메서드 바디를 가질 수 없음
- 접근제어자는 public 메서드 만 가능 (접근제어자 생략시 default 값이 public), 클래스(static) 변수, 상수(final) 만 가질 수 있음
간단하게 살펴보면 위와 같은 차이점이 있습니다.
그렇기 때문에 다중상속을 사용하기 위해서는 인터페이스를 사용하면 되고 public 외 접근제어자를 가지는 메서드 또는 메서드 구현하기 위해서는 추상 클래스를 사용하면 됩니다.
하지만 더 이야기하고 싶은건 OOP 관점에서 본 차이점입니다. 결국 상속(extends) 과 구현(implements)을 언제 사용하느냐 인가죠
클래스 상속(Extends)
- IS - A
- 연관 관계가 깊은 여러 클래스들의 코드 재사용 관점
- HAS-A
- 객체의 메세지 추상화 관점
위에 업근한 내용이 딱 와닿지 않고 막연합니다.^^;
언제나 그렇듯이 개발자는 코드로 살펴보는 게 이해하기 좋습니다. 아주 간단한 시나리오가 있고 모델링을 해보겠습니다.
시나리오
"독수리와 올빼미가 있습니다. 날카로운 부리 와 날개를 가지고 있고 하늘을 날 수 있습니다."
Eagle.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public class Eagle { private static final String KO_NAME = "독수리"; private Beak a; private Wing b; public Eagle() { this.a = new Beak(); this.b = new Wing(); } public void fly() { System.out.println(KO_NAME + " 하늘을 날다."); } } | cs |
Owl.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public class Owl { private static final String KO_NAME = "올빼미"; private Beak a; private Wing b; public Owl() { this.a = new Beak(); this.b = new Wing(); } public void fly() { System.out.println(KO_NAME + " 하늘을 날다."); } } | cs |
Main.java
1 2 3 4 5 6 7 8 9 | public class Main { public static void main(String[] args) { Eagle e = new Eagle(); Owl o = new Owl(); e.fly(); o.fly(); } } | cs |
OUTPUT:
독수리 하늘을 날다.
올빼미 하늘을 날다.
살펴보면 Eangle.java 와 Owl.java에 코드에 중복이 많이 보입니다. 그래서 Bird 라는 추상 클래스를 만들어 상속해 보겠습니다.
Bird.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public abstract class Bird { protected final String koName; private Beak a; private Wing b; public Bird(Beak a, Wing b, String koName) { this.a = a; this.b = b; this.koName = koName; } public void fly() { System.out.println(koName + " 하늘을 날다."); } } | cs |
Eagle.java
1 2 3 4 5 6 | public class Eagle extends Bird { public Eagle(String koName) { super(new Beak(), new Wing(), koName); } } | cs |
Owl.java
1 2 3 4 5 | public class Owl extends Bird { public Owl(String koName) { super(new Beak(), new Wing(), koName); } } | cs |
Main.java
1 2 3 4 5 6 7 | public static void main(String[] args) { Bird e = new Eagle("독수리"); Bird o = new Owl("올뺴미"); e.fly(); o.fly(); } | cs |
Bird.java 에 중복되는 코드를 옮기고 Eagle.java, Owl.java 에 코드가 간결해 졌습니다. 추가로 어떠한 새가 추가 되어도 IS-A 가 성립한다면 Bird 클래스를 상속 받으면 됩니다.
제비 is Bird
까마귀 is Bird
제비와 까마귀 클래스를 추가하는일도 쉬워 졌습니다.
그런데 만약에 펭귄을 추가 한다면 어떻게 될까요?
Peguin.java
1 2 3 4 5 | public class Peguin extends Bird { public Peguin(String koName) { super(new Beak(), new Wing(), koName); } } | cs |
Main.java
1 2 3 4 5 6 | public class Main { public static void main(String[] args) { Bird p = new Peguin("팽귄"); p.fly(); } } | cs |
OUTPUT:
펭귄 하늘을 날다.
분명 펭귄은 부리와 날개를 가지고 있는 새입니다.
펭귄은 is Bird 도 성립됩니다.
하지만 펭귄이 하늘을 날 수 있을까요? 독수리와 올빼미는 하늘을 날 수 있는 능력이 있습니다. 날개를 펴고 접는 방법은 서로 다르겠지만
어쨌든 하늘을 날수 있습니다. 하지만 펭귄은 날수 없죠 결국 이런 부분이 인터페이스 대상이 되게 됩니다. 다시 코드를 변경해 보겠습니다.
FlyBehavior.java
1 2 3 | public interface FlyBehavior { void fly(); } | cs |
SwimBehavior.java
1 2 3 4 | public interface SwimBehavior { void swim(); } | cs |
Peguin.java
1 2 3 4 5 6 7 8 9 10 | public class Peguin extends Bird implements SwimBehavior { public Peguin(String koName) { super(new Beak(), new Wing(), "펭귄"); } @Override public void swim() { System.out.println(super.koName + " 수영을 하다."); } } | cs |
Eagle.java
1 2 3 4 5 6 7 8 9 10 11 | public class Eagle extends Bird implements FlyBehavior { public Eagle(String koName) { super(new Beak(), new Wing(), koName); } @Override public void fly() { System.out.println(koName + " 하늘을 날다."); } } | cs |
Main.java
1 2 3 4 5 6 7 8 9 10 11 | public class Main { public static void main(String[] args) { SwimBehavior p = new Peguin("팽귄"); FlyBehavior f = new Eagle("독수리"); p.swim(); f.fly(); } } | cs |
OUTPUT:
펭귄 수영을 하다.
결국 추상 클래스는 변경되지 않을 유전적(?) 형태에 대한 클래스들의 코드의 중복제거와 재사용에 이용되고
-> 독수리,펭귄,오리,참새 모든 새는 부리와 날개를 가지고 있다.
인터페이스는 객체의 행위에 대한 메세지 수신여부에 사용하면 됩니다.
-> 독수리와, 참새와 같은 객체들은 "하늘늘 날아라" 라는 메세지를 받고 처리 할 수 있지만
펭귄은 해당 메세지를 받을 수 없다.
'Java' 카테고리의 다른 글
Run vs Start in Java Thread (0) | 2020.05.22 |
---|---|
[자바] 접근 제어자(Access modifier) (0) | 2018.03.19 |
[자바]static(클래스) 메소드는 언제 쓰나 (0) | 2018.03.07 |
[자바] Guava 를 이용한 코드 작성 (0) | 2018.02.21 |
[자바]List 에서 previous 값 접근하기 (0) | 2018.02.13 |
- Total
- Today
- Yesterday