<Java> 인터페이스

라모스·2021년 9월 2일
0

Java☕

목록 보기
8/14
post-thumbnail

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

학습할 것

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

인터페이스?

일종의 추상클래스이다. 클래스가 구현해야 할 메소드들이 선언되는 추상형으로, 추상 클래스와 달리 일반 메소드, 멤버 변수를 가질 수 없고 오직 추상 메소드와 상수만을 멤버로 가진다.

Java 7까지는 인터페이스는 상수와 추상 메소드로만 구성되었으나, Java 8 부터 default 메소드와 static 메소드를 정의 할 수 있게 되었고, Java 9 부턴 private 메소드를 정의할 수 있게 되었다.

다중 상속이 불가한 Java의 문제점을 극복하기 위해 인터페이스라는 개념을 도입하였다.
말 그대로 설계적인 요소를 위한 유형이라 메소드를 정의하지만 기본적으로 구현 코드는 제공하지 않는다. 추상 메소드를 선언하고 모아둔다는게 주 목적이라 생각하면 된다. 인터페이스를 활용하면 다음과 같은 이점을 얻을 수 있다.

  • 개발 시간 단축
  • 표준화 가능
  • 서로 관계없는 클래스들 간의 관계를 맺어준다.
  • 독립적인 프로그래밍이 가능

1. 인터페이스 정의하는 방법

인터페이스는 다음과 같이 메소드의 정의만 존재하고, 구현은 없다. 인터페이스는 인스턴스화(객체화) 할 수 없으므로 생성자가 필요 없다. 하지만 인터페이스 타입의 레퍼런스 변수는 선언 가능하다.

📌 인터페이스의 구성 요소

  • 상수: public만 허용. public static final 생략 가능
  • 추상 메소드: public abstract 생략 가능
  • default 메소드
  • private 메소드
  • static 메소드
interface PhoneInterface {  // 인터페이스 선언
    int TIMEOUT = 10000;    // 상수 필드
    void sendCall();        // 추상메소드. public abstract 생략
    void receiveCall();
    void printLogo();
}

2. 인터페이스 구현하는 방법

인터페이스의 추상 메소드를 모두 구현한 클래스를 작성해야 한다. implements 키워드를 사용하고, 여러 개의 인터페이스를 동시에 구현 가능하다.

class iPhone implements PhoneInterface { // 인터페이스의 구현
    // PhoneInterface의 모든 메소드 구현
    public void sendCall() { System.out.println("애플 전화 bgm"); }
    public void receiveCall() { System.out.println("전화가 왔습니다."); }
    public void printLogo() { System.out.println("** iPhone **"); }
    // 메소드 추가 작성
    public void flash() { System.out.println("플래시가 켜졌습니다."); }
}

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

위에서 PhoneInterface를 구현한 iPhone 처럼 다른 구현체를 만들면 다음과 같다.

class Galaxy implements PhoneInterface {
    public void sendCall() { System.out.println("삼성 갤럭시 전화 bgm"); }
    public void receiveCall() { System.out.println("전화가 왔습니다."); }
    public void printLogo() { System.out.println("** Samsung Galaxy **"); }
}

Galaxy와 iPhone은 PhoneInterface의 구현체이고 이 두 클래스를 인스턴스화 하여 메소드를 호출하면 다음과 같다.

iPhone iphone = new iPhone();
Galaxy galaxy = new Galaxy();

iphone.sendCall();
galaxy.sendCall();

iphone.receiveCall();
galaxy.sendCall();

iphone.printLogo();
galaxy.printLogo();

Galaxy와 iPhone은 PhoneInterface 타입으로도 생성 가능하다. PhoneInterface 인터페이스를 인자로 받는 메소드가 있다면, Galaxy와 iPhone 타입의 인스턴스를 인자로 사용할 수 있다.

public void myPhone(PhoneInterface phoneinterface) {
    System.out.println("폰 정보: " + phoneinterface.printlogo());
}

iPhone iphone = new iPhone();
Galaxy galaxy = new Galaxy();

myPhone(iphone);
myPhone(galaxy);

4. 인터페이스 상속

인터페이스가 다른 인터페이스를 상속할 수 있다. 이 땐, 일반적인 상속과 마찬가지로 extends 키워드를 사용한다. 인터페이스는 클래스와 달리 멤버 데이터가 없기 때문에 다중 상속도 가능하다.

interface MobilePhoneInterface extends PhoneInterface {
    void sendSMS();
    void receiveSMS();
}

interface MP3Interface {
    void play();
    void stop();
}

// 다중 상속
interface MusicPhoneInterface extends MobilePhoneInterface, PhoneInterface {
    void playMP3RingTone();
}

5. 인터페이스의 기본 메소드 (Default Method)

Java 8 부터 도입된 기능으로 구현을 포함하는 메소드를 포함한 인터페이스를 정의할 수 있다. 인터페이스 내에 코드가 작성된 메소드로 인터페이스를 구현하는 클래스에 자동으로 상속된다. 매번 반복적으로 구현할 필요가 없을 때 기본 메소드로 정의하여 사용하면 된다. public 접근 지정만 허용하고 생략이 가능하다. default는 접근 권한이 아니라 함수가 구현되어 있고 public 임을 의미한다.

public interface Keyboard {
    void type(); // 추상메소드
    default void typing() {
        System.out.println("키보드로 타이핑");
    }
}

Keyboard 클래스에 typing 메소드가 새롭게 추가 되었다고 할 때, 추상 메소드가 아닌 일반 메소드이기 때문에 이를 구현한 클래스는 변경할 것이 하나도 없다.

만약 default 메소드가 충돌이 난다면(이름이 중복되어 충돌 발생)?

  • 클래스에서 default 메소드를 재정의했다면 첫 번째 우선순위이다.
  • 상속구조의 인터페이스에서 자식 인터페이스가 부모 인터페이스의 default 메소드를 재정의했다면 두 번재 우선순위이다.
  • 우선순위가 없는 경우라면 구현 클래스에서 오버라이딩을 해주면 된다.
    (이 경우 선언 시 명시적으로 '인터페이스명.super.메소드명' 으로 선언한다.)

6. 인터페이스의 static 메소드

Java 8 에서 도입된 기능으로 인터페이스에 static 메소드 추가가 가능하다. 클래스에서와 동일하게 사용 가능하고 기본적으로 public 으로 간주한다. static 메소드 이므로 상속이 불가능하다. public, private 모두 지정 가능하다. 또한 객체를 만들지 않고, 반드시 클래스명을 통해 호출해야 한다.

public interface Calculator {
    static int sum(int num1, int num2) {
        return num1 + num2;
    }

    default int multiple(int num1, int num2) {
        return num1 * num2;
    }
}
// 객체를 생성해서 sum 메소드를 호출하는 것은 불가능 함.
// 다음과 같이 클래스명.메소드명(); 으로 호출하자.
public class Main {
    public static void main(String[] args) {
        int sum = Calculator.sum(10, 20);
        System.out.println(sum);
    }
}

7. 인터페이스의 private 메소드

Java 9 부터 도입된 기능으로 이미 Java 8 에서 기본 메소드 사용이 가능해졌기 때문에 로직을 분리하기 위해 사용 가능하다. 인터페이스 내에 코드가 작성된 메소드로 인터페이스 내에 있는 다른 메소드에 의해서만 호출이 가능하다. default 메소드와 private 메소드 사이의 공통적인 코드를 공유할 수 있다.

public interface Caffe {
   default void americano() {
      start();
      System.out.println("분쇄한 원두를 물과 섞는다.");
   }

   default void caffeLatte() {
      start();
      System.out.println("분쇄한 원두를 우유와 섞는다.");
   }

   private void start() {
      System.out.println("원두를 분쇄한다.");
   }
}
public interface Calculator {
    default int sum(int num1, int num2) {
        startSum();
        // startMultiple(); // 사용 가능
        return num1 + num2;
    }

    private void startSum() {
        System.out.println("덧셈 시작");
    }

    static int multiple(int num1, int num2) {
        //startSum();     // 컴파일 에러
        startMultiple();
        return num1 * num2;
    }
    
    private static void startMultiple() {
        System.out.println("곱셈 시작");
    }
}

다음과 같이 private static 메소드를 살펴보면,
default 메소드에서는 static, instance 메소드를 호출 할 수 있지만, static 메소드에서는 private static 메소드만 호출할 수 있다.

References

profile
Step by step goes a long way.

0개의 댓글