- 한 클래스가 여러 인터페이스를 구현
인터페이스는 구현 코드나 멤버 변수를 가지지 않기 때문에 여러 개를 동시에 구현할 수 있음.
두 인터페이스에 이름이 같은 메서드가 선언되었다고 해도 구현은 클래스에서 이루어지므로, 어떤 메서드를 호출해야 하는지 모호하지 않기 때문임. (* 상속은 한번에 여러 개 상속 받기 불가능)
예시
package first_project;
public interface Buy {
void buy();
}
package first_project;
public interface Sell {
void sell();
}
Customer 클래스는 Buy 인터페이스와 Sell 인터페이스를 모두 구현
package first_project;
public class Customer implements Buy, Sell{
@Override
public void sell() {
System.out.println("구매");
}
@Override
public void buy() {
System.out.println("판매");
}
}
Customer 클래스는 두 인터페이스를 모두 구현했기때문에 Buy형이자 Sell형이 됨.
package first_project;
public class CustomerTest {
public static void main(String[] args) {
Customer customer = new Customer();
Buy buyer = customer;
buyer.buy();
Sell seller = customer;
seller.sell();
if(seller instanceof Customer) {
Customer customer2 = (Customer) seller;
customer2.buy();
customer2.sell();
}
}
}
buyer는 Buy 인터페이스의 메서드만 호출 가능함
seller는 Sell 인터페이스의 메서드만 호출 가능함.
그런데 seller를 하위 클래스형인 Customer로 다시 형변환해주면(다운 캐스팅)
Buy 인터페이스와 Sell 인터페이스의 메서드를 모두 사용할 수 있게 됨
- 두 인터페이스의 디폴트 메서드가 중복되는 경우
Customer 클래스는 Buy, Sell 두 인터페이스를 구현하고 이때 Buy인터페이스와 Sell 인터페이스에 pay()라는 동일한 이름의 정적 메서드가 있다고 해보자. 정적 메서드는 인스턴스 생성과 관계없이 사용할 수 있기 때문에 이 각각을
Buy.pay()와 Sell.pay()로 특정하여 호출 할 수 있음. 따라서 이 경우엔 문제가 안생김.
그런데 디폴트 메서드의 경우에는?
디폴트 메서드는 인스턴스를 생성해야 호출할 수 있기 때문에 이름이 같은 디폴트 메서드가 있으면 문제가 생김.
예시
package first_project;
public interface Buy {
void buy();
default void order() {
System.out.println("구매 주문.");
}
}
package first_project;
public interface Sell {
void sell();
default void order() {
System.out.println("구매 주문.");
}
}
이러면 아래와 같이 오류가 생김
이때 메서드를 재정의 하면 오류를 제거할 수 있게 됨
package first_project;
public class Customer implements Buy, Sell{
@Override
public void sell() {
System.out.println("구매");
}
@Override
public void buy() {
System.out.println("판매");
}
@Override
public void order() { // order() 메서드를 재정의
System.out.println("고객 판매 주문");
}
}
그리고 다음을 실행하면 아래와 같은 결과를 얻음
package first_project;
public class CustomerTest {
public static void main(String[] args) {
Customer customer = new Customer();
Buy buyer = customer;
buyer.buy();
buyer.order(); // 재정의된 메서드 호출됨.
Sell seller = customer;
seller.sell();
seller.order(); // 재정의된 메서드 호출됨.
if(seller instanceof Customer) {
Customer customer2 = (Customer) seller;
customer2.buy();
customer2.sell();
}
customer.order(); // 재정의된 메서드 호출됨.
}
}
결과
판매
고객 판매 주문
구매
고객 판매 주문
판매
구매
고객 판매 주문
주의할 점은 buyer.order()를 호출하면 Buy에 구현한 디폴트 메서드가 아닌 Customer 클래스에서 재정의한 메서드가 호출된다는 사실. 이는 이전에 배웠던 상속에서 가상 메서드 원리와 동일함.
- 인터페이스 상속하기
인터페이스 간에도 상속이 가능
클래스의 경우 하나의 클래스만 상속받을 수 있지만, 인터페이스는 여러 개를 동시에 상속받을 수 있다.
한 인터페이스가 여러 인터페이스를 상속받으면, 상속받은 인터페이스는 상위 인터페이스에 선언한 추상 메서드를 모두 가지게 됨
예시
package first_project;
public interface X {
void x();
}
package first_project;
public interface Y {
void y();
}
package first_project;
public interface MyInterface extends X, Y {
void myMethod();
}
MyInterface를 상속받은 MyClass 클래스는 x(), y(), myMethod()를 모두 구현해야 함
package first_project;
public class MyClass implements MyInterface{
@Override
public void y() {
System.out.println("y()");
}
@Override
public void x() {
System.out.println("x()");
}
@Override
public void myMethod() {
System.out.println("myMethod()");
}
}
package first_project;
public class MyClassTest {
public static void main(String[] args) {
MyClass mClass = new MyClass();
X xClass = mClass; // X형으로 변환 시 X에서 선언한 메서드만 호출 가능
xClass.x();
Y yClass = mClass; // Y형으로 변환 시 Y에서 선언한 메서드만 호출 가능
yClass.y();
MyInterface iClass = mClass;
iClass.myMethod();
iClass.x();
iClass.y();
}
}
결과
x()
y()
myMethod()
x()
y()
- 인터페이스 구현과 클래스 상속 함께 쓰기
한 클래스에서 클래스 상속과 인터페이스 구현을 모두 할 수도 있음.
예시
package first_project;
import java.util.ArrayList;
public class Shelf {
protected ArrayList<String> shelf;
public Shelf() {
shelf = new ArrayList<String>();
}
public ArrayList<String> getShelf(){
return shelf;
}
public int getCount() {
return shelf.size();
}
}
package first_project;
public interface Queue {
void enQueue(String title); //배열의 맨 마지막에 추가
String deQueue(); //배열의 맨 처음 항목 반환
int getSize(); // 현재 Queue에 있는 개수 반환
}
BookShelf 클래스는 Shelf 클래스를 상속받고 Queue 인터페이스를 구현한다.
package first_project;
public class BookShelf extends Shelf implements Queue {
@Override
public void enQueue(String title) {
shelf.add(title);
}
@Override
public String deQueue() {
return shelf.remove(0);
}
@Override
public int getSize() {
return getCount();
}
}
package first_project;
public class BookShelfTest {
public static void main(String[] args) {
Queue shelfQueue = new BookShelf();
shelfQueue.enQueue("드래곤볼 1");
shelfQueue.enQueue("드래곤볼 2");
shelfQueue.enQueue("드래곤볼 3");
System.out.println(shelfQueue.deQueue());
System.out.println(shelfQueue.deQueue());
System.out.println(shelfQueue.deQueue());
}
}
결과
드래곤볼 1
드래곤볼 2
드래곤볼 3
댓글