ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [live-study] 8: 인터페이스
    개발/Java 2021. 1. 9. 10:45

    목표

    자바의 인터페이스에 대해 학습하세요.

    학습할 것 (필수)

    • 인터페이스 정의하는 방법
    • 인터페이스 구현하는 방법
    • 인터페이스 레퍼런스를 통해 구현체를 사용하는 방법
    • 인터페이스 상속
    • 인터페이스의 기본 메소드 (Default Method), 자바 8
    • 인터페이스의 static 메소드, 자바 8
    • 인터페이스의 private 메소드, 자바 9

    인터페이스란

    • 인터페이스란 객체가 할 일을 정의해놓은 타입이다.

    • 구현 코드(객체를 사용하는 코드)와 객체 사이에 위치해서 서로 통신하도록 다리 역할을 한다.

    • 다리 역할을 해줌으로서 교환성이 높아지고, 곧 다형성의 구현을 의미한다.

    • 모든 기능을 추상적으로 정의하여 설계도처럼 사용한다.

      • 자바8 부터 static 메소드default 메소드로 추상화가 아닌 구현도 가능해졌다.
    • 자바8 에서 나온 람다식때문에 중요성이 커졌다.

      람다식에서는 함수적 인터페이스 구현 객체를 생성하기 때문이다.

    추상 클래스 상속과 인터페이스 구현의 차이?

    기능적으로는 상태가 존재할 수 있냐에 대한 차이가 크다.
    인터페이스에서는 상수 필드만 가질 수 있고 상태를 가질 수 없다. (private 접근제한자도 불가능)
    추상 클래스는 상태를 가질 수 있다 (ex. private String msg = "message")

    기능적인 차이도 있지만, 의미적인 차이가 있다.
    추상 클래스에서 상속으로 만들어지는 관계는 서브 클래스 is kind of a 상위 클래스에 가깝다. (분류)
    인터페이스와 그 구현의 관계는 구현 is able to 인터페이스에 가깝다.

    인터페이스를 정의하는 방법

    • 인터페이스는 .java파일에 작성하고 컴파일하면 .class파일이 되기 때문에 기본적으로 클래스와 형태는 동일하지만, 선언만 다르다.

      • 그러나 인터페이스는 객체로 생성할 수 없기 때문에 생성자를 가지지 않는다.
      • 즉, 구성 멤버로 필드와 메소드만 가질 수 있다.
    • class대신에 interface 키워드를 사용한다.

    • publicdefault를 접근지시자로 사용할 수 있다.

      public interface myInterface {
        // 필드
        // 메소드
      }
      

    인터페이스의 필드

    • 인터페이스는 기본적으로 데이터를 저장하는 용도가 아니다. 하지만 상수를 정의할 수는 있다.

    • 인터페이스에 필드를 선언하면 기본적으로 public static final를 정의하지 않아도 컴파일 과정에 붙는다.

      (컴파일에는 문제가 없지만, 기본적으로 상수는 대문자로 정의하며, 띄어쓰기 대신 _(underscore)를 사용한다.)

    public interface Calculatable {  
      public static final double pi = 3.14159;  
        double euler = 2.71828; // public static final 이 컴파일 과정에서 붙는다.  
        // 메소드  
    }
    

    인터페이스의 메소드

    • 인터페이스의 메소드는 기본적으로 추상 메소드를 사용한다.
    • 자바8 에서는 static 메소드, default 메소드가 추가 되었으며, 자바9 에서는 private 메소드가 추가되었다.

    추상 메소드

    • 추상 메소드는 구현 없이 객체가 가지고 있는 메소드를 설명한 것이다.
      • 중괄호({})가 없이, 리턴 타입, 메소드명, 매개 변수만 명시되어 있다.
    • 인터페이스에서는 메소드를 선언하면 기본적으로 public abstract를 명시하지 않아도 컴파일 과정에서 붙는다.
    • 개발 코드에서 인터페이스를 통해 메소드를 호출하면, 실제 구현된(메소드가 오버라이드된) 객체에서 실행이 된다.
    public interface Calculatable {  
        public static final double pi = 3.14159;  
        double euler = 2.71828;
    
          public static final double add(double a, double b); 
          double substract(double a, double b);  // public abstract 가 컴파일 과정에서 붙는다.
    }
    

    default 메소드

    • 추상 메소드와 달리, 인터페이스에 메소드를 구현해놓고 사용할 수도 있고 구현 객체에서 오버라이드 할 수도 있다.
      • 구현 객체가 가지고 있는 인스턴스 메소드나 마찬가지다.
    • 리턴 타입 앞에 default 키워드가 붙는다.
    • public을 명시하지 않아도 컴파일 과정에서 붙는다.

    default 메소드가 필요한 이유 - 확장
    인터페이스에 새로운 기능을 추가하고자 추상 메소드를 추가하면 구현 클래스들이 모두 그 새로운 추상 메소드를 구현해야 한다.
    구현 클래스가 많으면 불필요하게 많은 작업을 해야한다. (구현 클래스가 많지 않더라도 불필요한 작업을 해야할 수 있다.)

    그러나 default 메소드를 사용하면 인터페이스에만 메소드를 추가하면 된다.

    public interface Calculatable {  
      public abstract double add(double a, double ); // 추상 메소드  
      public default double substract(double a, double b) { // default 메소드  
          return a-b;  
      }  
    }
    

    static 메소드

    • 인터페이스에서 메소드를 구현하고, 객체 없이 인터페이스 만으로 호출이 가능하다.
      • ex. 객체없이 static 호출. -> Calculatable.addition()
    • 자바8에서 생긴 메소드로, 리턴 타입 앞에 static 키워드가 붙는다.
    public interface Calculatable {  
      public abstract double add(double a, double ); // 추상 메소드  
      public default double substract(double a, double b) { // default 메소드  
          return a-b;  
    }
    
      public static void changeBatterty() { // static 메소드  
          System.out.println("건전지를 교환합니다.");  
      }  
    }
    

    private 메소드

    • interface 에서 private한 메소드를 정의할 수 있게 해준다.
      • 기존에는 interface에서 사용할 수 있는 접근 제한자가 publicdefault 뿐이었다.
    • 인터페이스 내부에서 default 혹은 static 메소드를 구현할 때
      • 자바8에서는 반복되는 코드를 분리하려면 public하게 default 혹은 static 메소드를 만들어야 했다.
      • 자바9의 private 메소드는 코드를 외부에 노출하지 않고 재사용성을 높여준다.
    • static, non-static 모두 구현 가능하다.
    • private 메소드는 다른 인터페이스나 구현 클래스에 상속되지 않는다.
    public interface Calculatable {  
      private static void takeOutBattery(); { // static-private 메소드  
          System.out.println("건전지를 빼냅니다.");  
    
      public static void changeBatterty() {
          takeOutBattery();
          System.out.println("건전지를 교환합니다.");
      }
    
    }
    

    인터페이스를 구현하는 방법

    • 개발 코드에서 인터페이스를 통해 메소드를 호출하면 인터페이스는 객체의 메소드를 호출한다.
    • 구현 객체를 생성하는 방법은 구현 클래스 생성익명 구현 객체가 있다.
    • 추상 메소드는 모두 오버라이드 해야한다. 그렇지 않으면 컴파일 에러가 발생한다.

    구현 클래스

    • 인터페이스에서 추상화로 선언한 메소드를 실제로 오버라이드해서 구현하고 있는 클래스를 구현 클래스라고 한다.

    • 구현 클래스는 일반 클래스와 같은데, 클래스명 뒤에 implements 인터페이스를 명시해야 한다.

      • 인터페이스에 있는 추상 메소드와 메소드 시그니처가 같아야 한다.

    예제

    인터페이스

    public interface Calculatable {  
      public abstract double add(double a, double b);  
    }

    주판 구현 클래스

    public class Abacus implements Calculatable { // 주판을 구현한다.  
      @Override  
      public double add(double a, double b) {  
        System.out.println("주판을 튕긴다.");  
        return a+b;  
      }  
    }

    디지털 계산기 구현 클래스

    public class DigitalCalculator implements Calculatable { // 주판을 구현한다.  
      @Override  
      public double add(double a, double b) {  
        System.out.println("계산기의 버튼을 누른다.");  
        return a+b;  
      }  
    }

    익명 구현 객체

    • 구현 클래스를 사용하면 재사용을 할 수 있지만, 일회성으로 사용하기엔 비효율적이다.

    • 그래서 클래스 정의나 소스 파일을 생성하지 않고 구현 객체를 만드는 방법이 익명 구현 객체 방식이다.

      • 모든 객체는 클래스로부터 만들어지기 때문에, 컴파일시에 익명 구현 객체도 .class파일이 만들어지긴 한다.

      • 익명구현객체가_포함된_클래스명$1.class 과 같은 이름으로 만들어진다.

        UI 프로그래밍의 이벤트 처리나 임시 작업 스레드를 만들때 많이 사용한다고 한다.

    익명 구현 객체는 다음과 같이 사용할 수 있다.

    인터페이스 변수 = new 인터페이스() {  
        // 인터페이스의 추상메소드 구현  
    }
    

    예제)

    인터페이스

    public interface Calculatable {  
        public abstract double add(double a, double b);  
    }
    

    익명 구현 객체

    public class Main() {  
      public static void main(String args\[\]) {  
        Calculatable calculator = new Calculatable() {  
          @Override  
          public double add(double a, double b) {  
              ....구현....  
          }  
        }  
      }  
    }

    인터페이스 레퍼런스를 통해 구현체를 사용하는 방법

    • 인터페이스로 구현 객체를 사용하려면

      • 인터페이스 변수를 선언하고

      • 변수에 구현 객체를 대입한다.

        인터페이스 변수는 참조 타입이기 때문에 구현 객체가 대입될 경우 구현 객체의 번지를 저장한다.

    • 개발 코드에서 인터페이스는 클래스의 필드, 생성자 || 메소드의 매개 변수, 생성자, 메소드의 로컬변수로 선언될 수 있다.

    예제)

    인터페이스

    public interface Calculatable {
        public abstract double add(double a, double b);
        public default double subtract(double a, double b) {
            System.out.println("빼기 기능이 고장나서 두번 뺀다.");
            return a-b-b;
        }
        public default double multiply(double a, double b) {
            return a*b;
        }
        public static void printPI() {
            System.out.println("3.14159");
        }
    
    }

    구현 클래스

    public class DigitalCalculator implements Calculatable{
    
        // 추상 메소드 구현
        @Override
        public double add(double a, double b) {
            return a+b;
        }
    
        // default 메소드 구현 (optional)
        @Override
        public double subtract(double a, double b) {
            return a-b;
        }
    }

    개발 코드

    public class myInterface {
        public static void main(String[] args) {
            Calculatable digitalCalculator = new DigitalCalculator();
            digitalCalculator.add(1, 2);
    
            Calculatable.printPI(); // static 메소드의 사용
        }
    }
    • 인터페이스가 참조 타입으로 사용된다.
    • 추상 메소드는 구현 클래스에서 반드시 구현해야하기 때문에 구현했다.
    • default 메소드는 구현하지 않으면 인터페이스에 구현된 것을 사용하고, 구현 클래스에서오버라이드 할 수도 있다.
      • 그래서 substract는 오버라이드 하였고, multiply는 하지 않았다.
    • static 메소드는 기존의 인터페이스명.메소드() 으로 사용한다.

    인터페이스 상속

    • 인터페이스도 extends 키워드를 통해 상속이 가능하다.
    • 다중 상속도 가능하다.
    • 하위 인터페이스의 구현클래스는 하위 인터페이스의 메소드 구현체 뿐만 아니라, 상위 인터페이스의 메소드 구현체도 필요하다.

    예제)

    예제 계층 구조

    아래 코드는 위에 그림과 같은 계층 구조를 갖는다.

     

    상위 인터페이스 (Callable, Internet)

    public interface Calculatable {
        public abstract double add(double a, double b);
        public default double subtract(double a, double b) {
            return a-b;
        }
        public default double multiply(double a, double b) {
            return a*b;
        }
        public static void printPI() {
            System.out.println("3.14159");
        }
    }
    
    public interface Internet {
        public abstract void connectToInternet();
    }
    

    하위 인터페이스

    public interface Phone extends Calculatable, Internet {
        public abstract void call(String name);
    }

    구현 클래스

    class iPhone implements Phone{
        @Override
        public double add(double a, double b) {
            return a+b;
        }
    
        @Override
        public void connectToInternet() {
            System.out.println("와이파이로 연결");
        }
    
        @Override
        public void call(String name) {
            System.out.println(name + " 에게 통화 시도");
        }
    }
    

    개발 코드

    public class myInterface {
        public static void main(String[] args) {
            Internet internet = new iPhone();
            Phone phone = new iPhone();
            iPhone iPhone = new iPhone();
        }
    }

    개발 코드에서 보이듯이 상위 인터페이스, 하위 인터페이스, 구현 클래스 모두 타입으로 사용할 수 있다.

    참고

    이것이 자바다
    https://howtodoinjava.com/java9/java9-private-interface-methods/#:~:text=Since%20java%209%2C%20you%20will,private%20static%20method%20in%20interfaces.&text=Private%20interface%20method%20cannot%20be,and%20non%2Dstatic%20interface%20methods.
    https://flyburi.com/605

    댓글

Designed by Tistory.