인터페이스(Interface)

임준철·2021년 4월 11일
1

OOPAdvance

목록 보기
4/4

인터페이스란

  • 클래스를 사용하는 방식, 접점만을 선언하는 클래스와 유사한 틀
  • 아무런 구현이 되어 있지 않으며, 모든 메소드가 추상 메소드
  • 이름 짓는 방법
    1. interface IFOO <- class Foo 상속을 하는 자식에서 부모 방향으로 / 앞에 i로 쓴다.
    2. interface Printable(형용사 형태로 많이 씀) <- Bar 사용 가능한 형용사 형태로 쓴다.

인터페이스의 특징

  • class가 아니고 interface 키워드를 사용한다.
    • public 또는 default 접근 제어자만 사용 가능.
  • 구현 코드가 들어가지 않아서 new(인스턴스화)될 수 없다.
  • 생성자를 가질 수 없고, 객체 생성을 할 수 없다.
  • 자식 객체는 참조할 수 있으나 메소드와 변수는 사용할 수 없다.
  • 인터페이스명으로 클래스 메소드 호출이 가능하나, 자식클래스명으로 클래스 메소드 호출 불가능.
  • 멤버 변수는 항상 public static final 이다. (생략 가능하다.)
  • 멤버 메소드는 항상 public abstract 이며, 생략가능
  • (자바에서는) 클래스는 하나만 상속할 수 있으나, 인터페이스는 여러개 상속이 가능하다.
  • 인터페이스의 상속은 implements 키워드 사용

인터페이스의 요소

  • 상수: 선언된 모든 변수는 상수로 처리됨(static 멤버 변수는 가질 수 있지만, 인스턴스 멤버 변수는 가질 수 없다)
    • 인터페이스명.클래스 멤버 변수 접근이 가능하다.
  • 메소드 : 모든 메소드는 추상 메소드이다.
  • 디폴트 메소드 : 기본 구현을 가지는 메소드 구현하는 클래스에서 재정의 할 수 있음(java 8)
    • 만약 이미 운영중인 프로그램에 새로운 기능을 추가하려고 할 때, 추상메소드로 구현을 하면 구현한 클래스들은 강제적으로 추상메소드를 구현해야하니
      구현하지 않았을 때 에러가 발생한다. 그래서 이미 운영되고 있는 시스템에서 추가 요건으로 인해 불가피하게 반영을 해야할 때 디폴트 메소드를 쓰면 효과적이다.
  • 정적 메소드 : 인스턴스 생성과 상관없이 인터페이스 타입으로 호출하는 메소드(java 8)
  • private 메소드 : 인터페이스 내에서 사용하기 위해 구현한 메소드
    구현하는 클래스에서 재정의 할 수 없음(java9)

인터페이스 선언과 구현

인터페이스에서 클래스로의 타입 상속

Calc calc = new CompleteCacl();
  • 인터페이스를 구현한 클래스는 인터페이스 타입으로 변수를 선언하여 인스턴스를 생성할 수 있다.
  • 인터페이스는 구현 코드가 없기 때문에 타입 상속이라고 한다.

  • 또 다른 구현코드(예문)
     interface IFoo{
         public static final int MEMBER_VAR = 10; //변하지 않는 상수는 대문자_ 형식으로 사용하기 때문에 여기도 사용한다.
         int MEMBER_VAR2 = 20 ; //이렇게 사용해도됨 public static final;
     
         public abstract void methodA(int param);
         void methodB(int param); //public abstract
     
     }
     
     class Foo implements IFoo{
     
         @Override
         public void methodA(int param) {
             System.out.println(param);
         }
     
         @Override
         public void methodB(int param) {
             System.out.println(param);
     
         }
     }

인터페이스간의 상속

  • 인터페이스 간에도 상속이 가능하데, 이 경우 extends 키워드를 사용한다. (구현하는 것이 아니라서 extends 사용)

    • 클래스에서 인터페이스를 상속받는 경우에는 implements 키워드를 사용한다.
  • 클래스간의 상속과 달리 인터페이스간의 상속은 다중 상속 가능하다.

    interface  Walkable{
        void walk();
    }
    interface  Runable{
        void run();
    }
    
    interface Jumpable extends  Walkable, Runable{
        void jump();
        //  워크, 런이 다 포함되어 있음
    }
    
    class Jumper implements Jumpable{
    // 3개다 구현해야함  -?
        @Override
        public void walk() {
            System.out.println("걷다");
        }
    
        @Override
        public void run() {
            System.out.println("달리다");
        }
    
        @Override
        public void jump() {
            System.out.println("뛰다");
    
        }
    }

인터페이스를 활용한 다형성 구현

인터페이스의 역할은?

  • 인터페이스는 클라이언트 프로그램에 어떤 메소드를 제공하는지 알려주는 명세 또는 약속이다.
  • 한 객체가 어떤 인터페이스의 타입이라 함은 그 인터페이스의 메소드를 구현했다는 의미한다.
  • 클라이언트 프로그램은 실제 구현 내용을 몰라도 인터페이스의 정의만 알면 그 객체를 사용할 수 있다.
  • 인터페이스를 구현해 놓은 다양한 객체를 사용한다 - 다형성
    • JDBC를 구현한 오라클, MSSQL라이브러리 등
    • ORACLE, MySQL, MsSQL 같은 DB회사들이 구현을해서 라이브러리(.jar)를 준다.
      그러면 우리가 오라클로 연결해서 쿼리문을 수행 하겠다 하면 라이브러리를 import해서 라이브러리를 가져다가 커넥션을 연결을 할 때
      오라클 소스를 뒤져보지 않는다 단지, 메소드들만 확인한다.(명세들만 확인을 한다.설계에서 중요하다.*)

다형성 구현 문제

  • 고객센터에는 전화 상담을 하는 상담원들이 있습니다. 일단 고객 센터로 전화가 오면 대기열에 저장됩니다.
  • 상담원이 지정되기 전까지 대기 상태가 됩니다. 각 전화가 상담원에게 배분되는 정책은 다음과 같이 여러 방식으로 구현될 수 있습니다.
    • 상담원 순서대로 배분하기
    • 대기가 짧은 상담원 먼저 배분하기
    • 우선순위가 높은(숙련도가 높은) 상담원에게 먼저 배분하기
  • 위와 같은 다양한 정책이 사용되는 경우 interface를 정의하고 다양한 정책을 구현하여 실행하세요.

구현 코드

  • Scheduler 인터페이스
public interface Scheduler {
    public void getNextCall();
    public void sendCallToAgent();
}
  • RoundRobin 클래스
public class RoundRobin implements Scheduler{
    @Override
    public void getNextCall() {
        System.out.println("상담 전화를 순서대로 대기열에서 가져옵니다.");
    }

    @Override
    public void sendCallToAgent() {
        System.out.println("다음 순서의 상담원에게 배분합니다.");
    }
}
  • PriorityAllocation 클래스
public class PriorityAllocation implements Scheduler{
    @Override
    public void getNextCall() {
        System.out.println("고객등급이 높은 고객의 call을 먼저 가져옵니다.");
    }

    @Override
    public void sendCallToAgent() {
        System.out.println("업무 숙련도가 높은 상담원에게 먼저 배분합니다.");
    }
}
  • LeastJob 클래스
public class LeastJob implements Scheduler{
    @Override
    public void getNextCall() {
        System.out.println("상담 전화를 순서대로 대기열에서 가져옵니다.");
    }

    @Override
    public void sendCallToAgent() {
        System.out.println("현재 상담업무가 없거나 상담대기가 가장 적은 상담원에게 할당합니다.");
    }
}
  • Test 클래스
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; // 왜 리턴을 넣지?
        /**
         * return을 사용하는 이유
         * return은 값을 반환하거나 메소드를 호출한 곳으로 되돌아갈 때 사용하는 예약어이다
         * 만약 메소드 내부에서 return값이 void일때 return을 하면 여기서 메소드를 끝내겠다는 의미
         * return을 호출하면 나머지 로직을 실행하지 않고 함수를 종료하는 의미이다.
         */
        }

        scheduler.getNextCall();
        scheduler.sendCallToAgent();
    }

}

인터페이스와 strategy pattern

  • 인터페이스를 활용하면 다양한 정책이나 알고리즘을 프로그램의 큰 수정 없이 적용, 확장할 수 있음

JDK 1.8 이후의 인터페이스

  • 인터페이스에도 default 메소드를 구현할 수 있다. 접근제어자는 항상 public 키워드를 사용해야 한다.

    • 인터페이스는 원래 메소드를 구현할 수 없었는데 1.8버전 이후에 defualt 메소드가 생기면서 메소드를 구현할 수 있게 되었다.
  • 인터페이스 철학과 맞지 않으나, 기존에 사용하던 인터페이스가 개선되어,
    모든 자식 메소드에 동일하게 구현되어야 하는 메소드가 생긴 경우에 쉽게 기능을 추가하기 위해 만들어졌다.

  • 많은 클래스들이 인터페이스를 상속하는 경우 선택적으로 오버라이드해서 사용할 수 있다.

    interface IFoo2{
       void abstractMethod();
       default void defaultMethod(){ //디폴트 메소드
           System.out.println("default method");
       }
    }
    
    class FooTwo implements IFoo2{
       @Override
       public void abstractMethod() {
           System.out.println("abstract method implemented");
       }
    
    //    @Override // overriding not necessary 반드시 오버라이딩 해야 하는 것은 아님.
    //    public void defaultMethod() {
    //        System.out.println("overriden default method");
    //    }
    
    }
    //처음부터 디폴트메소드를 구현하고 시작하는 것은 철학적으로 맞지않음 원래 abstract 메소드만 있음
    
    class SuperFoo{ //인터페이스가 있는데 부족한 부분이 있어서 메소드를 생성해서 사용하고 있었다
       public void defaultMethod(){
           System.out.println("default method in super class");
       }
    }
    
    
    class Foo3 extends SuperFoo implements IFoo2 {
    
       @Override
       public void abstractMethod() {
           System.out.println("abstract metho implemented");
       }
    
    }
  • 인터페이스의 static 메소드 : 클래스 메소드와 동일하게 사용 가능하다.

    • 인터페이스 이름으로 호출 가능하다.
    • 클래스 구현체의 이름으로도 호출 가능하다.
     interface IFoo {
         static void staticMethod() {
             System.out.println("static method");
         }
     }
     
     public class Main {
         public static void main(String[] args) {
             IFoo.staticMethod(); // static method
         }
     }
profile
지금, 새로운 문을 열자! 문 저편에 무엇이 있을지 두렵더라도!!

0개의 댓글