인터페이스
인터페이스란?
-모든 메서드가 추상 메서드로 선언 됨 public abstract
-모든 변수는 상수로 선언 됨 public static final
interface 인터페이스이름
{
public static final float pi = 3.14F;
public void makeSth();
}
인터페이스 정의와 구현

10+0=10
10-0=10
10*0=0
10/0=-99999999
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);
}
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;
}
}
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 ERROR;
else
return num1 / num2;
}
public void showInfo()
{
System.out.println("모두 구현 함.");
}
}
public class CalculatorTest {
public static void main(String[] args) {
Calc calc = new CompleteCalc();
int num1 = 10;
int num2 = 0;
System.out.println(num1 + "+" + num2 + "=" + calc.add(num1, num2));
System.out.println(num1 + "-" + num2 + "=" + calc.substract(num1, num2));
System.out.println(num1 + "*" + num2 + "=" + calc.times(num1, num2));
System.out.println(num1 + "/" + num2 + "=" + calc.divide(num1, num2));
}
}
인터페이스 구현과 형 변환
-인터페이스를 구현한 클래스는 인터페이스 형으로 선언한 변수로 형 변환 할 수 있음
Calc calc = new CompleteCalc();
-상속에서의 형 변환과 동일한 의미
-클래스 상속과 달리 구현 코드가 없으므로 여러 인터페이스를 구현할 수 있음(eg. extends)
-형 변환되는 경우 엔터페이스에 선언된 메서드만 사용 가능 함
인터페이스 사용 이유
-클래스나 프로그램이 제공하는 기능을 명시적으로 선언
-클라이언트 프로그램은 인터페이스에 선언된 메서드 명세만 보고 이를 구현한 클래스를 사용할 수 있음
-어떤 객체가 하나의 인터페이스 타입이라는 것은 그 인터페이스가 제공하는 모든 메서드를 구현 했다는 의미임
-인터페이스를 구현한 다양한 객체를 사용함 - 다형성
여러 인터페이스 구현하기
-자바의 인터페이스는 구현 코드가 없으므로 하나의 클래스가 여러 인터페이스를 구현 할 수 있음.
-디폴트 메서드가 중복 되는 경우는 구현하는 클래스에서 재정의 해야 함
-여러 인터페이스를 구현한 클래스는 인터페이스 타입으로 형 변환 되는 경우 해당 인터페이스에 선언된 메서드면 사용 가능 함

customer buy
customer sell
Hello
customer buy
customer sell
public interface Buy {
void buy();
}
public interface Sell {
void sell();
}
public class Customer implements Buy, Sell {
@Override
public void sell()
{
System.out.println("customer sell");
}
@Override
public void buy()
{
System.out.println("customer buy");
}
public void sayHello()
{
System.out.println("Hello");
}
}
public class CustomerTest {
public static void main(String[] args) {
Customer customer = new Customer();
customer.buy();
customer.sell();
customer.sayHello();
Buy buyer = customer;
buyer.buy();
Sell seller = customer;
seller.sell();
}
}
디폴트 메서드가 중복되는 경우
디폴트 메서드가 두 개의 인터페이스에서 중복 되면 구현하는 클래스에서 재정의 해야 함
customer buy
customer sell
Hello
customer buy
customer sell
customer order
customer order
public interface Sell {
void sell();
default void order() // 중복 메서드
{
System.out.println("판매 주문");
}
}
public interface Buy {
void buy();
default void order() // 중복 메서드
{
System.out.println("구매 주문");
}
}
public class Customer implements Buy, Sell {
@Override
public void sell()
{
System.out.println("customer sell");
}
@Override
public void buy()
{
System.out.println("customer buy");
}
public void sayHello()
{
System.out.println("Hello");
}
@Override
public void order()
{
System.out.println("customer order"); // 중복 메서드 재정의
}
}
public class CustomerTest {
public static void main(String[] args) {
Customer customer = new Customer();
customer.buy();
customer.sell();
customer.sayHello();
Buy buyer = customer;
buyer.buy();
Sell seller = customer;
seller.sell();
buyer.order(); //**
seller.order(); //**
}
}
인터페이스의 상속
-인터페이스 사이에도 상속을 사용할 수 있음
- extends 키워드 사용
-인터페이스는 다중 상속이 가능하고 구현 코드의 상속이 아니므로 타입 상속이라고 함

public interface X {
void x();
}
public interface Y {
void y();
}
public interface MyInterface extends X, Y{
void myMethod();
}
public class MyClass implements MyInterface {
@Override
public void x() {
System.out.println("x()");
}
@Override
public void y() {
System.out.println("y()");
}
@Override
public void myMethod() {
System.out.println("myMethod()");
}
}
public class MyClassTest {
public static void main(String[] args) {
MyClass mClass = new MyClass();
X xClass = mClass; // X만 사용
xClass.x();
Y yClass = mClass; // Y만 사용
yClass.y();
MyClass iClass = mClass; // 모두 사용
iClass.x();
iClass.y();
iClass.myMethod();
}
}
클래스 상속과 인터페이스 구현 함께 쓰기
-실무에서 프레임워크나 오픈소스와 함께 연동되는 구현을 하면 클래스 상속과 인터페이스 구현을 같이 사용하는 경우가 많음

-책이 순서대로 대여가 되는 도서관 구현
-책을 보관하는 자료 구조가 Shelf에 구현 됨 - ArrayList
- Queue 인터페이스 구현 함
-Shelf 클래스를 상속 받고 Queue 구현 함
태백산맥1
태백산맥2
태백산맥3
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();
}
}
public interface Queue {
void enQueue(String title); // 들여보냄(책)
String deQueue(); // 꺼냄(반환)
int getSize();
}
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();
}
}
public class BookShelfTest {
public static void main(String[] args) {
Queue bookQueue = new BookShelf();
bookQueue.enQueue("태백산맥1");
bookQueue.enQueue("태백산맥2");
bookQueue.enQueue("태백산맥3");
System.out.println(bookQueue.deQueue());
System.out.println(bookQueue.deQueue());
System.out.println(bookQueue.deQueue());
}
}
복습 예제
추상 클래스와 템플릿 메서드
-Player가 있고 Player는 GameLevel 속성을 가진다. 각 GameLevel 단계마다 run(), jump(), turn() 세 가지 기능이 업그레이드 된다.
-초보자 레벨 : 천천히 달린다. run()만 가능
-중급자 레벨 : 빠르게 달리고, 점프할 수 있다. run(), jump() 가능
-고급자 레벨 : 엄청 빠르게 달리고, 높게 점프하고, 턴할 수 있다. run(), jump(), turn() 가능
-Player는 한 번에 하나의 레벨 상태만을 가질 수 있다.
-Player가 play() 중에 레벨에 있는 go(int count)라는 메서드를 호출하면 run()하고 count 횟수 만큼 jump()하고 turn한다. 다음 클래스 다이어그램을 참고해 각 레벨에서 go()가 호출될 때 다음과 같이 출력 되게 한다.

**** 초급자 레벨입니다. ****
천천히 달립니다.
jump 못합니다.
turn 못합니다.
**** 중급자 레벨입니다. ****
빨리 달립니다.
높이 jump 합니다.
높이 jump 합니다.
turn 못합니다.
**** 고급자 레벨입니다. ****
엄청 빠르게 달립니다.
아주 높이 jump 합니다.
아주 높이 jump 합니다.
아주 높이 jump 합니다.
turn 합니다.
public class Player {
private PlayerLevel level;
public Player()
{
level = new BeginnerLevel();
level.showLevelMessage(); // PlayerLevel의 Level 출력
}
public PlayerLevel getLevel()
{
return level;
}
public void upgradeLevel(PlayerLevel level)
{
this.level = level;
level.showLevelMessage();
}
public void play(int count)
{
level.go(count);
}
}
public abstract class PlayerLevel {
public abstract void run();
public abstract void jump();
public abstract void turn();
public abstract void showLevelMessage();
// 메서드 호출 시 run() → count 횟수만큼 jump() → turn()
final public void go(int count)
{
run();
for(int i=0; i<count; i++)
{
jump();
}
turn();
}
}
// 초급자
public class BeginnerLevel extends PlayerLevel {
@Override
public void run() {
System.out.println("천천히 달립니다.");
}
@Override
public void jump() {
System.out.println("jump 못합니다.");
}
@Override
public void turn() {
System.out.println("turn 못합니다.");
}
@Override
public void showLevelMessage() {
System.out.println("**** 초급자 레벨입니다. ****");
}
}
// 중급자
public class AdvancedLevel extends PlayerLevel {
@Override
public void run() {
System.out.println("빨리 달립니다.");
}
@Override
public void jump() {
System.out.println("높이 jump 합니다.");
}
@Override
public void turn() {
System.out.println("turn 못합니다.");
}
@Override
public void showLevelMessage() {
System.out.println("**** 중급자 레벨입니다. ****");
}
}
// 고급자
public class SuperLevel extends PlayerLevel {
@Override
public void run() {
System.out.println("엄청 빠르게 달립니다.");
}
@Override
public void jump() {
System.out.println("아주 높이 jump 합니다.");
}
@Override
public void turn() {
System.out.println("turn 합니다.");
}
@Override
public void showLevelMessage() {
System.out.println("**** 고급자 레벨입니다. ****");
}
}
public class MainBoardPlay {
public static void main(String[] args) {
Player player = new Player();
player.play(1);
AdvancedLevel aLevel = new AdvancedLevel();
player.upgradeLevel(aLevel);
player.play(2);
SuperLevel sLevel = new SuperLevel();
player.upgradeLevel(sLevel);
player.play(3);
}
}
인터페이스를 활용한 정책 프로그래밍
고객 센터에 전화 상담을 하는 상담원들이 있다. 고객에게서 전화가 오면 일단 대기열에 저장되고 각 상담원에게 배분 된다.
배분이 되는 정책은 크게 3가지가 있다.
1. 모든 상담원이 동일한 상담 건수를 처리하도록 상담원 순서대로 배분한다.
2. 쉬고 있거나 상담원에게 할당된 통화 수가 가장 적인 상담원에게 배분한다.
3. 고객의 등급에 따라 등급이 높은 고객은 업무 능력이 우수한 상담원에게 배분한다.
세 가지 정책은 필요에 따라 바뀌어 운영될 수 있다. 다음 클래스 다이어그램을 참고해 각 배분 규칙이 적용 되도록 구현하자.

전화 상담원 할당 방식을 선택하세요.
R : 한명씩 차례대로
L : 대기가 적은 상담원 우선
P : 우선 순위가 높은 고객 우선 숙련도 높은 상담원
R
상담 전화를 순서대로 대기열에서 가져옵니다.
다음 순서의 상담원에게 배분합니다.
public interface Scheduler {
public void getNextCall();
public void sendCallToAgent();
}
import java.io.IOException;
public class SchedulerTest {
public static void main(String[] args) throws IOException {
System.out.println("전화 상담원 할당 방식을 선택하세요.");
System.out.println("R : 한명씩 차례대로");
System.out.println("L : 대기가 적은 상담원 우선");
System.out.println("P : 우선 순위가 높은 고객 우선 숙련도 높은 상담원");
int ch = System.in.read(); // 입력 값 받기
Scheduler scheduler = null;
if(ch == 'R' || ch == 'r')
{
scheduler = new RoundRobin(); // 출력
}
else if(ch == 'L' || ch == 'l')
{
scheduler = new LeastJob();
}
else if(ch == 'P' || ch == 'p')
{
scheduler = new PriorityAllocation();
}
else
{
System.out.println("지원되지 않는 기능입니다.");
return;
}
scheduler.getNextCall();
scheduler.sendCallToAgent();
}
}
public class RoundRobin implements Scheduler {
@Override
public void getNextCall() {
System.out.println("상담 전화를 순서대로 대기열에서 가져옵니다.");
}
@Override
public void sendCallToAgent() {
System.out.println("다음 순서의 상담원에게 배분합니다.");
}
}
public class LeastJob implements Scheduler {
@Override
public void getNextCall() {
System.out.println("상담 순서를 순서대로 대기열에서 가져옵니다.");
}
@Override
public void sendCallToAgent() {
System.out.println("현재 상담 업무가 없거나 상담 대기가 가장 적은 상담원에게 할당합니다.");
}
}
public class PriorityAllocation implements Scheduler {
@Override
public void getNextCall() {
System.out.println("고객 등급이 높은 고객의 call을 먼저 가져옵니다.");
}
@Override
public void sendCallToAgent() {
System.out.println("업무 숙련도가 높은 상담원에게 먼저 배분합니다.");
}
}