*본 내용은 [Do it! 자바 프로그래밍 입문] 책과 강의를 보고 공부하면서 요점 정리한 내용입니다.
interface 인터페이스이름{
public static final float pi = 3.14f;
public void add();
}
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); 프리컴파일 단계에서 자동으로 추상메서드 처리됨
}
※ 점선 화살표(Calculator-->Calc): (인터페이스를) 구현한다는 의미(implements)
※ 실선 화살표(CompleteCalc-->Calculator): 상속한다는 의미(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에 정의된 메소드를 구현할 책임이 있다.
[예제 시나리오]
고객 센터에는 전화 상담을 하는 상담원들이 있습니다. 일단 고객 센터로 전화가 오면 대기열에 저장됩니다. 상담원이 지정되기 전까지 대기 상태가 됩니다. 각 전화를 상담원에게 배분하는 정책은 다음과 같이 여러 방식으로 구현할 수 있습니다
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();
}
}
실행 결과
구매하기
판매하기
고객 판매 주문
고객 판매 주문
고객 판매 주문
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