Chapter 10 인터페이스

Ruinak·2021년 6월 4일
0

Java

목록 보기
10/15
post-thumbnail

1. 인터페이스란

1-1 구현 코드가 없는 인터페이스

  • 인터페이스(interface)는 클래스 혹은 프로그램이 제공하는 기능을 명시적으로 선언하는 역할을 합니다.
  • 인터페이스는 추상 메서드와 상수로만 이루어져 있습니다.
  • 구현된 코드가 없기 때문에 당연히 인터페이스로 인스턴스를 생성할 수 없습니다.

인터페이스 만들기

예제 10-1 Calc 인터페이스 만들기

public interface Calc {
	
	// 인터페이스에서 선언한 변수는 컴파일 과정에서 상수로 변환됨
	double PI = 3.14;
	int ERROR = -99999999;
	
	// 인터페이스에서 선언한 메서드는 컴파일 과정에서 추상 메서드로 변환됨
	int add(int num1, int num2);
	int substract(int num1, int num2);
	int times(int num1, int num2);
	int divide(int num1, int num2);
	int square(int num);
}
  • 계산기를 만들기 위한 코드입니다.
  • 인터페이스에서 선언한 메서드는 구현 코드가 없는 추상 메서드입니다.
  • 이 메서드들은 public abstract 예약어를 명시적으로 쓰지 않아도 컴파일 과정에서 자동으로 추상 메서드로 변환됩니다.
  • 인터페이스에서 선언한 변수는 모두 컴파일 과정에서 값이 변하지 않는 상수로 자동 변환됩니다.
  • public static final 예약어를 쓰지 않아도 무조건 상수로 인식하는 것입니다.

1-2 클래스에서 인터페이스 구현하기

  • 클래스 간 상속에서 상위 클래스에 구현한 기능을 하위 클래스에서 확장한다는 의미로 extends 예약어를 사용합니다.
  • 인터페이스에서는 인터페이스에 선언한 기능을 클래스가 구현한다는 의미로 implements 예약어를 사용합니다.

예제 10-2 인터페이스 구현하기

//추상 클래스
public abstract class Calculator implements Calc {	
	
	@Override
	public int add(int num1, int num2) {
		return num1 + num2;
	}
	
	@Override
	public int substract(int num1, int num2) {
		return num1 - num2;
	}
}
  • 추상 메서드 times( )와 divide( )를 구현하지 않았으므로 Calculator는 추상 클래스입니다.

1-3 클래스 완성하고 실행하기

  • Calculator 추상 클래스를 상속받아 CompleteCalc 클래스를 만듭니다.
  • 아직 구현하지 않은 times( )와 divide( ) 추상 메서드를 이 클래스에서 구현합니다.

예제 10-3 계산기 클래스 만들기

public class CompleteCalc extends Calculator {

	@Override
	public int times(int num1, int num2) {
		return num1 * num2;
	}

	@Override
	public int divide(int num1, int num2) {
		if (num2 != 0) {
			return num1 / num2;
		} else {
			// num2가 0, 즉 나누는 수가 0인 경우에 대해 오류 반환
			return Calc.ERROR;	
		}			
	}

	// CompleteCalc에서 추가로 구현한 메서드
	public void showInfo() {	
		System.out.println("Calc 인터페이스를 구현하였습니다.");
	}
}
  • 숫자를 0으로 나눌 수 없기 때문에 num2가 0이 아닐때만 나누기 연산을 진행합니다.

예제 10-4 CompleteCalc 클래스 실행하기

public class CalculatorTest {
	public static void main(String[] args) {
		int num1 = 10;
		int num2 = 5;
		
		// CompleteCalc 클래스 생성
		CompleteCalc calc = new CompleteCalc();	
		System.out.println(calc.add(num1, num2));
		System.out.println(calc.substract(num1, num2));
		System.out.println(calc.times(num1, num2));
		System.out.println(calc.divide(num1, num2));
		calc.showInfo();
		
	}	
}

  • CompleteCalc 클래스만 인스턴스를 생성할 수 있습니다.
  • Calculator 클래스는 추상 클래스이므로 인스턴스를 생성할 수 없습니다.
  • Calc 인터페이스는 추상 메서드만으로 선언되었기 때문에 인스턴스를 생성할 수 없습니다.

1-4 인터페이스 구현과 형 변환

  • Calculator 클래스는 인터페이스에서 선언한 추상 메서드 중 일부 메서드만 구현했으므로 추상 클래스입니다.
  • 이를 상속받은 CompleteCalc 클래스는 Calculator 클래스에서 구현하지 않은 나머지 추상 메서드를 모두 구현하고 showinfo( ) 메서드를 추가로 구현했습니다.
  • 상속 관계에서 하위 클래스는 상위 클래스 자료형으로 묵시적으로 형 변환할 수 있습니다.
  • 인터페이스도 마찬가지이므로, CompleteCalc 클래스는 상위 클래스인 Calculator형이면서, Calc 인터페이스를 구현하였으므로 Calc형이기도 합니다.
  • 따라서 별 다른 조치 없이 다음처럼 Calc형으로 선언한 변수에 대입할 수 있습니다.
  • newCalc가 사용할 수 있는 메서드 목록에 Calc에서 선언한 추상 메서드는 있지만, CompleteCalc 클래스에서 추가로 구현한 showInfo( ) 메서드는 없습니다.
  • Calc형으로 선언한 변수에서 사용할 수 있는 메서드는 Calc 인터페이스에서 선언한 메서드뿐입니다.

정리

  • 인터페이스를 구현한 클래스가 있을 때 그 클래스는 해당 인터페이스형으로 묵시적 형 변환이 이루어집니다.
  • 형 변환되었을 때 사용할 수 있는 메서드는 인터페이스에서 선언한 메서드뿐입니다.

2. 인터페이스와 다형성

2-1 인터페이스의 역할

  • 인터페이스는 클라이언트 프로그램에 어떤 메서드를 제공하는지 미리 알려주는 명세(specification) 또는 약속의 역할을 합니다.
  • 클라이언트는 프로그래밍에서는 서버와 대응되는 의미로 사용됩니다.
  • 서버는 기능을 제공하는 쪽, 클라이언트는 기능을 사용하는 쪽이라고 생각하면 됨

예시

  • Abc 인터페이스를 구현한 A 클래스가 있습니다.
  • 위 클래스를 사용하는 Z 프로그램이 있습니다.
  • Abc 인터페이스에는 구현할 추상 메서드가 모두 선언되어 있고, 어떤 매개변수가 사용되는지, 어떤 자료형 값이 반환되는지 선언되어 있습니다.
  • Z 프로그램에서는 A 클래스의 구현 코드 전체를 살펴보지 않고, Abc 인터페이스의 선언부만 봐도 이 A 클래스를 어떻게 사용할지 알 수 있는 것입니다.
  • 만약 Z 프로그램에서 Abc 인터페이스를 구현한 다른 클래스인 B를 사용하고 싶다면, 인터페이스 명세에 따라 A 클래스에서 B 클래스로 교체해서 사용할 수 있습니다.

정리

  • 인터페이스의 역할은 인퍼테이스를 구현한 클래스가 어떤 기능의 메서드를 제공하는지 명시하는 것입니다.
  • 클라이언트 프로그램은 인터페이스에서 약속한 명세대로 구현한 클래스를 생성해서 사용하면 됩니다.

2-2 인터페이스와 다형성

  • 인터페이스를 사용하면 다형성을 구현하여 확장성 있는 프로그램을 만들 수 있습니다.
  • 클라이언트 프로그램을 많이 수정하지 않고 기능을 추가하거나 다른 기능을 사용할 수 있습니다.

예제 10-5 Scheduler 인터페이스 정의하기

public interface Scheduler {
	
	// 다음 전화를 가져오는 기능
	public void getNextCall();		
	// 상담원에게 전화를 배분하는 기능
	public void sendCallToAgent();	
}
  • 상담원에게 전화 업무를 배분하는 기능을 구현하기 위해 Scheduler 인터페이스 생성합니다.
  • Scheduler 인터페이스에는 시나리오 1~3에서 모두 공통으로 사용하는 메서드를 선언합니다.
  • getNextCall( )은 다음 전화를 가져오는 기능을 담당합니다.
  • sendCallToAgent( )는 상담원에게 전화를 배분하는 기능을 담당합니다.

예제 10-6 순서대로 배분하기(시나리오1)

// 상담원 한 명씩 돌아가며 동일하게 상담 업무 배분
public class RoundRobin implements Scheduler{

	@Override
	public void getNextCall() {
		System.out.println("상담 전화를 순서대로 대기열에 가져옵니다.");
	}
	
	@Override
	public void sendCallToAgent() {
		System.out.println("다음 순서 상담원에게 배분합니다.");
	}
}
  • RoundRobin 클래스는 고객 센터에 걸려온 상담전화를 순서대로 가져와서 상담원에게 배분합니다.

예제 10-7 짧은 대기열 먼저 배분하기(시나리오2)

// 현재 상담 업무가 없거나 상담 대기가 가장 적은 상담원에게 배분
public class LeastJob implements Scheduler{

	@Override
	public void getNextCall() {
		System.out.println("상담 전화를 순서대로 대기열에서 가져옵니다.");
	}
	
	@Override
	public void sendCallToAgent() {
		System.out.println("현재 상담 업무가 없거나 가장 적은 상담원에게 할당합니다.");
	}
}
  • LeastJob 클래스는 고객 센터에 걸려 온 상담 전화를 순서대로 가져와서 상담 업무가 없거나 상담 대기가 가장 적은 상담원에게 배분합니다.

예제 10-8 우선순위에 따라 배분하기(시나리오3)

// 고객 등급이 높은 고객의 전화부터 대기열에서 가져와 업무 능력이 높은 상담원 우선 배분
public class PriorityAllocation implements Scheduler{

	@Override
	public void getNextCall() {
		System.out.println("고객 등급이 높은 고객의 전화를 먼저 가져옵니다.");
	}
	
	@Override
	public void sendCallToAgent() {
		System.out.println("업무 skill 값이 높은 상담원에게 우선적으로 배분합니다.");
	}
}
  • PriorityAllocation 클래스는 고객 센터에 걸려온 전화 중 고객 등급이 높은 고객의 전화를 먼저 가져와서 업무 능력이 가장 좋은 상담원에게 배분합니다.

예제 10-9 입력 문자에 따라 배분 정책 수행하기

public class SchedulerTest {
	// 문자를 입력받는 System.in.read()를 사용하려면 IOExeption에서 오류를 처리해야함
	public static void main(String[] args) throws IOException {
		System.out.println("전화 상담 할당 방식을 선택하세요.");
		System.out.println("R : 한명씩 차례로 할당");
		System.out.println("L : 쉬고 있꺼나 대기가 가장 적은 상담원에게 할당");
		System.out.println("P : 우선순위가 높은 고객 먼저 할당");
		
		// 할당 방식을 입력받아 ch 변수에 대입
		int ch = System.in.read();
		Scheduler scheduler = null;
		if(ch == 'R' || ch == 'r') {
			// 입력 받은 값이 R 또는 r이면 RoundRobin 클래스 생성
			scheduler = new RoundRobin();
		} else if(ch == 'L' || ch == 'l') {
			// 입력 받은 값이 L 또는 l이면 LeastJob 클래스 생성
			scheduler = new LeastJob();
		} else if(ch == 'P' || ch == 'p') {
			// 입력 받은 값이 P 또는 p이면 PriorityAllocation 클래스 생성
			scheduler = new PriorityAllocation();
		} else {
			System.out.println("지원되지 않는 기능입니다.");
			return;
		}
		
		scheduler.getNextCall();
		// 어떤 정책인가와 상관없이 인터페이스에 선언한 메서드 호출
		scheduler.sendCallToAgent();
	}
}

  • RoundRobin, LeastJob, PriorityAllocation 클래스로 생성한 인스턴스는 모두 Scheduler형 변수에 대입할 수 있습니다.
  • 사용할 인스턴스가 어떤 클래스로 생성되었는지와 상관없이 인터페이스에서 제공하는 메서드를 호출하면 됩니다.
  • 즉, 인터페이스를 이용해 다형성을 구현한 것입니다.

2-3 클라이언트가 클래스를 사용하는 방법

  • 추가로 만들어야 하는 배분 정책은 앞에서와 마찬가지로 scheduler 인터페이스를 구현하는 새클래스로 만들면 됩니다.
  • 어떤 클래스를 구현하건 클라이언트가 인터페이스를 구현한 클래스를 사용하는 방식은 아래 코드와 같습니다.

3. 인터페이스 요소 살펴보기

3-1 인터페이스 상수

  • 인터페이스는 추상 메서드로 이루어지므로 인스턴스를 생성할 수 없으며, 멤버 변수도 사용할 수 없습니다.
  • 인터페이스에 선언한 변수를 컴파일하면 상수로 변환됩니다.

3-2 디폴트 메서드와 정적 메서드

  • 디폴트 메서드는 인터페이스에서 구현 코드까지 작성한 메서드입니다.
  • 디폴트 메서드는 인터페이스를 구현한 클래스에 기본적으로 제공할 메서드인 것입니다.
  • 정적 메서드는 인스턴스 생성과 상관없이 사용할 수 있는 메서드입니다.
  • 디폴트 메서드나 정적 메서드를 추가했다고해서 인터페이스가 인스턴스를 생성할 수 있는 것은 아닙니다.

3-3 디폴트 메서드

  • 디폴트 메서드는 말 그대로 기본으로 제공되는 메서드입니다.
  • 디폴트 메서드는 인터페이스에서 구현하지만, 이후 인터페이스를 구현한 클래스가 생성되면 그 클래스에서 사용할 기본 기능입니다.
  • 디폴트 메서드를 선언할 때는 default 예약어를 사용합니다.

예제 10-10 Calc 인터페이스에 디폴트 메서드 구현하기

public interface Calc {
	
	......
	
	default void description() {
		System.out.println("정수 계산기를 구현합니다.");
	}
}
  • 디폴트 메서드는 일반 메서드와 똑같이 구현하면 되고, 메서드 자료형 앞에 default 예약어만 써주면 됩니다.

예제 10-11 디폴트 메서드 호출하기

public class CalculatorTest {
	public static void main(String[] args) {
		int num1 = 10;
		int num2 = 5;
		
		// CompleteCalc 클래스 생성
		
		...
		
		// 디폴트 메서드 호출
		calc.description();		
	}
}

  • 디폴트 메서드는 인터페이스에 이미 구현되어 있으므로 인터페이스를 구현한 추상 클래스 Calculator나 추상 클래스를 상속받은 CompleteCalc 클래스에서 코드를 구현할 필요가 없습니다.

디폴트 메서드 재정의하기

  • 이미 인터페이스에 구현되어 있는 디폴트 메서드가 새로 생성한 클래스에서 원하는 기능과 맞지 않는다면, 하위 클래스에서 디폴트 메서드를 재정의할 수 있습니다.
  • Calc 인터페이스를 구현하는 Calculator 클래스에서 재정의할 수도 있고, Calculator 클래스를 상속받은 CompleteCalc 클래스에서 재정의할 수도 있습니다.
  • CompleteCalc 클래스 파일의 소스 코드에서 오버라이드 메서드 탭을 열어서 재정의가 가능합니다.

예제 10-12 디폴트 메서드 재정의하기

public class CompleteCalc extends Calculator {

	...

	// 디폴트 메서드 description()을 CompleteCalc 클래스에서 원하는 기능으로 재정의
	@Override
	public void description() {				
		super.description();
	}
}
  • super.description( )은 인터페이스에 선언한 메서드를 의미합니다.
  • 위 코드를 사용하지 않을거라면 지우고 새코드를 작성하면 됩니다.

3-4 정적 메서드

  • 정적 메서드는 static 예약어를 사용하여 선언하며 클래스 생성과 무관하게 사용할 수 있습니다.
  • 정적 메서드를 사용할 때는 인터페이스 이름으로 직접 참조하여 사용합니다.

예제 10-13 정적 메서드 구현하기

public interface Calc {
	
	...
    
	static int total(int[] arr) {	// 인터페이스에 정적 메서드 total() 구현
		int total = 0;
		
		for(int i : arr) {
			total += i;
		}
		return total;
	}
}

예제 10-14 정적 메서드 호출하기

package study.interfaceex;

public class CalculatorTest {
	public static void main(String[] args) {
		
		...
		
		int[] arr = {1, 2, 3, 4, 5};
		// 정적 메서드 사용하기
		System.out.println(Calc.total(arr));
		
	}	
}

4. 인터페이스 활용하기

4-1 한 클래스가 여러 인터페이스를 구현하는 경우

  • 한 클래스가 여러 클래스를 상속받으면 메서드 호출이 모호해지는 문제가 발생할 수 있습니다.
  • 인터페이스는 한 클래스가 여러 인터페이스를 구현할 수 있습니다.

예제 10-15 인터페이스 Buy와 Sell

public interface Buy {
	void buy();
}
  • Buy 인터페이스에 추상 메서드 buy( )가 선언되어 있습니다.
public interface Sell {	
	void sell();
}
  • Sell 인터페이스에 추상 메서드 sell( )이 선언되어 있습니다.

예제 10-16 Customer 클래스에서 두 인터페이스를 구현

// Customer 클래스는 Buy와 Sell 인터페이스를 모두 구현함
public class Customer implements Buy, Sell{ 
	
	@Override
	public void sell() {
		System.out.println("구매하기");
	}
	@Override
	public void buy() {
		System.out.println("판매하기");
	}
}
  • 인터페이스는 구현 코드나 멤버 변수를 가지지 않기 때문에 여러 개를 동시에 구현할 수 있습니다.
  • 두 인터페이스에 이름이 같은 메서드가 선언되었다고 해도 구현은 클래스에서 이루어지므로, 어떤 메서드를 호출해야 하는지 모호하지 않습니다.

예제 10-17 CustomerTest로 확인하기

package study.interfaceex;

public class CustomerTest {
	public static void main(String[] args) {
		Customer customer = new Customer();
		
		// Customer 클래스 형인 customer를 Buy 인터페이스 형인 buyer에 대입하여 형 변환
		Buy buyer = customer;
		// buyer는 Buy 인터페이스의 메서드만 호출 가능
		buyer.buy();
		buyer.order();	
				
		// Customer 클래스 형인 customer를 Sell 인터페이스 형인 seller에 대입하여 형 변환
		Sell seller = customer;	
		// seller는 Sell 인터페이스의 메서드만 호출 가능
		seller.sell();	
		seller.order();	
		
		if(seller instanceof Customer) {
			// seller를 하위 클래스형인 Customer로 다시 형 변환
			Customer customer2 = (Customer)seller;
			customer2.buy();
			customer2.sell();
		}
	}	
}

  • Customer 클래스는 Buy형이자 Sell형이기도 합니다.
  • 상속 관계에서와 마찬가지로 원래의 인스턴스 자료형으로 다운 캐스팅하기 위해서는 instanceof를 사용하여 본래 인스턴스 자료형으로 안전하게 변환할 수 있습니다.
  • Buy buyer = customer;처럼 customer를 Buy 인터페이스형 변수에 대입하면 형 변환이 일어나 Buy 인터페이스에 선언한 메서드만 호출할 수 있습니다.
  • Sell seller = customer;처럼 customer를 Sell 인터페이스형 변수에 대입하면 형 변환이 일어나 Sell 인터페이스에 선언한 메서드만 호출할 수 있습니다.

4-2 두 인터페이스의 디폴트 메서드가 중복되는 경우

  • 정적 메서드는 인스턴스 생성과 상관없이 사용할 수 있습니다.
  • Customer 클래스가 Buy, Sell 두 인터페이스를 구현하고 Buy 인터페이스와 Sell 인터페이스에 똑같은 pay( ) 정적 메서드가 있다면 Buy.pay( )와 Sell.pay( )로 특정하여 호출할 수 있기 때문에 문제가 되지 않습니다.

예제 10-18 중복되는 정적 메서드

public interface Buy {

	void buy();

	static void pay() {

	}
}
public interface Sell {
	
	void sell();

	static void pay() {
		
	}
}
  • 디폴트 메서드는 인스턴스를 생성해야 호출할 수 있는 메서드이기 때문에, 이름이 같은 디폴트 메서드가 두 인터페이스에 있으면 문제가 됩니다.

예제 10-19 중복되는 디폴트 메서드

public interface Buy {

	...

	default void order() {
		System.out.println("구매 주문");
	}
}
public interface Sell {
	
	...
	
	default void order() {
		System.out.println("판매 주문");
	}
}

  • 디폴트 메서드가 중복되었으니 구현하는 Customer 클래스에서 재정의하라는 오류가 발생
public class Customer implements Buy, Sell { 
			 	
	...
	
	// 디폴트 메서드 order()를 Customer 클래스에서 재정의함
	@Override
	public void order() {						
		System.out.println("고객 판매 주문");
	}
}
  • Customer 클래스에서 디폴트 메서드를 재정의하면, Customer 클래스를 생성하여 사용할 때 재정의된 메서드가 호출됩니다.
public class CustomerTest {
	public static void main(String[] args) {
		
		...
        
		// 재정의된 메서드 호출됨
		buyer.order();	
				
		...
		// 재정의된 메서드 호출됨
		seller.order();	
		
		...
        
		// 재정의된 메서드 호출됨
		customer.order();
	}	
}

  • customer가 Buy형으로 변환되고 buyer.order( )를 호출하면 Buy에 구현한 디폴트 메서드가 아닌 Customer 클래스에 재정의한 메서드가 호출됩니다.
  • 자바 가상 메서드 원리와 동일합니다.

4-3 인터페이스 상속하기

  • 인터페이스 간에도 상속이 가능합니다.
  • 인터페이스 간 상속은 구현 코드를 통해 기능을 상속하는 것이 아니므로 형 상속(type inheritance)이라고 부릅니다.
  • 클래스의 경우에는 하나의 클래스만 상속 받을 수 있지만, 인터페이스는 여러 개를 동시에 상속받을 수 있습니다.
  • 한 인터페이스가 여러 인터페이스를 상속받으면, 상속받은 인터페이스는 상위 인터페이스에 선언한 추상 메서드를 모두 가지게 됩니다.

예제 10-20 인터페이스 상속하기

  • MyInterface 인터페이스는 X와 Y 인터페이스를 상속받고, MyClass 클래스는 MyInterface 인터페이스를 실제로 사용할 수 있도록 구현합니다.
  • Myinterface 인터페이스는 두 인터페이스를 상속받고 자신이 추상 메서드를 1개 가지고 있으므로 상속받은 후 추상 메서드를 총 3개 가지게 됩니다.
  • MyClass 클래스가 구현해야 할 추상 메서드 개수는 총 3개입니다.
public interface X {
	void x();
}
  • X 인터페이스에 추상 메서드 x( ) 선언
public interface Y {
	void y();
}
  • Y 인터페이스에 추상 메서드 y( ) 선언
//인터페이스 여러 개를 상속받을 수 있음
public interface MyInterface extends X, Y {	
	void myMethod();
}
  • 인터페이스간 상속에서도 클래스를 상속할 때 사용하는 extends 예약어를 그대로 사용합니다.
public class MyClass implements MyInterface{

	// X 인터페이스에서 상속받은 x() 메서드 구현
	@Override
	public void x() {						
		System.out.println("x( )");	
	}

	// Y 인터페이스에서 상속받은 y() 메서드 구현
	@Override
	public void y() {						
		System.out.println("y( )");
	}

	// MyInterface 인터페이스의 myMethod() 메서드 구현
	@Override
	public void myMethod() {				
		System.out.println("myMethod( )"); 
	}
}
  • MyClass 클래스에서는 x( ), y( ) 메서드도 구현해야 합니다.

예제 10-21 상속받은 인터페이스 테스트

public class MyClassTest {
	public static void main(String[] args) {
		MyClass mClass = new MyClass();
		
		// 상위 인터페이스 X형으로 대입하면 X에 선언한 메서드만 호출 가능
		X xClass = mClass;				
		xClass.x();	
		
		// 상위 인터페이스 Y형으로 대입하면Y에 선언한 메서드만 호출 가능
		Y yClass = mClass;				
		yClass.y();	 
		
		// 구현한 인터페이스형 변수에 대입하면 인터페이스가 상속한 모든 메서드 호출 가능
		MyInterface iClass = mClass;	
		iClass.myMethod();
		iClass.x();
		iClass.y();
	}	
}
  • 생성한 클래스는 상위 인터페이스형으로 변환할 수 있습니다.
  • 상위 인터페이스로 형 변환을 하면 상위 인터페이스에 선언한 메서드만 호출할 수 있습니다.
  • 인터페이스를 정의할 때 기능상 계층 구조가 필요한 경우에 상속을 사용하기도 합니다.

4-4 인터페이스 구현과 클래스 상속 함께 쓰기

  • 한 클래스에서 클래스 상속과 인터페이스 구현을 모두 할 수도 있습니다.
  • BookShelf(책장) 클래스는 책을 넣은 대로 순서대로 꺼내어 볼 수 있도록 만듭니다.
  • Queue(큐) 자료 구조는 처음 들어간 자료부터 꺼내어 쓰는 구조를 말합니다.

예제 10-22 Shelf 클래스 만들기

public class Shelf {
	// 자료를 순서대로 저장할 ArrayList 선언
	protected ArrayList<String> shelf;		
	
	// 디폴트 생성자로 Shelf 클래스를 생성하면 ArrayList도 생성됨
	public Shelf() {
		shelf = new ArrayList<String>();
	}
	
	public ArrayList<String> getShelf(){
		return shelf;
	}
	
	public int getCount() {
		return shelf.size();
	}	
}
  • 자료를 순서대로 저장할 배열 객체 shelf 선언했습니다.
  • 이름을 저장할 수 있도록 자료형은 String을 사용합니다.
  • getShelf( ) 메서드는 저장되어 있는 배열 shelf를 반환하고, getCount( ) 메서드는 배열 shelf에 저장된 요소 개수를 반환합니다.

예제 10-23 Queue 인터페이스 정의하기

public interface Queue {
	
	// 배열의 맨 마지막에 추가
	void enQueue(String title);	
	// 배열의 맨 처음 항목 반환
	String deQueue();
	// 현재 Queue에 있는 개수 반환
	int getSize();				
}
  • Queue 인터페이스는 먼저 들어온 자료를 먼저 꺼내는 기능을 정의합니다.
  • enQueue( ) 메서드는 입력되는 요소 값을 배열의 맨 뒤에 추가합니다.
  • deQueue( ) 메서드는 배열에서 맨 앞에 있는 요소를 제거하고 그 값을 반환합니다.

예제 10-24 BookShelf 클래스 구현하기

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();
	}
}
  • BookShelf 클래스는 Shelf 클래스를 상속받고 Queue 인터페이스를 구현합니다.
  • Shelf 클래스가 가지고 있는 ArrayList 배열을 사용하여 Queue 인터페이스에서 선언한 메서드를 모두 구현합니다.

예제 10-25 BookShelf 테스트하기

package study.bookshelf;

public class BookShelfTest {
	public static void main(String[] args) {
		Queue shelfQueue = new BookShelf();
		
		// 순서대로 요소를 추가
		shelfQueue.enQueue("다크메이지");			
		shelfQueue.enQueue("데이몬");
		shelfQueue.enQueue("마왕데이몬");
		
		// 입력 순서대로 요소를 꺼내서 출력
		System.out.println(shelfQueue.deQueue());	
		System.out.println(shelfQueue.deQueue());
		System.out.println(shelfQueue.deQueue());
	}
}

4-5 실무에서 인터페이스를 사용하는 경우

  • 인터페이스는 클래스가 제공할 기능을 선언하고 설계하는 것입니다.
  • 여러 클래스가 같은 메서드를 다르게 구현한다면, 우선 인터페이스에 메서드를 선언한 다음 인터페이스를 구현한 각 클래스에서 같은 메서드에 대해 다양한 기능을 구현하면 됩니다.
  • 이것이 바로 인터페이스를 이용한 다형성의 구현입니다.

profile
Nil Desperandum <절대 절망하지 마라>

0개의 댓글