인터페이스 (Interface)
일종의 추상 클래스로,
추상 클래스보다 추상화 정도가 높아
추상 메소드와 상수만 멤버로 가질 수 있음. (일반 메소드, 멤버변수 X)
•
추상 클래스는 미완성 설계도, 인터페이스는 기본 설계도!
•
추상 클래스와 마찬가지로, 다른 클래스 작성에 도움을 줄 목적으로 사용
인터페이스의 작성
•
클래스 작성법과 같지만, 키워드로 interface 사용
interface 인터페이스이름 {
public static final 타입 상수이름 = 값;
public abstract 메소드이름(매개변수목록);
}
Java
•
제약 사항
1.
모든 멤버변수는 public static final (생략 가능, 컴파일러가 자동 추가)
2.
모든 메소드는 public abstract (생략 가능)
(JDK 1.8부터 static 메소드와 default 메소드는 예외)
interface PlayingCard {
public static final int SPADE = 4;
final int DIAMOND = 3; // public static final int DIAMOND = 3;
static int HEART = 2; // public static final int HEART = 2;
int CLOVER = 1; // public static final int CLOVER = 1;
public abstract String getCardNumber ();
String getCardKind(); // public abstract String getCardKind();
}
Java
인터페이스의 상속
•
인터페이스는 인터페이스로만 상속 가능
•
클래스와 달리 다중상속 가능
interface Movable {
// 지정된 위치로 이동하는 기능
void move(int x, int y);
}
interface Attackable {
// 지정된 대상을 공격하는 기능
void attack(Unit u);
}
interface Fightable extends Movable, Attackable { }
Java
인터페이스의 구현
•
추상 클래스처럼, 인터페이스 자체로는 인스턴스 생성 불가
•
인터페이스의 추상 메소드에 몸통을 만들어주는 클래스 작성 필요
•
extends 대신 implements 키워드를 사용하여 구현
class 클래스이름implements 인터페이스이름 {
// 인터페이스에 정의돈 추상 메소드를 구현해야 함
}
class Fighter implements Fightable {
public void move(int x, int y) { ... }
public void attack(Unit u) { ... }
}
Java
•
인터페이스의 메소드 중 일부만 구현하려면 abstract 키워드 붙이기
abstract class Fighter implements Fightable {
public void move(int x, int y) { ... }
}
Java
•
동시에 상속과 구현 가능
class Fighter extends Unit implements Fightable {
public void move(int x, int y) { ... }
public void attack(Unit u) { ... }
}
Java
FighterTest.java
인터페이스를 이용한 다중 상속
•
실제로 인터페이스로도 다중상속을 구현하는 경우는 거의 없다!
•
두 클래스로부터 상속을 받기 위해서,
하나는 상속으로, 하나는 클래스 내부에서 인스턴스를 생성하여 사용
public class Tv {
protected boolean power;
protected int channel;
protected int volume;
public void power() { power == !power; }
public void channelUp() { channel++; }
public void channelDown() { channel--; }
public void volumeUp() { volume++; }
public void volumeDown() { volume--; }
}
public class VCR {
protected int counter;
public void play() { }
public void stop() { }
public void reset() { counter = 0; }
public int getCounter() { return counter; }
public void setCounter(int c) { counter = c; }
}
// VCR 클래스에 정의된 메소드와 일치하는 추상메소드를 갖는 인터페이스
public interface IVCR {
public void play();
public void stop();
public void reset();
public int getCounter();
public void setCounter(int c);
}
// IVCR 인터페이스 구현 및 tv 클래스로부터 상속받는 TVCR 클래스 작성
// VCR 클래스 타입의 참조변수를 멤버변수로 선언하여 IVCR 인터페이스의 추상 메소드 구현
public class TVCR extends Tv implements IVCR { // VCR 클래스의 인스턴스를 이용해 손쉽게 다중상속 구현
// 인터페이스 작성없이 VCR 클래스를 TVCR 클래스에 포함시켜도 되긴 하나, 인터페이스를 이용하면 다형적 특성 이용 가능
VCR vcr = new VCR();
// 코드 작성 대신 VCR 인스턴스의 메소드 호출
public void play() { vcr.play(); }
public void stop() { vcr.stop(); }
public void reset() { vcr.reset(); }
public int getCounter() { return vcr.getCounter(); }
public int setCounter() { vcr.setCounter(c); }
}
Java
인터페이스를 이용한 다형성
•
인터페이스 타입의 참조변수로, 인터페이스로 구현한 클래스의 인스턴스 참조 가능
•
인터페이스 타입으로의 형변환도 가능
// 인터페이스 Fightable을 클래스 Fighter가 구현했을 때,
// Fighter 인터페이스를 Fightable 타입의 참조변수로 참조 가능
Fightable f = (Fightable) new Fighter();
// or
Fightable f = new Fighter();
// 인터페이스를 메소드의 매개변수 타입으로 사용 가능
void attack(Fightable f) {
...
}
// attck 호출 시, 매개변수로 Fightable 인터페이스를 구현한 클래스의 인스턴스를 넘겨줘야 함.
class Fighter extends Unit implements Fightable {
public void move(int x, int y) { }
public void attack(Fightable f) { }
{
// 메소드의 리턴타입으로 인터페이스의 타입 지정도 가능
// 리턴타입이 인터페이스라는 건,
// 메소드가 해당 인터페이스를 구현한 클래스의 인스턴스를 반환함을 의미
Fightable method() {{{
...
// return new Fighter();
Fighter f = new Fighter();
return f;
}
Java
•
리턴 타입이 인터페이스 → 메소드가 해당 인터페이스를 구현한 클래스의 인스턴스를 반환!
ParseTest.java
인터페이스의 장점
1. 개발시간 단축 가능
•
메소드 내용과 상관없이, 선언부만 알아도 메소드 호출 가능
•
동시에 다른 한 쪽에선 인터페이스를 구현하는 클래스를 작성하면,
인터페이스를 구현하는 클래스가 작성될 때까지 기다리지 않고도 동시 개발 진행 가능
2. 표준화 가능
•
기본 틀을 인터페이스로 작성하면, 일관되고 정형화된 프로그램 개발 가능
3. 서로 관계없는 클래스들에게 관계를 맺어줄 수 있음
•
서로 상속 관계에 있지도 않고, 같은 조상 클래스를 가지고 있지 않은 아무런 관계 없는 클래스들에게 하나의 인터페이스를 공통적으로 구현하도록 함으로써 관계를 맺어줄 수 있음.
4. 독립적인 프로그래밍 가능
•
인터페이스로 클래스의 선언과 구현을 분리시킬수 있기 떄문에, 실제 구현에 독립적인 프로그램 작성 가능
•
클래스와 클래스 간의 직접적인 관계를 인터페이스를 이용하여 간접적인 관계로 변경하면,
한 클래스의 변경이 관련된 다른 클래스에 영향을 미치지 않는 독립적인 프로그래밍 가능
RepairableTest.java
자손 클래스 중 특정 클래스들에게만 인터페이스를 사용하여 메소드를 추가해주는 경우
인터페이스의 이해
•
인스턴스 이해를 위한 필수 염두 사항
1.
클래스를 사용하는 쪽(User)과 클래스를 제공하는 쪽(Provider)이 있음.
2.
메소드를 사용(호출)하는 쪽(User)에서는 사용하려는 메소드(Provider)의 선언부만 알면 됨. (내용은 몰라도 됨)
•
직접적인 연결 관계는 한 쪽 클래스가 변경되면 다른 쪽도 영향 받는다는 단점 有
InterfaceTest.java
•
클래스를 직접 호출하지 않고,
인터페이스를 사용하여 다른 클래스에 접근하면 서로 전혀 영향을 받지 않도록 하는 것이 가능
interface I {
public abstract void methodB();
}
class B implements I {
public void methodB() {
System.out.println("methodB in B class");
}
}
// 인터페이스를 사용하여 B의 메소드를 간접적으로 사용
class A {
public void methodA(I i) {
i.methodB(); // methodB in B class
}
}
Java
InterfaceTest2.java
InterfaceTest3.java
디폴트 메소드와 static 메소드
•
JDK 1.8부터 인터페이스에 디폴트 메소드와 static 메소드 추가 가능 (원랜 추상 메소드만)
•
static 메소드는 인스턴스와 관계 없는 독립적인 메소드라 추가못할 이유는 없었지만,
규칙의 단순화를 위해 Java에선 허용되지 않았음.
◦
그 예로 java.util.Collection 인터페이스에 static 메소드들이 들어가지 못하고,
별도의 Collections라는 클래스에 들어가게 됐음.
•
인터페이스의 static 메소드 역시 접근 제어자는 항상 public (생략 가능)
디폴트 메소드
•
인터페이스에 메소드를 추가한다는 것?
→ 인터페이스를 구현한 기존의 클래스들이 새로 추가된 메소드들을 구현해야 하는 큰 이슈 발생
•
디폴트 메소드(Default Method)
◦
추상 메소드의 기본적인 구현을 제공하는 메소드
◦
추상 메소드가 아니기 떄문에
디폴트 메소드가 새로 추가되어도 해당 인터페이스를 구현한 클래스를 변경하지 않아도 됨.
•
키워드 default 사용, 몸통 { }이 있어야 하며, 접근 제어자는 public (생략 가능)
interface MyInterface {
void method();
void newMethod(); // 추상 메소드
}
interface MyInterface {
void method();
default void newMethod(); // 디폴트 메소드
}
Java
•
새로 추가된 디폴트 메소드가 기존 메소드 이름과 중복되어 충돌 발생시, 해결 규칙
1.
여러 인터페이스의 디폴트 메소드 간 충돌
•
인터페이스를 구현한 클래스에서 디폴트 메소드를 오버라이딩
2.
디폴트 메소드와 조상 클래스의 메소드 간 충돌
•
조상 클래스의 메소드가 상속되고, 디폴트 메소드는 무시
3.
그냥 필요한 쪽 메소드와 같은 내용으로 오버라이딩 하면 간단
DefaultMethodTest.java