인터페이스(Interface)
-
인터페이스란 일종의 추상클래스인데, 추상클래스보다 추상화 정도가 높다. 추상클래스가 미완성 설계도라면 인터페이스는 기본 설계도이며, 다른 클래스 작성에 도움을 준다. 인터페이스에는 추상메서드와 상수만 올 수 있다. (일반 메서드와 상수가 아닌 변수는 올 수 없다)
- 모든 멤버변수는 public static final 이어야하며 생략 가능 - 모든 메서드는 public abstract 이어야하며 생략 가능
-
예시
class FighterTest { public static void main(String[] args) { Fighter f = new Fighter(); } } class Fighter extends Unit implements Fightable { public void move(int x, int y) { /* 내용 생략*/ } // 접근제어자가 public! public void attack(Unit u) { /* 내용 생략*/ } } class Unit { int currentHP; int x, y; } interface Fightable extends Movable, Attackable { } // 다중 상속 interface Movable { void move(int x, int y); } interface Attackable { void attack(Unit u); }
- 인터페이스는 인터페이스로부터만 상속받을 수 있다.
- 클래스와 달리 다중 상속이 가능하다.
- 위 예시에서 Fighter클래스에서 구현하는 메서드의 제어자가
public
인 이유는 Movable과 Attackable은 인터페이스이므로 메서드의 제어자가public abstract
이기 때문이다. 오버라이딩할 때는 조상의 메서드보다 넓은 범위의 접근 제어자를 지정해야 하므로 인터페이스의 메서드를 오버라이딩할 때에는public
만 사용할 수 있다.
-
인터페이스를 사용한 다형성
- 인터페이스 역시, 메서드의 매개변수 타입이 될 수도 있고 리턴타입이 될 수도 있다. 이는 매개변수나 리턴이 해당 인터페이스를 구현한 클래스의 인스턴스를 넣거나 반환한다는 의미이다.
-
예시
public class Main { public static void main(String[] args) { // GroundUnit Tank tank = new Tank(); Marine marine = new Marine(); SCV scv = new SCV(); // AirUnit Dropship dropship = new Dropship(); // Repairable한 기계유닛 scv.repair(tank); scv.repair(dropship); } } interface Repairable { } class Unit { int hitPoint; // 현재 HP final int MAX_HP; // 상수이지만 컨스트럭터를 통해 초기화되도록 한다. Unit(int hp) { MAX_HP = hp; } } class GroundUnit extends Unit { GroundUnit(int hp) { super(hp); } } class AirUnit extends Unit { AirUnit(int hp) { super(hp); } } class Tank extends GroundUnit implements Repairable{ Tank() { super(150); hitPoint = MAX_HP; } public String toString() { return "Tank"; } } class Marine extends GroundUnit { Marine() { super(50); hitPoint = MAX_HP; } } class Dropship extends AirUnit implements Repairable{ Dropship() { super(125); hitPoint = MAX_HP; } public String toString() { return "Dropship"; } } class SCV extends GroundUnit implements Repairable { SCV() { super(60); hitPoint = MAX_HP; } void repair(Repairable r) { if(r instanceof Unit) { Unit u = (Unit)r; while(u.hitPoint != u.MAX_HP) { u.hitPoint++; } } System.out.println(r + "의 수리가 끝났습니다."); } }
여기서 Unit별로 나누었을 때 GroundUnit과 AirUnit으로 나누는데, 문제는 scv가 수리할 수 있는 기계유닛만 인스턴스로 받는 repair메서드를 만들고 싶다는 것이다. 기계유닛은 그라운드유닛 및 에어유닛의 분류와는 다른 분류이다. 공통 조상인 Unit으로 받으면 받을 수는 있겠지만, 기계유닛이 아닌 다른 유닛들도 포함할 수 있게 되어 목적에 맞지 않다.
이 때에 Repairable
interface
를 사용하여 scv, dropship, tank에 관계를 지어준다. 그리고 repair메서드에 Repairable 타입을 매개변수로 선언하면, Repairable 인터페이스를 구현한(상속 받은) 클래스 인스턴스만 받아들여지게 된다.repair메서드에서 주목해야할 점은 Unit에 대해 인스턴스 체크를 한 다음 캐스팅을 한다는 것이다. 이는 Repairable 인터페이스에 아무런 멤버도 없기 때문에 이 타입의 참조변수로는 할 수 있는 일이 없기 때문이다. 이 때에 Unit형으로 캐스팅하여 MAX_HP, hitpoint 변수에 접근할 수 있게 한다. 이를 통해 hitPoint가 MAX_HP보다 낮을 때에 HP를 채우는 작업을 수행한다.