추상 클래스
- 자바에서는
abstract
키워드를 통해 추상 클래스를 선언할 수 있다.
abstract class Unit {
int x, y;
abstract void move(int x, int y); // 지정된 위치로 이동
void stop() {} // 현재 위치에 정지
}
class Marine extends Unit{
void move(int x, int y) {
System.out.println("걸어서 이동");
}
void stimPack() {} // 고유 능력 스팀팩 사용
}
class Tank extends Unit{
void move(int x, int y) {
System.out.println("굴러서 이동");
}
void siegeMode() {} // 고유 능력 시즈 모드 사용
}
class DropShip extends Unit{
void move(int x, int y) {
System.out.println("날아서 이동");
}
void loadUnload() {} // 고유 능력 탑승 사용
}
- 위 코드에서 Marine, Tank, DropShip은 Unit 추상 클래스를 상속받는다.
- 추상 클래스를 사용하여 얻는 이점은 다음이 있다.
- 공통 멤버 통합으로 인한 중복 제거
- Marine, Tank, Dropship의 좌표, 움직임/정지는 공통적인 기능으로 통합되어 코드의 중복이 사라졌다. → 하지만 이건 굳이 추상 클래스가 아닌 일반 클래스로도 가능하다.
- 구현의 강제성을 통한 기능 보장
- 새 유닛 클래스를 생성한다고 할 때, 다음과 같이 유닛 클래스의 정의부를 구현하지 않으면, 오류를 보여주어 바로 수정 가능하도록 한다.
- Battlecruiser가 move()를 구현하지 않으면 에러가 발생하여 개발자의 실수를 차단한다.
- 규격에 맞는 설계 구현
- 규격에 맞춰 개발하면 되므로 생산성이 증가한다.

인터페이스
- 추상 메서드의 집합이며, 동일한 목적 하에 동일한 기능을 수행하게끔 강제화 하는 역할을 한다.
- 다양한 클래스끼리 통신하는데 자주 사용된다.
- 결합도를 낮춰 유지보수성을 늘리는 디자인 패턴으로서의 역할도 병행한다.
- 기본 문법
- 인터페이스를 작성하는 것은 추상 클래스를 작성하는 것과 같다(추상 메서드의 집합).
- 인터페이스도 필드를 선언할 수 있지만, 변수가 아닌 상수(final)로서만 정의할 수 있다.
public static final
과public abstract
제어자는 생략이 가능하다(컴파일러가 자동으로 추가해 준다).
interface 인터페이스이름{
public static final 타입 상수이름 = 값;
public abstract 타입 메서드이름(매개변수목록);
}
// --------------------------------------------------------
interface TV {
int MAX_VOLUME = 10; // public static final 생략 가능
int MIN_VOLUME = 10;
void turnOn(); // public abstract 생략 가능
void turnOff();
void changeVolume(int volume);
void changeChannel(int channel);
}
- 인터페이스도 추상 클래스와 마찬가지로 그 자체로는 인스턴스를 생성할 수 없고, 구현부를 만들어주는 클래스에 구현(상속)되어야 한다.
- 해당 클래스에 인터페이스를 구현하고 싶으면,
implements
키워드를 쓴 후 인터페이스를 나열하면 된다.
- 인터페이스를 상속받은 자식 클래스는 인터페이스가 포함하고 있는 추상 메서드를 구현해준다.
- 인터페이스의 가장 큰 특징은 다중구현(다중상속)이 가능하다는 것이다.
interface Animal {
public abstract void cry();
}
interface Pet {
public abstract void play();
}
class Tail {
// ...
}
class Cat extends Tail implements Animal, Pet { // 클래스와 인터페이스를 동시에 상속
public void cry() {
System.out.println("냐옹냐옹!");
}
public void play() {
System.out.println("쥐 잡기 놀이하자~!");
}
}
interface Iflower {
int ex = 10; // 각각 public static final
}
interface IPlant extends Iflower {
int ex = 20; // 각각 public static final
}
class Tulip implements IPlant {
int ex = 30; // 그냥 인스턴스 변수
}
public class Main {
public static void main(String[] args) {
// 클래스 타입 객체로 ex 멤버에 접근하면, 클래스 인스턴스 변수로 접근
Tulip t = new Tulip();
System.out.println(t.ex); // 30
// 인터페이스 타입 객체로 멤버에 접근하면, 인터페이스 static 상수로 접근
Iflower a = new Tulip();
System.out.println(a.ex); // 10 - 좋지않은 방법
System.out.println(Iflower.ex); // 10 - 클래스 static 처럼 '인터페이스.멤버' 로 접근
IPlant b = new Tulip();
System.out.println(b.ex); // 20 - 좋지않은 방법
System.out.println(IPlant.ex); // 20 - 클래스 static 처럼 '인터페이스.멤버' 로 접근
}
}
인터페이스와 추상클래스의 비교

- 인터페이스와 추상 클래스는 둘이 똑같이 추상 메소드를 통해 상속/구현을 통한 메소드 강제 구현 규칙을 가지는 추상화 클래스이다.
- 하지만 각각의 고유 특징에 의해 각각 사용처가 갈리게 된다.
추상 클래스를 사용하는 경우
- 상속 받을 클래스들이 공통으로 가지는 메소드와 필드가 많아 중복 멤버 통합을 할 때
- 멤버에 public 이외의 접근자(protected, private) 선언이 필요한 경우
- 추상 클래스는 이를 상속할 각 객체들의 공통정을 찾아 추상화시켜 놓은 것으로, 상속관계를 타고 올라갔을대 같은 부모 클래스를 상속하며 부모 클래스가 가진 기능들을 구현해야 할 때 사용한다.
- 추상 클래스를 사용하는 경우 인터페이스를 사용할때보다 상속 객체들이 관계적으로 더 묶여있다고 볼 수 있다.
인터페이스를 사용하는 경우
- 애플리케이션의 기능을 정의해야 하지만, 그 구현 방식이나 대상에 대해 추상화 할 때
- 서로 관련성이 없는 클래스들을 묶어주고 싶을 때(형제 관계)
- 다중 상속(구현)을 통한 추상화 설계를 해야할 때
- 클래스와 별도로 구현 객체가 같은 동작을 한다는 것을 보장하기 위해 사용
- 인터페이스의 가장 큰 특징은 상속에 구해받지 않는 상속이 가능하다는 것이다.
- 자유로운 타입 묶음
- 관련이 적은 클래스도 형제 타입처럼 묶어버릴 수 있다.
추상클래스는 클라이언트에서 자료형을 사용하기 전에 미리 논리적인 클래스 상속 구조를 만들어 놓고 사용이 결정되는 느낌이라면
인터페이스는 반대로 먼저든 나중이든 그때 그때 필요에 따라 구현해서 자유롭게 붙였다 뗏다하는 느낌으로 보면 된다.
인터페이스 + 추상클래스 조합
- 추상 클래스의 중복 멤버 통합과 인터페이스의 다중 상속 기능을 동시에 사용할 때 자주 이용된다.
- 디자인 패턴의 근간이 되었다.
interface Animal {
void walk();
void run();
void breed();
}
// Animal 인터페이스를 일부만 구현하는 포유류 추상 클래스
abstract class Mammalia implements Animal {
public void walk() { ... }
public void run() { ... }
// breed() 메서드는 자식 클래스에서 구체적으로 구현하도록 일부로 구현하지 않음 (추상 메서드로 처리)
}
// 인터페이스 + 추상 클래스를 상속하여 사용
class Lion extends Mammalia {
@Override
public void breed() { ... }
}
인터페이스 + 추상클래스 + 구체클래스 조합
- 인터페이스에서의 필드는 final로, 상수만 선언할 수 있고, 중복되는 멤버에 대해 클래스와 같이 묶어주는 역할을 하지 못한다.
- 이런 문제점을 해결할 방법이, 인터페이스와 구체 클래스 중간에 추상 클래스를 두고, 공통되는 부분을 모아 두는 것이다.
// 인터페이스
interface IShape {
void setOpacity(double opacity); // 도형 투명도 지정
void setColor(String color); // 도형 색깔 지정
void draw(); // 도형 그리기
}
// 추상 클래스
abstract class Shape implements IShape {
// 중복되는 멤버들을 모아놓고
protected double opacity;
protected String color;
public void setOpacity(double opacity) {
this.opacity = opacity;
}
public void setColor(String color) {
this.color = color;
}
// void draw(); 는 구체화 안함
}
// 구체 클래스
class Rectangle extends Shape {
public void draw() { // 자식 클래스에서 draw() 구현
System.out.println("draw Rectangle with");
System.out.println(opacity);
System.out.println(color);
}
}
// 구체 클래스
class Square extends Shape {
public void draw() { // 자식 클래스에서 draw() 구현
System.out.println("draw Rectangle with");
System.out.println(opacity);
System.out.println(color);
}
}
= = Reference
Share article