본문 바로가기
Programming Language/Java

[자바] 인터페이스 - (3) 활용하기

by ggyongi 2021. 10. 26.
반응형

- 한 클래스가 여러 인터페이스를 구현

인터페이스는 구현 코드나 멤버 변수를 가지지 않기 때문에 여러 개를 동시에 구현할 수 있음.

두 인터페이스에 이름이 같은 메서드가 선언되었다고 해도 구현은 클래스에서 이루어지므로, 어떤 메서드를 호출해야 하는지 모호하지 않기 때문임. (* 상속은 한번에 여러 개 상속 받기 불가능)

 

예시

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
 

비전공자 네카라 신입 취업 노하우

시행착오 끝에 얻어낸 취업 노하우가 모두 담긴 전자책!

kmong.com

댓글