인터페이스
- 인터페이스는 클래스 혹은 프로그램이 제공하는 기능을 명시적으로 선언하는 역할을 한다.
- 인터페이스는 추상 메서드와 상수로만 이루어져 있다.
구현하기
package interfaceex;
public interface Calc {
double PI = 3.14;
int ERROR = -999999999;
int add(int num1, int num2);
int substract(int num1, int num2);
int times(int num1, int num2);
int divide(int num1, int num2);
}
인터페이스를 사용하기 위해서는 implements 예약어를 사용해야 하며, 클래스를 추상 클래스로 만들거나
추상 메서드를 전부 사용해야 한다.
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;
}
}
클래스 완성하고 실행하기
계산기 클래스를 만들어서 실행을 해본다.
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;
else
return Calc.ERROR; //나누는 수가 0인 경우 오류 반환
}
public void showInfo() { //CompleteCalc에서 추가로 구현한 메소드
System.out.println("Calc 인터페이스를 구현하였습니다");
}
}
package interfaceex;
public class CalculatorTest {
public static void main(String[] args) {
int num1 = 10;
int num2 = 5;
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();
}
}
인터페이스 구현과 형 변환
- 상속과 마찬가지로 인터페이스에서도 하위 클래스가 상위 클래스 자료형을 묵시적 형 변환이 가능하다.
- 인터페이스를 구현한 클래스가 있을 때 그 클래스는 해당 인터페이스형으로 묵시적 형 변환이 이루어지고
형 변환되었을 때 사용할 수 있는 메서드는 인터페이스에서 선언한 메서드 뿐이다.
인터페이스와 다형성
인터페이스의 역할
인터페이스의 역할은 인터페이스를 구현한 클래스가 어떤 기능의 메서드를 제공하는 명시하는 것이다.
인터페이스와 다형성
인터페이스를 사용하면 다형성을 구현하여 확장성 있는 프로그램을 만들 수 있다.
Schedule 인터페이스를 구현하는 각 세 클래스를 직접 만들어서 확인해 보자
package scheduler;
public interface Scheduler { //인터페이스 정의
public void getNextCall();
public void sendCallToAgent();
}
package scheduler;
//상담원 한명씩 돌아가며 동일하게 상담업무를 배분합니다.
public class RoundRobin implements Scheduler{
@Override
public void getNextCall() {
System.out.println("상담 전화를 순서대로 대기열에서 가져옵니다");
}
@Override
public void sendCallToAgent() {
System.out.println("다음 순서 상담원에게 배분합니다.");
}
}
package scheduler;
// 현재 상담업무가 없거나 상담대기가 가장 작은 상담원에게 배분합니다.
public class LeastJob implements Scheduler{
@Override
public void getNextCall() {
System.out.println("상담 전화를 순서대로 대기열에서 가져옵니다");
}
@Override
public void sendCallToAgent() {
System.out.println("현재 상담업무가 없거나 상담대기가 가장 작은 상담원에게 할당합니다.");
}
}
package scheduler;
// 고객등급이 높은 고객부터 대기열에서 가져와 업무 능력이 높은 상담원 우선으로 배분합니다.
public class PriorityAllocation implements Scheduler{
@Override
public void getNextCall() {
System.out.println("고객 등급이 높은 고객의 전화를 먼저 가져옵니다.");
}
@Override
public void sendCallToAgent() {
System.out.println("업무 skill 값이 높은 상담원에게 우선적으로 배분합니다.");
}
}
package scheduler;
import java.io.IOException;
public class SchedulerTest {
public static void main(String[] args) throws IOException {
//문자를 입력받는 System.in.read()를 사용하기 위해서는 IOException에서 오류를 처리해야 한다.
System.out.println("전화 상담 할당 방식을 선택 하세요.");
System.out.println("R : 한명씩 차례로 할당 ");
System.out.println("L : 쉬고 있거나 대기가 가장 적은 상담원에게 할당 ");
System.out.println("P : 우선순위가 높은 고객 먼저 할당 ");
int ch = System.in.read(); //할당 방식을 입력받아 ch에 대입
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();
}
}
이 방식에서 분배 방식을 추가하려면 인터페이스를 구현하는 새 클래스를 만들면 된다. 이것이 인터페이스의 다형성이다.
인터페이스 요소 살펴보기
인터페이스 상수
- 인터페이스는 추상 메서드로 이루어지기 때문에 인스턴스 생성이 불가능하고, 멤버 변수도 사용할 수 없다.
하지만 변수를 선언해도 인터페이스에 선언한 변수는 컴파일하면 상수로 변환되기 때문에 변수를 선언할 수 있다.
디폴트 메서드
- 디폴트 메서드는 인터페이스에서 구현하지만 이후 인터페이스를 구현한 클래스가 생성되면
그 클래스에서 사용할 기본 기능이다. - 디폴트 메서드를 선언할 때는 default 예약어를 사용한다.
- 디폴트 메서드는 하위 클래스에서 재정의가 가능하다.
앞에서 만든 Calc 인터페이스에 디폴드 메서드를 구현하고 호출해 보자
package interfaceex;
public interface Calc {
double PI = 3.14;
int ERROR = -999999999;
int add(int num1, int num2);
int substract(int num1, int num2);
int times(int num1, int num2);
int divide(int num1, int num2);
default void description() { //디폴트 메서드 선언
System.out.println("정수 계산기를 구현합니다");
}
}
package interfaceex;
public class CalculatorTest {
public static void main(String[] args) {
int num1 = 10;
int num2 = 5;
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();
calc.showInfo(); //디폴트 메서드 호출
}
}
정적 메서드
- 정적 메서드는 static 예약어를 사용하여 선언하며 클래스 생성과 무관하게 사용할 수 있다.
- 인터페이스 이름으로 직접 참조하여 사용한다.
이번에는 Calc 인터페이스에 정적 메서드를 선언하고 호출해 보자
static int total(int[] arr){ //정적 메서드 선언
int total = 0;
for(int i: arr){
total += i;
}
return total;
}
package interfaceex;
public class CalculatorTest {
public static void main(String[] args) {
int num1 = 10;
int num2 = 5;
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();
calc.showInfo(); //디폴트 메서드 호출
int[] arr = {1,2,3,4,5};
System.out.println(Calc.total(arr)); //정적 메소드 사용하기
}
}
private 메서드
- private 메서드는 인터페이스를 구현한 클래스에서 사용하거나 재정의할 수 없다.
- 추상 메서드에 private 예약어를 사용할 수는 없지만, static 예약어는 함께 사용 가능하다.
- private static 메서드는 정적 메서드에서 호출하여 사용한다.
package interfaceex;
public interface Calc {
double PI = 3.14;
int ERROR = -999999999;
int add(int num1, int num2);
int substract(int num1, int num2);
int times(int num1, int num2);
int divide(int num1, int num2);
default void description() { //디폴트 메서드 선언
System.out.println("정수 계산기를 구현합니다");
myMethod(); //디폴트 메서드에서 private 메서드 호출
}
static int total(int[] arr){ //정적 메서드 선언
int total = 0;
for(int i: arr){
total += i;
}
myStaticMethod(); //정적 메서드에서 private 메서드 호출
return total;
}
private void myMethod() { //private 메서드
System.out.println("private 메서드 입니다.");
}
private static void myStaticMethod() { //private static 메서드
System.out.println("private static 메서드 입니다.");
}
}
인터페이스 활용하기
한 클래스가 여러 인터페이스를 구현하는 경우
인터페이스는 한 클래스가 여러 인터페이스를 구현할 수 있다.
package interfaceex;
public interface Buy {
void buy();
}
package interfaceex;
public interface Sell {
void sell();
}
package interfaceex;
public class Customer implements Buy, Sell{
//Customer 클래스는 Buy와 Sell 인터페이스를 모두 구현함
@Override
public void buy() {
System.out.println("구매하기");
}
@Override
public void sell() {
System.out.println("판매하기");
}
}
package interfaceex;
public class CustomerTest {
public static void main(String[] args) {
Customer customer = new Customer();
Buy buyer = customer;
//Customer 클래스형인 customer를 Buy 인터페이스형인 buyer에 대입하여 형 변환
//buyer는 Buy 인터페이스의 메서드만 호출 가능
buyer.buy();
Sell seller = customer;
//Customer 클래스형인 customer를 Sell 인터페이스형인 seller에 대입하여 형 변환
//buyer는 Buy 인터페이스의 메서드만 호출 가능
seller.sell();
if( seller instanceof Customer){
Customer customer2 = (Customer)seller;
//seller를 하위 클래스형인 Customer로 다시 형 변환
customer2.buy();
customer2.sell();
}
}
}
두 인터페이스의 디폴트 메서드가 중복되는 경우
package interfaceex;
public interface Buy {
void buy();
default void order(){
System.out.println("구매 주문");
}
}
package interfaceex;
public interface Sell {
void sell();
default void order(){
System.out.println("판매 주문");
}
}
두 인터페이스 모두 order() 디폴트 메서드를 가지고 있기 때문에 Customer 클래스에서 재정의 해야 사용이 가능하다.
package interfaceex;
public class Customer implements Buy, Sell{
//Customer 클래스는 Buy와 Sell 인터페이스를 모두 구현함
@Override
public void buy() {
System.out.println("구매하기");
}
@Override
public void sell() {
System.out.println("판매하기");
}
@Override
public void order() { //디폴트 메서드를 재정의
System.out.println("고객 판매 주문");
}
}
package interfaceex;
public class CustomerTest {
public static void main(String[] args) {
Customer customer = new Customer();
Buy buyer = customer;
//Customer 클래스형인 customer를 Buy 인터페이스형인 buyer에 대입하여 형 변환
//buyer는 Buy 인터페이스의 메서드만 호출 가능
buyer.buy();
buyer.order();
Sell seller = customer;
//Customer 클래스형인 customer를 Sell 인터페이스형인 seller에 대입하여 형 변환
//buyer는 Buy 인터페이스의 메서드만 호출 가능
seller.sell();
seller.order();
if( seller instanceof Customer){
Customer customer2 = (Customer)seller;
//seller를 하위 클래스형인 Customer로 다시 형 변환
customer2.buy();
customer2.sell();
}
customer.order();
}
}
인터페이스 상속하기
- 인터페이스 간에도 상속이 가능하지만 구현 코드를 통해 기능을 상속하는 것이 아니기 때문에 형 상속이라고 부른다.
- 인터페이스는 여러 개를 동시에 상속받을 수 있다.
package interfaceex;
public interface X {
void x();
}
package interfaceex;
public interface Y {
void y();
}
package interfaceex;
public interface MyInterface extends X, Y{
//인터페이스 X와 Y를 여러개 상속 받음
void myMethod();
}
package interfaceex;
public class MyClass implements MyInterface{
@Override
public void x() { //X 인터페이스에서 상속받은 x() 메서드 구현
System.out.println("x()");
}
@Override
public void y() { //Y 인터페이스에서 상속받은 y() 메서드 구현
System.out.println("y()");
}
@Override
public void myMethod() { //MyInterface에서 상속받은 myMethod() 메서드 구현
System.out.println("myMethod()");
}
}
package interfaceex;
public class MyClassTest {
public static void main(String[] args) {
MyClass mClass = new MyClass();
X xClass = mClass;
//상위 인터페이스 X형으로 대입하면 X에 선언한 메서드만 호출 가능
xClass.x();
Y yClass = mClass;
//상위 인터페이스 Y형으로 대입하면 Y에 선언한 메서드만 호출 가능
yClass.y();
//구현한 인터페이스형 변수에 대입하면 인터페이스가 상속한 모든 메서드 호출 가능
MyInterface iClass = mClass;
iClass.myMethod();
iClass.x();
iClass.y();
}
}
인터페이스 구현과 클래스 상속 함께 쓰기
한 클래스에서 클래스 상속과 인터페이스 구현을 모두 할 수 있다.
package bookshelf;
import java.util.ArrayList;
public class Shelf {
protected ArrayList<String> shelf;
//자료를 순차적으로 저장할 ArrayList 선언
public Shelf(){ //디폴트 생성자로 Shelf 클래스를 생성하면 ArrayList도 생섬됨
shelf = new ArrayList<String>();
}
public ArrayList<String> getShelf(){
return shelf;
}
public int getCount(){
return shelf.size();
}
}
package bookshelf;
public interface Queue {
void enQueue(String title); //배열의 맨 마지막에 추가
String deQueue(); //배열의 맨 처음 항복 반환
int getSize(); //현재 Queue에 있는 개수 반환
}
package bookshelf;
public class BookShelf extends Shelf implements Queue{
//Shelf 클래스를 상속받고 Queue 인터페이스를 구현
@Override
public void enQueue(String title) { //배열에 요소 추가
shelf.add(title);
}
@Override
public String deQueue() { //맨 처음 요소를 배열에서 삭제하고 반환
return shelf.remove(0);
}
@Override
public int getSize() { //배열 요소 개수 반환
return getCount();
}
}
package bookshelf;
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());
}
}
'Java-Study' 카테고리의 다른 글
Java Stduy : 7주차 정리 - 기본 클래스(Object, String, Wrapper, Class 클래스) (0) | 2022.12.23 |
---|---|
Java Study : 6주차 - 백준 문제풀이 (1) | 2022.12.09 |
Java Study : 5주차 - 백준 문제풀이 (0) | 2022.12.02 |
Java Study : 5주차 정리 - 추상 클래스 (1) | 2022.12.02 |
Java Study : 4주차 - 백준 문제풀이 (0) | 2022.11.26 |