공부하게 된 배경
디자인 패턴 및 클린 코드를 공부할 때 가장 많이 나오는 개념이 바로 ‘추상화’가 아닐까 싶다.
따라서 추상화의 핵심인 추상클래스와 인터페이스의 개념을 확실히 하고자 이 글을 포스팅한다.
다루는 내용
- 추상클래스
- 인터페이스
- 추상메소드
추상클래스와 인터페이스
추상 클래스 vs 인터페이스 정리 표
항목 | 추상 클래스 | 인터페이스 |
---|---|---|
사용 키워드 | abstract | interface |
사용 가능 변수 | 제한 없음 | static final (상수) |
사용 가능 접근 제어자 | 제한 없음 (public, private, protected, default) | public |
사용 가능 메소드 | 제한 없음 | abstract method, default method, static method, private method |
상속 키워드 | extends | implements |
다중 상속 가능 여부 | 불가능 | 가능 (클래스에 다중 구현, 인터페이스 끼리 다중 상속) |
존재 목적 | 상속 받아서 기능 확장 목적 | 구현 객체의 동일한 실행 기능 보장 목적 |
공통점
- 추상 메소드를 가지고 있어야 한다.
- 인스턴스화 할 수 없다 (new 생성자 사용 X)
- 인터페이스 혹은 추상 클래스를 상속받아 구현한 구현체의 인스턴스를 사용해야 한다.
- 인터페이스와 추상클래스를 구현, 상속한 클래스는 추상 메소드를 반드시 구현하여야 한다.
이 두 가지는 하는 일은 비슷하지만, 개념의 차이는 명확하다.
추상 클래스와 인터페이스는
상속받는 클래스 혹은 구현하는 인터페이스 안에 있는 추상 메소드를 구현하도록 강제한다.
그렇다면 추상 클래스 안에 추상 메소드를 여러 개 두거나 혹은 전부 추상 메소드만 두면 될텐데, 왜 인터페이스가 존재하는 것일까?
이 두가지의 존재 목적을 알아보자.
추상 클래스
는 그 추상 클래스를 상속받아서 기능을 이용하고, 확장시키는 데 있다.
반면,인터페이스
는 함수의 껍데기만 있는데, 그 이유는 그 함수의 구현을 강제하기 위해서이다.
(인터페이스에서 구현을 강제하는 이유는, 구현 객체의 같은 동작을 보장하기 위해서이다.)
이렇게 애매하지만 명확하게 다른 존재 이유가 있다.
하지만 서로 상호 보완적인 측면도 존재하는데, 이는 java가 다중 상속을 지원하지 않는 부분에서 나타난다.
다중 상속
다중 상속은 무엇이고, 왜 지원하지 않을까?
다중 상속 : 여러 개의 슈퍼 클래스를 두는 것
1 2 3 4 5 6
class MyVehicle extends car, plane { @Override public void goTo(){ super.drive(); } }
위와 같은 코드에서 car, plane 클래스 모두 drive() 라는 메소드를 가지고 있다면, 어떤 메소드가 실행될까?
이것이 바로 다중 상속의 모호성이다. 따라서 자바는 과감하게 다중 상속을 못하도록 해버렸다.
이와는 다르게 인터페이스는 아래와 같이 여러 개의 인터페이스를 구현할 수 있다.
1
2
3
4
5
6
class car implements vehicle,engine{
@Override
public void drive(){
@doSomething
}
}
마치 여러 개를 상속받는 것처럼 보인다. 그래서 추상 클래스의 상속과 헷갈린다.
이렇게 외관상 헷갈리게 생긴 것 말고도, 둘 다 추상 메소드를 가지고 있다는 점 때문에 인터페이스가 다중 상속의 문제점을 해결하기 위해 존재한다는 오해를 사기도 한다.
이 오해를 풀기 위해서는 다시 한번 두 개념의 존재 이유를 되새겨 볼 필요가 있다.
상속은 슈퍼클래스의 기능을 이용하거나 확장하기 위해서 사용되고, 다중 상속의 모호성 때문에 하나만 상속받을 수 있다.
반면 인터페이스는 해당 인터페이스를 구현한 객체들에 대해서 동일한 동작을 약속하기 위해 존재한다.
만약 도형을 그리는 프로그램을 만든다고 치자.
삼각형을 만들 때도 그려야하고, 사각형을 만들 때도 그려야하니 draw() 메소드가 필요하겠지만, 도형종류가 다르니 그리는 방식도 다를 것이다.
하지만 도형이 무슨 모양인지 결정이 안되어있는 상황이니 draw() 메소드를 결정할 수 없다.
이러한 상황이 추상화의 대표적인 예라고 생각할 수 있다.
추상메소드로 선언만 해놓고 안의 내용은 상황에 맞게 자식 클래스에서 알아서 다시 구현해 쓰라고 선언만 해놓고, 구현을 기다리는 방식(오버라이드로 재정의해서 사용)이 바로 추상 클래스와 인터페이스다.
여기서 추상메소드만 선언할꺼면 인터페이스를, 다른 일반 메소드나 필드도 필요하면 추상클래스를 쓰면 된다.
어렵게 생각하지말고, 인터페이스는 한눈에 보면 다 빈껍데기 뿐이기 때문에 어떤 것을 구현해야되는지 한눈에 몽땅 들어오므로 쓰는 빈도가 높고, 추상클래스는 필요에 의해서 일반 메소드와 더불어서 추상화 기능을 가미할 때 쓴다는 정도로만 이해하면 될듯 싶다.
추상클래스와 인터페이스의 상속 방식
우선 둘다 같은 추상메소드를 넘겨받아 안의 내용을 채우는 것이므로 당연히 오버라이딩 해야한다.
하지만 abstract class는 상속처럼 extends를 쓴다.
1
2
3
4
5
class Triangle extends Shape{
void draw(){ // 일반 메소드 형태로 구현
System.out.println("삼각형을 그린다.");
}
}
interface는 implements로 상속을 받는다.
1
2
3
4
5
6
7
8
class Triangle implements Shape{
public void draw(){ // 일반 메소드 형태로 전부 구현
System.out.println("삼각형을 그린다.");
}
public void move(int y){ // 접근 지정자를 완화시켜 public으로 구현해준다.
System.out.println("삼각형을 이동시킨다.");
}
}
추상클래스든 인터페이스든 상속 받기로 했으면, 안에 있는 추상메소드는 모두 구현해주어야한다.
위에서 이야기 했듯이, 추상클래스는 인터페이스와 달리 클래스이므로 extends로 상속하고, 다중상속이 안되지만, 추상클래스가 추상클래스를 상속하는 것은 가능하다.
이 경우에는 자식클래스도 추상클래스이므로, 상속받았다 할지라도 추상메소드를 꼭 구현할 필요는 없고 다른 추상메소드를 만들어도 상관없다.
1
abstract class Triangle extends Shape //추상 클래스가 추상클래스 상속
인터페이스는 implements라는 키워드를 사용해서 상속하고, 다중 상속이 가능하다.
1
class Triangle implements **Shape, 인터페이스, 인터페이스 //인터페이스 다중 상속**
마지막으로 추상클래스와 인터페이스를 어떤 상황에서 쓰는지 다시 한번 구별하자면,
같은 종류나 행동들을 구현하고, 상속에 대한 계층 구조를 명확히 표현할 때 추상클래스를 이용한다.
추상클래스는 일반 변수들과 일반 메소드들도 쓸 수 있고, 아직 구현하지 않아도될 메소드는 그냥 내버려둘 수 있어 상황에 따라 편리하다.
인터페이스는 디자인을 구성하는 요소들이 자주 바뀔 때 쓰면 유용하고, 메소드 형태만 서로 공유해서 구현하는 상황일 때 적합하다.
: Reference
자바의 추상 클래스와 인터페이스
자바의 추상클래스(abstract class)와 인터페이스(interface)
추상 클래스와 인터페이스 상세 설명
클래스는 크게 일반 클래스와 추상 클래스로 나뉜다.
추상클래스는 클래스 내 추상 메소드
가 하나 이상 포함되거나 abstract로 정의된 경우를 말한다.
추상 메소드란 무엇일까? 추상메소드란 안이 아직 구현되어 있지 않은
abstract
로 정의된 메소드를 말한다.
1
2
3
4
5
6
클래스 안의 메소드 중 단 한개라도 추상메소드가 있다면, 그 클래스 앞에는 반드시 `abstract` 클래스명으로 표기되어야 하며, `abstract`와 `final` 키워드를 동시에 표기할 수 없다. (추상 클래스로 설정)
또한, 추상클래스에서는 일반변수들을 가질 수 있고, 생성자를 가질 수 있다.
반면 인터페이스는 final을 붙일 수 없고, 인터페이스 변수들은 static이어야만 한다.(일반 변수 불가)
그리고 생성자 또한 가질 수 없다.
반면, 인터페이스는 모든 메소드가 추상 메소드인 경우를 말한다.