인터페이스

목표 및 학습할 것

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

인터페이스 정의하는 방법

인터페이스는 오직 추상메서드와 상수만을 맴버로 가질 수 있습니다. 인터페이스는 아래와 같이 정의할 수 있습니다.

interface name {
    public static final variable = value;
    public abstract methodName(parameter a, parameter b ...);
}

인터페이스는 일반 클래스와 달리 아래와 같은 제약사항이 있습니다.

  1. 모든 맴버변수는 public static final 이어야 하며, 이를 생략할 수 있습니다.
  2. 모든 메서드는 public abstract 이어야 하며, 이를 생략할 수 있습니다. 단 static 메서드와 default 메서드는 예외(jdk1.8부터)

이는 생략될 수 있어, 편의상 생략하는 경우가 많습니다. 생략된 제어자는 컴파일 시에 컴파일러가 자동적으로 추가해줍니다.

interface PlayingCard {
    public static final int SPADE = 4;
    final int DIAMOND = 3;  // public static final int DIAMOND = 3;
    static int HEART = 2;   // public static final int HEART = 2;
    int CLOVER = 1;         // public static final int CLOVER = 1;

    public abstract String getCardNumber();
    String getCardKind();   // public abstract String getCardKind();
}

인터페이스 구현하는 방법

인터페이스도 구현하는 방법은 추상클래스의 구현과 비슷합니다. 다만 인터페이스는 구현한다는 의미의 키워드 ‘implements’를 사용합니다.

interface Movable {
    void move(int x, int y);
}

interface Attackable {
    void attack(unit u);
}

interface Fightable extends Movable, Attackable { }

class Fighter implements Fightable {
    public void move(int x, int y) {}
    public void attact(Unit u) {}
}

만일 구현하는 인터페이스의 메서드 중 일부만 구현한다면, abstract를 붙여서 추상클래스로 선언해야 합니다.

abstract class Fighter implements Fightable {
    public void move(int x, int y) {}
}

또한, 상속과 구현을 동시에 할 수도 있습니다.

class Fighter extends Unit implements Fightable {
    public void move(int x, int y) {}
    public void attact(Unit u) {}
}

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

예제를 통해 알아보겠습니다.

interface Parseable {
    public abstract void parse(String fileName);
}

class ParserManager {
    public static Parseable getParser(String type) {
        if(type.equals("XML")) {
            return new XMLParser();
        } else {
            Parseable p = new HTMLParser();
            return p;
        }
    }
}

class XMLParser implements Parseable {
    public void parse(String fileName) {
        System.out.println(fileName + "- XML parsing completed.");
    }
}

class HTMLParser implements Parseable {
    public void parse(String fileName) {
        System.out.println(fileName + "- HTML parsing completed.");
    }
}

class ParserTest {
    public static void main(String args[]) {
        Parseable parser = ParserManager.getParser("XML");
        parser.parse("document.xml");
        parser = ParserManager.getParser("HTML");
        parser.parse("document2.html");
    }
}

리턴타입이 인터페이스라는 것은 메서드가 해당 인터페이스를 구현한 클래스의 인스턴스를 반환한다는 것을 의미합니다


인터페이스 상속

인터페이스를 이용한 다중상속에 대해 알아보겠습니다. 아래 예제는 두 개의 클래스로부터 상속을 받아야 할 상황이라면, 두 조상클래스 중에서 비중이 높은 쪽을 선택하고 다른 쪽은 클래스 내부에 맴버로 구현하거나 필요한 부분을 인터페이스로 뽑아서 구현하도록 합니다.

Tv 클래스와 VCR 클래스가 있을 때, 이를 상속받아 TVCR 클래스를 작성하는 경우입니다.

public class Tv {
    protected boolean power;
    protected int channel;
    protected int volume;

    public void power()         { power = ! power; }
    public void channelUp()     { channel++; }
    public void channelDown()   { channel--; }
    public void volumeUp()      { volume++; }
    public void volumeDown()    { volume--; }
}

public class VCR {
    protected int counter;
    public void play() {}
    public void stop() {}
    public void reset() {}
    public int getCounter()
    public void setCounter(int c) {}
}

VCR 클래스에서 정의된 메서드와 일치하는 추상메서드를 갖는 인터페이스를 작성합니다.

public interface IVCR {
    public void play();
    public void stop();
    public void reset();
    public int getCounter();
    public void setCounter(int c);
}

이제 IVCR 인터페이스를 구현하고 Tv 클래스로부터 상속받는 TVCR 클래스는 아래와 같습니다.

class TVCR extends Tv implements IVCR {
    VCR vcr = new VCR();
    public void play() {
        vcr.play();
    }
    public void stop() {
        vcr.stop();
    }
    public void reset() {
        vcr.reset();
    }
    public int getCounter() {
        return vcr.getCounter();
    }
    public void setCounter(int c) {
        vcr.setCounter(c);
    }
}

이 때, VCR 클래스의 참조변수를 맴버변수로 선언하여 VCR 인스턴스의 함수를 호출할 수 있게 됩니다.

새로 IVCR 인터페이스에 메서드들을 구현해야한다는 점이 있지만, 이처럼 VCR 인스턴스를 이용해 다중상속을 구현할 수 있게 되었습니다.

인터페이스의 이해
  1. 클래스는 사용하는 쪽(User)과 제공하는 쪽(Provider)이 있습니다.
  2. 사용하는 쪽은 제공하는 쪽의 메서드 선언부분만 알면 됩니다.

인터페이스의 기본 메소드 (Default Method), 자바 8

인터페이스의 메소드를 추가하기 위해서는(이는 추상 메소드를 추가한다는 것이고), 해당 인터페이스가 구현된 모든 클래스에 추가된 메소드를 구현해주어야 합니다. 이러한 단점을 해결하고자 JDK 설계자들은 자바8부터 기본 메소드 (Default method)를 고안했습니다.

기본 메소드 (Default Method)는 추상 메소드가 아니기 때문에 인터페이스가 구현된 모든 클래스에서 메소드를 구현할 필요가 없습니다.

디폴트 메서드는 앞에 키워드 default를 붙이며, 추상 메소드와 달리 일반 메소드처럼 {}(curly bracket)으로 감싸야 합니다. 디폴트 메서드 역시 접근 제어자가 public이며 생략가능합니다.

interface MyInterface {
    void mthod(); // 추상 메서드
    default void newMethod() {} // 디폴트 메서드
}

인터페이스의 static 메소드, 자바 8

static 메소드 또한 기본 메소드와 마찬가지로 자바 8부터 사용가능하게 된 기능입니다.

  • 이전에는 인터페이스에는 추상 메서드만 선언 가능

인터페이스의 private 메소드, 자바 9

인터페이스에서 private 메소드 구현이 가능해짐에 따라, 이 private 메소드를 구현 클래스에 노출시키지 않고도 실행시킬 수 있게 되었습니다.

public interface CustomInterface {
     
    public abstract void method1();
     
    public default void method2() {
        method4();  //private method inside default method
        method5();  //static method inside other non-static method
        System.out.println("default method");
    }

    private void method4(){
        System.out.println("private method");
    } 
     
    private static void method5(){
        System.out.println("private static method");
    } 

출처

자바의 정석 https://howtodoinjava.com/java9/java9-private-interface-methods/