Search

인터페이스

인터페이스 (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

참고 자료