티스토리 뷰

추상클래스 와 인터페이스는 언제 사용하면 될까요?


추상클래스, 인터페이스는 혼자서는 인스턴스화 될 수 없으면 자바에서 상속을 위해 사용할 수 있는 기법입니다.

그럼 언제 추상클래스를 사용하고 인터페이스를 사용할까요?

자바8+ 이상부터는 인터페이스의 큰 변화가 생겼지만  (java 8 interface change) 여기서는 논외로 두겠습니다.

우선 추상 클래스 와 인터페이스의 스펙상 차이점을 알아보겠습니다.


추상클래스

  • 단일 상속
  • 추상 메서드와 구현 메서드 두 가지 모두를 가질 수 있음
  • 접근제어자 public,private,protected 메서드,변수를 가질 수 있음


인터페이스

  • 다중 상속
  • 메서드 바디를 가질 수 없음
  • 접근제어자는 public 메서드 만 가능 (접근제어자 생략시 default 값이 public), 클래스(static) 변수, 상수(final) 만 가질 수 있음


간단하게 살펴보면 위와 같은 차이점이 있습니다.

그렇기 때문에 다중상속을 사용하기 위해서는 인터페이스를 사용하면 되고 public 외 접근제어자를 가지는 메서드 또는 메서드 구현하기 위해서는 추상 클래스를 사용하면 됩니다.


하지만 더 이야기하고 싶은건 OOP 관점에서 본 차이점입니다. 결국 상속(extends) 과 구현(implements)을 언제 사용하느냐 인가죠


클래스 상속(Extends)

  • IS - A
  • 연관 관계가 깊은 여러 클래스들의 코드 재사용 관점


인터페이스 구현(Implements)
  • 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:

펭귄 수영을 하다.

독수리 하늘을 날다.


결국 추상 클래스는 변경되지 않을 유전적(?) 형태에 대한 클래스들의 코드의 중복제거와 재사용에 이용되고

 -> 독수리,펭귄,오리,참새 모든 새는 부리와 날개를 가지고 있다.

인터페이스는 객체의 행위에 대한 메세지 수신여부에 사용하면 됩니다.

 -> 독수리와, 참새와 같은 객체들은 "하늘늘 날아라" 라는 메세지를 받고 처리 할 수 있지만

     펭귄은 해당 메세지를 받을 수 없다.


공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크