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();
}
public interface Y {
void 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 실무에서 인터페이스를 사용하는 경우
- 인터페이스는 클래스가 제공할 기능을 선언하고 설계하는 것입니다.
- 여러 클래스가 같은 메서드를 다르게 구현한다면, 우선 인터페이스에 메서드를 선언한 다음 인터페이스를 구현한 각 클래스에서 같은 메서드에 대해 다양한 기능을 구현하면 됩니다.
이것이 바로 인터페이스를 이용한 다형성의 구현입니다.