Chap 10. 인터페이스 [Do it! 자바 프로그래밍 입문]

doriskim·2022년 12월 14일
0

*본 내용은 [Do it! 자바 프로그래밍 입문] 책과 강의를 보고 공부하면서 요점 정리한 내용입니다.

◼ 인터페이스(interface)

✔ 인터페이스란?

  • 모든 메서드가 추상 메서드(abstract method)로 이루어진 클래스
  • 형식적인 선언만 있고 구현은 없다.
  • 설계 목적으로 사용한다.
interface 인터페이스이름{
	public static final float pi = 3.14f;
    public void add();
}
  • 인터페이스에 선언된 모든 메서드는 public abstract로 추상 메서드
  • 인터페이스에 선언된 모든 변수는 public static final로 상수
  • 예시
    Calc.java
package interfaceex;

public interface Calc {
// public class Calc { 로 작성하면 오류발생.
// interface로 선언하면 메서드들이 추상메서드가 되기때문에 오류가 발생하지 않는다.

	double PI = 3.14;
//	public static final double PI = 3.14; 아무 키워드 쓰지 않아도 프리컴파일 단계에서 자동으로 상수 처리됨
	
	int add(int num1, int num2);
//	public abstract int add(int num1, int num2); 프리컴파일 단계에서 자동으로 추상메서드 처리됨
}

✔ 클래스에서 인터페이스 구현하기

  • 선언한 인터페이스를 클래스가 사용하는 것을 '클래스에서 인터페이스를 구현한다(implements)'라고 표현한다.
  • implements 예약어를 사용한다.

※ 점선 화살표(Calculator-->Calc): (인터페이스를) 구현한다는 의미(implements)
※ 실선 화살표(CompleteCalc-->Calculator): 상속한다는 의미(extends)
※ 이탤릭체: 추상메서드

  • Calc 인터페이스를 Calculator 클래스에서 구현한다.
  • Calc의 모든 추상 메서드를 구현하지 않으면 Calculator는 추상 클래스가 된다.
  • CompleteCalc 클래스가 Calculator를 상속받은 후 모든 메서드를 구현한다.
  • CompleteCalc는 생성가능한 클래스이다.


    [타입상속 vs 구현상속]
  • 타입 상속: 인터페이스 implements, 부모는 선언만 하고 반드시 자식이 정의를 오버라이딩해서 사용
  • 구현 상속: 클래스 extends, 일부는 상속을 받고 나머지는 구현하는 상속

Calc.java

package interfaceex;

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);

}

Calculator.java

package interfaceex;

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;
	}

	// 다 구현하지 않을 것 --> 추상 메서드 갖게 되므로 추상 클래스 됨(abstract 써주기)
//	@Override
//	public int times(int num1, int num2) {
//		return 0;
//	}
//
//	@Override
//	public int divide(int num1, int num2) {
//		return 0;
//	}

}

CompleteCalc.java

package interfaceex;

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;
		return ERROR;
	}

	public void showInfo() {
		System.out.println("Calc 인터페이스를 구현하였습니다.");
	}
}

CalculatorTest.java

package interfaceex;

public class CalculatorTest {

	public static void main(String[] args) {

		int num1 = 10;
		int num2 = 2;
		
		Calc calc = new CompleteCalc(); // 가능
//		Calc calc1 = new Calc(); 인터페이스는 인스턴스화 될 수 없다.
//		Calc calc2 = new Calculator(); 추상클래스이므로 인스턴스화 될 수 없다.
		CompleteCalc calc3 = new CompleteCalc(); // 가능
		Calculator calc4 = new CompleteCalc(); // 가능
		
		System.out.println(calc.add(num1, num2));
	}

}

실행 결과

12

✔ 인터페이스 구현과 형 변환

`Calc calc = new CompleteCalc();`
  • 인터페이스를 구현한 클래스는 인터페이스형으로 선언한 변수로 형 변환 할 수 있다.

  • 상속에서의 형 변환과 동일한 의미를 가진다.

  • 클래스 상속과 달리 구현코드가 없기 때문에 여러 인터페이스를 구현할 수 있다.
    (자바에서 extends 키워드 뒤에 한 개만 올 수 있지만, implements 키워드 뒤에는 여러 개 올 수 있다.)

  • 형 변환시 사용할 수 있는 메서드는 인터페이스에 선언된 메서드만 사용할 수 있다.

  • CompleteCalc가 Calc형으로 대입된 경우 사용할 수 있는 메서드

◼ 인터페이스와 다형성

✔ 인터페이스의 역할

  • 인터페이스는 "Client Code"와 서비스를 제공하는 "객체"사이의 약속이다.

  • Client Code란 'Calc calc = new CompleteCalc();'처럼 인터페이스를 기반으로구현된인스턴스 클래스를 가져다 쓰는 코드를 말한다.

    ※ 클라이언트: 서비스 제공 <--> 서버: 서비스 제공 받음

  • 어떤 객체가 어떤 Interface 타입이라 함은 그 Interface가 제공하는 메서드를 구현했다는 의미이다.

  • Client는 어떻게 구현되었는지 상관없이 Interface의 정의만을 보고 사용할 수 있다.

  • 예: JDBC
    UserInfoWeb은 IUserInfoDao에 정의된 메소드 명세만 보고 Dao를 사용할 수 있고, Dao 클래스들은 IUserInfoDao에 정의된 메소드를 구현할 책임이 있다.

✔ 인터페이스와 다형성

[예제 시나리오]
고객 센터에는 전화 상담을 하는 상담원들이 있습니다. 일단 고객 센터로 전화가 오면 대기열에 저장됩니다. 상담원이 지정되기 전까지 대기 상태가 됩니다. 각 전화를 상담원에게 배분하는 정책은 다음과 같이 여러 방식으로 구현할 수 있습니다

  1. 순서대로 배분하기
    모든 상담원이 동일한 상담 건수를 처리하도록 들어오는 전화 순서대로 상담원에게 하나씩 배분합니다.
  2. 짧은 대기열 찾아 배분하기
    고객 대기 시간을 줄이기 위해 상담을 하지 않는 상담원이나 가장 짧은 대기열을 보유한 상담원에게 배분합니다.
  3. 우선순위에 따라 배분하기
    고객 등급에 따라 등급이 높은 고객의 전화를 우선 가져와서 업무 능력이 좋은 상담원에게 우선 배분합니다.

Scheduler.java

package scheduler;

public interface Scheduler {

	void getNextCall();
	void sendCallToAgent();
}

RoundRobin.java

package scheduler;

public class RoundRobin implements Scheduler{

	@Override
	public void getNextCall() {
		System.out.println("상담 전화를 순서대로 대기열에서 가져옵니다.");
	}

	@Override
	public void sendCallToAgent() {
		System.out.println("다음 순서 상담원에게 배분합니다.");
	}

}

LeastJob.java

package scheduler;

public class LeastJob implements Scheduler{

	@Override
	public void getNextCall() {
		System.out.println("상담 전화를 순서대로 대기열에서 가져옵니다.");
	}

	@Override
	public void sendCallToAgent() {
		System.out.println("현재 상담업무가 없거나 상담대기가 가장 적은 상담원에게 할당합니다.");
	}

}

PriorityAllocation.java

package scheduler;

public class PriorityAllocation implements Scheduler{

	@Override
	public void getNextCall() {
		System.out.println("고객의 등급이 높은 고객의 전화를 먼저 가져옵니다.");
	}

	@Override
	public void sendCallToAgent() {
		System.out.println("업무 skill이 가장 높은 상담원의 대기열에 앞에 우선 배분합니다.");
	}

}

SchedulerTest.java

package scheduler;

import java.io.IOException;

public class SchedulerTest {

	public static void main(String[] args) throws IOException {
		
		System.out.println("전화 상담 배분방식을 선택하세요. R, L, 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("지원하지 않는 기능입니다.");
		}
		
		scheduler.getNextCall();
		scheduler.sendCallToAgent();
		
	}

}

실행 결과

전화 상담 배분방식을 선택하세요. R, L, P
L // 입력
상담 전화를 순서대로 대기열에서 가져옵니다.
현재 상담업무가 없거나 상담대기가 가장 적은 상담원에게 할당합니다.

◼ 인터페이스 요소 살펴보기

✔ 인터페이스의 요소

  • 상수: 모든 변수는 상수로 변환된다.
  • 추상 메서드: 모든 메서드는 추상메서드로 구현된다.

[자바8부터 가능한 메서드]

  • 디폴트 메서드
    기본 구현을 가지는 메서드, 구현 클래스에서 재정의할 수 있다.

  • 정적 메서드(static)
    인스턴스 생성과 상관 없이 인터페이스 타입으로 사용할 수 있는 메서드이다.

  • private 메서드
    인터페이스를 구현한 클래스에서 사용하거나 재정의 할 수 없다. 인터페이스 내부에서만 기능을 제공하기 위해 구현하는 메서드이다. (하위 클래스에서 오버라이딩 불가능, 내부에서만 사용)
    private 혹은 private static으로 선언. (private static은 정적 메서드에서 사용가능)

Calc.java

package interfaceex;

public interface Calc {

	...

	//default 키워드 사용해 default 메서드 생성
	default void description() {
		System.out.println("정수 계산기를 구현합니다.");
		myMethod(); // private method 호출
	}
	
	//static 키워드 사용해 static 메서드 생성
	static int total(int[] arr) {
		int total = 0;
		for(int i : arr) {
			total += i;
		}
		myStaticMethod(); //static 메서드에서 private static 메서드 호출
		return total;
	}
	
	//private 키워드 사용해 private 메서드 생성
	private void myMethod() {
		System.out.println("private 메서드입니다.");
	}
	
	//private static 메서드 생성
	private static void myStaticMethod() {
		System.out.println("private static 메서드입니다.");
	}
}

CompleteCalc.java

package interfaceex;

public class CompleteCalc extends Calculator{

	...
    
	//default 메서드를 오버라이드 할 수 있다.
	@Override
	public void description() {
		System.out.println("완벽한 계산기 입니다.");
	}

}

CalculatorTest.java

package interfaceex;

public class CalculatorTest {

	public static void main(String[] args) {

		int num1 = 10;
		int num2 = 2;
		
		Calc calc = new CompleteCalc(); 

		//default 메서드 사용
		calc.description(); //오버라이딩된 메서드 호출 됨
		
		//static 메서드 사용
		int[] arr= {1,2,3,4,5};
		int sum = Calc.total(arr); //인스턴스 생성과 상관없이 인터페이스 이름으로 호출 가능
		System.out.println(sum);
		
	}

}

실행 결과

완벽한 계산기 입니다.
private static 메서드입니다.
15

✔ 두 개의 인터페이스 구현하기

Buy.java

package interfaceex;

public interface Buy {

	void buy();
	
	default void order() {
		System.out.println("구매주문");
	}
}

Sell.java

package interfaceex;

public interface Sell {

	void sell();
	
	default void order() {
		System.out.println("판매주문");
	}
}

Customer.java

package interfaceex;

public class Customer implements Buy, Sell{

	//인터페이스 구현
	@Override
	public void sell() {
		System.out.println("판매하기");
	}

	@Override
	public void buy() {
		System.out.println("구매하기");
	}
    
	//default 메서드 이름이 똑같으면(order()) 오류 발생 --> 메서드 오버라이딩 해야한다.
	@Override
	public void order() {
		System.out.println("고객 판매 주문");
	}

}

CustomerTest.java

package interfaceex;

public class CustomerTest {
	
	public static void main(String[] args) {
		
		Customer customer = new Customer();
		
		Buy buyer = customer;
		buyer.buy();
		
		Sell seller = customer;
		seller.sell();
		//customer를 Buy, Sell 인터페이스 변수에 둘 다 대입할 수잇다.
		
		customer.order();
        // Buy, Sell의 메서드가 아닌 인스턴스의 메서드가 불린다.
		buyer.order(); 
		seller.order();
	}
}

실행 결과

구매하기
판매하기
고객 판매 주문
고객 판매 주문
고객 판매 주문

◼ 인터페이스 활용하기

✔ 인터페이스 상속

  • 인터페이스 간에도 상속이 가능
  • 구현코드의 상속이 아니므로 형 상속(type inheritance)라고 함

X.java

package interfaceex;

public interface X {

	void x();
}

Y.java

package interfaceex;

public interface Y {

	void y();
}

MyInterface.java

package interfaceex;

public interface MyInterface extends X, Y{
	
	void myMethod();

}

MyClass.java

package interfaceex;

public class MyClass implements MyInterface{

	// 구현해야 하는 메서드 총 3개
	@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 static void main(String[] args) {
		MyClass myClass = new MyClass();
		
		X xClass = myClass;
		xClass.x();
	
	}
}

실행 결과

x()

✔ 인터페이스 구현과 클래스 상속 함께 사용하기

순서는 클래스 상속 먼저.
실제 프레임워크(스프링, 안드로이드)를 사용하면 클래스를 상속받고 여러 인터페이스를 구현하는 경우가 종종 있다.

Queue.java

package interfaceex;

public interface Queue {

	void enQueue(String title);
	String deQueue();
	int getSize();
}

Shelf.java

package interfaceex;

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();
	}
}

BookShelf.java

package interfaceex;

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();
	}

}

BookShelfTest.java

package interfaceex;

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

0개의 댓글

관련 채용 정보