본문 바로가기
JAVA

자바 인터페이스

by mjjang 2021. 1. 7.

목표

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

학습할 것 (필수)

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

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

인터페이스

[public] interface 인터페이스이름 { ... }

인터페이스는 class 대신 interface를 사용하여 정의할 수 있습니다. 또한 접근 제어자는 public만 사용 가능합니다.

인터페이스 구성요소

인터페이스의 구성 요소는 상수 필드, 추상 메소드, 디폴트 메소드, 정적 메소드입니다. 여기서는 상수 필드와 추상 메소드에 대해 알아보겠습니다.

상수 필드

  • 인터페이스는 객체가 될 수 없기 때문에 런타임에 필드 데이터를 저장할 수 없습니다.
  • 그래서 compile time에 선언되는 상수 필드만 인터페이스에 선언 가능합니다.
  • public static final은 명시적으로 선언하지 않아도 compile time에 자동으로 선언됩니다.

추상 메소드

  • 인터페이스는 기본적으로 추상 메소드의 모음입니다.
  • 추상 메소드는 구현부가 없기 때문에 인터페이스를 구현하는 클래스에서 추상 메소드들을 모두 구현해야 합니다.
  • public abstract는 명시적으로 선언하지 않아도 compile time에 자동으로 선언됩니다.

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

인터페이스를 구현하는 방식은 세 가지가 있습니다.

1. 단일 인터페이스 구현

public interface CPU {
    void cpu();
}

public class Computer implements CPU {
    @Override
    public void cpu() {
        System.out.println("cpu");
    }
}

CPU 인터페이스와 CPU 인터페이스를 구현한 Computer클래스가 있습니다. CPU 인터페이스는 추상메소드로 cpu메소드를 정의하고 public abstract를 생략했습니다. 그리고 CPU 인터페이스를 구현한 Computer 클래스에서는 반드시 CPU 인터페이스의 모든 추상 클래스를 구현해야 합니다.

2. 다중 인터페이스 구현

public interface CPU {
    void cpu();
}

public interface Memory {
    void memory();
}

public class Computer implements CPU, Memory {
    @Override
    public void cpu() {
        System.out.println("cpu");
    }
    @Override
    public void memory() {
        System.out.println("memory");
    }
}

다중 인터페이스를 구현할 때에는 모든 인터페이스의 추상 메소드를 구현해야 합니다.

3. 익명 구현 객체

익명 구현 객체란 말 그대로 이름이 없는 객체입니다. 일반적으로 인터페이스를 사용하려면 해당 인터페이스를 구현한 클래스를 만들어야 합니다. 만약 재사용이 불필요한 클래스가 있을 땐 어떻게 할까요? 한 번밖에 쓰이지 않을 구현 클래스를 위해 클래스를 만드는 번거로움이 있습니다. 그럴 때 익명 구현 객체를 이용할 수 있습니다.

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

위와 같은 문법으로 익명 구현 객체를 만들 수 있습니다. 구체적인 코드로 예를 들어 보겠습니다.

public interface SMS {
    void sendText();
}

public class SmsTest {
    public static void main(String[] args) {
        SMS sms = new SMS() {
            public void sendText() {
                System.out.println("텍스트 문자 보내기");
            }
        };
        sms.sendText();
    }
}

SMS 인터페이스가 있고 SmsTest 클래스는 텍스트 문자를 보내고 싶어 합니다. SMS 인터페이스를 구현한 클래스가 재사용되지 않을 경우에는 구현 클래스를 만들지 않고 익명 구현 객체를 만든 후 sendText() 메소드를 사용하여 텍스트 문자를 보낼 수 있습니다.

public class SmsTest {
    public static void main(String[] args) {
        SMS sms = () -> System.out.println("텍스트 문자 보내기");
        sms.sendText();
    }
}

추상 메소드가 하나인 인터페이스를 함수형 인터페이스라고 부르는데 함수형 인터페이스는 익명 구현 객체를 람다로 더 간단하게 만들 수 있습니다.

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

인터페이스 레퍼런스는 참조 타입이기 때문에 객체 레퍼런스와 똑같이 사용할 수 있습니다.

public class SmsTest {

    // 1. 클래스의 필드
    SMS sms;

    // 2. 생성자의 파라미터
    SmsTest(SMS sms) {
        this.sms = sms;
    }

    // 3. 메소드의 파라미터, 지역 변수, 리턴 값
    SMS send(SMS sms) {
        SMS sms2;
        return sms;
    }
}

4. 인터페이스 상속

인터페이스도 클래스처럼 다른 인터페이스를 상속할 수 있습니다. 차이점은 인터페이스는 다중 상속을 허용합니다.

public interface SuperInterfaceA {
    void superA();
}

public interface SuperInterfaceB {
    void superB();
}

public interface SubInterface extends SuperInterfaceA, SuperInterfaceB {
    void sub();
}

public class ExtendsTest implements SubInterface {
    public void sub() {
        System.out.println("sub");
    }
    public void superA() {
        System.out.println("superA");
    }
    public void superB() {
        System.out.println("superB");
    }
}

인터페이스를 상속한 인터페이스를 구현할 때는 상위 인터페이스의 추상 메소드도 모두 구현해야 합니다.

public static void main(String[] args) {
        SubInterface subInterface = new ExtendsTest();
        subInterface.sub();
        subInterface.superA();
        subInterface.superB();

        SuperInterfaceA superInterfaceA = new ExtendsTest();
        superInterfaceA.superA();

        SuperInterfaceB superInterfaceB = new ExtendsTest();
        superInterfaceB.superB();
    }

SubInterface 타입은 SuperInterfaceA, SuperInterfaceA를 상속받았기 때문에 모든 추상 메소드를 사용할 수 있고 SuperInterfaceA, SuperInterfaceB 타입은 자신의 추상 메소드만을 사용할 수 있습니다.

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

디폴트 메소드는 인터페이스에 새로운 기능을 추가하기 위해 추상 메소드를 추가하면 그 인터페이스를 구현한 모든 클래스에서 추상 메소드를 구현해야 하는데 이미 구현된 많은 클래스들과 하위 호환성을 유지하기 위해 추가되었습니다.

interface 인터페이스 이름 {
    default 리턴타입 메소드이름(파라미터) {
        // 구현
    }
}

자바 8부터는 인터페이스에서 구현 메소드를 가질 수 있어 조금 더 유연한 인터페이스가 되었습니다.

public interface SuperInterfaceA {
    void superA();
    default void superC() {
    }
}

public interface SuperInterfaceB {
    void superB();
    default void superC() {
    }
}

public class defaultExample implements SuperInterfaceA, SuperInterfaceB {

    public void superA() {}
    public void superB() {}
    public void superC() {}
}

디폴트 메소드가 있는 인터페이스를 구현한 클래스에서는 디폴트 메소드를 그 자체로 사용할 수 있고 오버 라이딩해서 사용할 수도 있습니다. 하지만 다중으로 상속하는 인터페이스에서 디폴트 메소드의 이름이 중복되어 있다면 반드시 구현하는 클래스에서 오버 라이딩해줘야 합니다.

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

자바 8부터 인터페이스에 static메소드가 추가되었습니다.

public interface SuperInterfaceA {
    static void run() {
        System.out.println("static Method");
    }
}

public class staticExample {
    public static void main(String[] args) {
        SuperInterfaceA.run();
    }
}

static 메소드는 오버라이딩이 불가능하고 인터페이스명.static메소드() 형식으로 호출합니다.

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

자바 8부터 인터페이스에 static 메소드와 default 메소드가 생겨서 인터페이스 내부에서 구현을 포함할 수 있게 되었습니다. 내부 구현이 복잡해지면 메소드로 코드를 분리해야 하는데 이러한 메소드는 구현 세부 사항이며 외부에서 볼 수 없고 사용할 수 없으므로 private 메소드를 사용합니다. 그런데 자바 8에서는 인터페이스에 private 메소드가 없었기 때문에 길고 복잡한 메소드로 내부 구현을 할 수 밖에 없었습니다. 이러한 문제를 해결하고자 자바 9부터 private 메소드가 추가되었습니다.

public interface MyInterface {
    private static int staticMethod() {
        return 1;
    }

    private int nonStaticMethod() {
        return 0;
    }
}

private 메소드는 static 이거나 non-static일 수 있습니다.

참조

https://interconnection.tistory.com/129
https://siyoon210.tistory.com/95
https://flyburi.com/605

'JAVA' 카테고리의 다른 글

JAVA 예외 처리  (0) 2021.01.15
Garbage Collection  (0) 2021.01.08
자바 패키지에 관하여  (0) 2020.12.31
JVM 구조와 JAVA 메모리 구조  (0) 2020.12.28
상속  (0) 2020.12.25

댓글