인터페이스란
인터페이스는 본질적으로 클래스를 구현하는 약속이나 법칙이다.
class Animal {
String name;
void setName(String name) {
this.name = name;
}
}
class Tiger extends Animal {
}
class Lion extends Animal {
}
class ZooKeeper {
void feed(Tiger tiger) {
System.out.println("feed apple");
}
void feed(Lion lion) {
System.out.println("feed banana");
}
}
public class Sample {
public static void main(String[] args) {
ZooKeeper zooKeeper = new ZooKeeper();
Tiger tiger = new Tiger();
Lion lion = new Lion();
zookeeper.feed(tiger);
zookeeper.feed(lion);
}
}
위는 사육사(ZooKeeper)가 호랑이와 사자에게 먹이를 주는 프로그램을 클래스만으로 구현한 코드다. 하지만 사육사가 관리하는 동물이 늘어날 수록 동물 클래스와 feed 메소드를 새로 만들어야 한다는 불편함이 있다.
class Animal {
String name;
String food; // Animal 클래스의 역할이 많아진다.
String getFood() {
return this.food;
}
void setName(String name) {
this.name = name;
}
}
class Tiger extends Animal {
public void Tiger() {
this.name = "tiger";
this.food = "apple";
}
}
class Lion extends Animal {
public void Lion() {
this.name = "lion";
this.food = "banana";
}
}
class ZooKeeper {
void feed(Animal animal) {
System.out.println("feed "+animal.getFood());
}
}
public class Sample {
public static void main(String[] args) {
ZooKeeper zooKeeper = new ZooKeeper();
Animal tiger = new Tiger();
Animal lion = new Lion();
zookeeper.feed(tiger);
zookeeper.feed(lion);
}
}
부모 클래스인 Animal에 getFood 메소드를 추가한다면 개별 동물 클래스를 구현할 때만 그에 맞는 food를 설정해주면 된다. 그러면 ZooKeeper에서는 feed 메소드 하나로 먹이 주는 프로그램을 관히라 수 있다. 하지만 Animal 클래스의 역할이 두 가지다. 동물의 이름과 먹이를 모두 관리하고 있다. 만약 동물 먹이가 육식동물에게만 필요할 경우 이를 처리하기 위해 꽤나 복잡한 과정을 거쳐야 할 것이다.
interface Predator {
String getFood(); // interface의 메소드는 세부 내용을 작성하지 않는다.
}
class Animal {
String name;
void setName(String name) {
this.name = name;
}
}
class Tiger extends Animal implements Predator {
public String getFood() { // interface 메소드는 항상 public으로 구현해야 한다.
return "apple";
}
}
class Lion extends Animal implements Predator {
public String getFood() {
return "banana";
}
}
class ZooKeeper {
void feed(Predator predator) {
System.out.println("feed "+predator.getFood());
}
}
public class Sample {
public static void main(String[] args) {
ZooKeeper zooKeeper = new ZooKeeper();
Predator tiger = new Tiger();
Predator lion = new Lion();
zookeeper.feed(tiger);
zookeeper.feed(lion);
}
}
인터페이스를 새로 구현하면 문제가 해결된다. Predator 인터페이스를 통해 육식동물은 개별 동물 클래스에서 먹이를 관리하도록 한다. Predator 인터페이스는 getFood 메소드를 갖는다. interface 메소드는 Predator를 구현한 클래스에서 interface 메소드를 꼭 작성하도록 강제하는 역할을 하기 때문에 {} 안에 세부 내용을 작성하지 않는다.
Tiger와 Lion은 implements 키워드를 통해 Predator 인터페이스를 구현한다. 그러면 Tiger와 Lion은 Predator의 객체로 생성할 수 있다. 따라서 ZooKeeper의 Predator 객체를 입력으로 받는 feed 메소드 하나만 구현해도 된다.
USB 포트는 전세계적으로 하나의 규격으로 표준화되어 있다. 인터페이스도 이와 같아서 클래스의 규격 표준을 세우는 역할을 한다. 그래서 Predator 인터페이스를 구현하는 동물 클래스는 어떤 동물인지 상관없이 ZooKeeper에서 하나의 명령으로 사용할 수 있는 것이다.
디폴트 메서드
자바8 버전 이후로 인터페이스에 디퐅르 메서드(default method)를 사용할 수 있다. 기존에 인터페이스 메서드와 달리 디폴트 메서드는 몸통(세부 내용, 구현체)을 가질 수 있다.
interface Predator {
String getFood();
default void printFood() {
System.out.printf("my food is %s\n", getFood());
}
}
클래스를 상속받는 것처럼 Predator 인터페이스를 구현하는 클래스는 printFood 메서드를 구현하지 않아도 사용할 수 있다. 또한 오버라이딩이 가능하다.
스태틱 메서드
자바8 버전 이후로 인터페이스에 스태틱 메서드(static method)를 사용할 수 있다. 인터페이스에 구현한 스태틱 메서드는 클래스의 스태틱 메서드처럼 인터페이스명.스태틱메서드명 형태로 사용할 수 있다.
interface Predator {
String getFood();
default void printFood() {
System.out.printf("my food is %s\n", getFood());
}
int LEG_COUNT = 4; // 인터페이스 상수
static int speed() {
return LEG_COUNT * 30;
}
}
Predator.speed();
인터페이스 상수
위 코드에서 int LEG_COUNT는 인터페이스 상수다. 인터페이스 상수는 자동으로 public static final이 적용되며, 다른 형태의 상수는 정의할 수 없다.
'Language > Java 문법' 카테고리의 다른 글
점프 투 자바 2. 클래스 생성자 (0) | 2022.11.04 |
---|---|
점프 투 자바 1. 자바의 특징 (0) | 2022.11.04 |