Untitled_Blue

[JAVA] 인터페이스 본문

Programming Language/JAVA

[JAVA] 인터페이스

Untitled_Blue 2023. 5. 21. 20:19
반응형

안녕하세요. 이번 글은 인터페이스에 대한 설명입니다.

- 인터페이스란?

먼저 인터페이스의 사전적인 정의는 2개 이상의 장치가 연결될 수 있도록 전기 신호를 변환해주는 장치를 의미한다.

이를 프로그래밍 언어 관점에서 보면 인터페이스는 추상 메서드로만 구현되어 있는 것이 특징이며 두 개 이상의 객체 연결을 담당하는 문법을 의미한다. 사용하는 방법은 클래스명 뒤에 implement을 붙이고 그 뒤에 오버라이딩하고자 하는 인터페이스를 입력한다.

package classes;

interface Inter_A {
    int a = 5;
    public static final String b = "Hello";

    void mPrint();

    public abstract void mPrint2();
}

interface Inter_B {
    int k = 10;
    boolean tf = false;

    void mTF();
}

public class Interface_Practice implements Inter_A {
    public static void main(String[] args) {
        System.out.println(Inter_A.a);
        System.out.println(Inter_A.b);
    }
}

다음 소스 코드는 인터페이스의 예시를 설명하기 위한 코드이다. 해당 코드는 확인해보면 public class Interface_Practice implements Inter_A 부분에서 오류가 발생하는 점을 확인할 수 있다. 그 전에 먼저 인터페이스의 구조를 확인하고자 한다. 

다음과 같이 인터페이스는 프로그래밍 내 전반적인 설계도이다. 내부에서 필드는 public static final로 메서드는 public abstract로 기본적으로 정의하고 있다는 점이 특징이다. 이때 static final는 정적 상수이고 abstract는 추상적임을 의미한다. 이렇게 인터페이스 내부에 선언할 모든 필드와 메서드는 기본적으로 정의하는 제어자가 존재한다. 그만큼 다른 제어자 private나 protected 같은 제어자를 사용할 수 없도록 되어있다. 또한 기본적으로 정의하는 만큼 필드와 메서드를 선언할 때 앞에 제어자를 별도로 작성하지 않고 생략해도 된다. 컴파일러가 자동으로 제어자를 추가해준다.

상단 이미지와 같이 인터페이스를 클래스에서 사용하려면 implements 인터페이스명을 명시해야 한다. 그러나 단순히 명시만 한다고 사용할 수 있는 것은 아니다. 인터페이스를 상속한 이상 해당 인터페이스 내의 모든 메서드를 사용 여부에 관계없이 오버라이딩 해야한다. IDE를 사용한다면 해당 오류가 자동으로 나오고 클릭 한 번으로 자동으로 모든 메서드를 오버라이딩할 수 있다. (인텔리제이 기준 Alt + Shift + Enter)

package classes;

interface Inter_A {
    int a = 5;
    String b = "Hello";
    char c = 'A';

    void mPrint();
    void mPrint2();
}

public class Interface_Practice implements Inter_A {
    @Override
    public void mPrint() {
        System.out.println("Interface Inter_A's mPrint Override");
    }

    @Override
    public void mPrint2() {
        System.out.println("Interface Inter_A's mPrint2 Override");
    }
    
    public static void main(String[] args) {
        Interface_Practice obj = new Interface_Practice();

        System.out.println(Inter_A.a);
        System.out.println(Inter_A.b);
        System.out.println(Inter_A.c);

        obj.mPrint(); //Interface Inter_A's mPrint Override
        obj.mPrint2(); //Interface Inter_A's mPrint2 Override
    }
}

다음과 같이 모든 메서드를 오버라이딩하는 과정을 거쳐야 한다. 이때 오버라이딩을 통한 메서드를 선언하는 위치는 클래스가 상속받는 만큼 클래스 내 메인 메서드가 아닌 클래스 내부에 오버라이딩을 진행한다. 그리고 오버라이딩한 메서드 내부에 구현 코드를 작성하고 오버라이딩한 메서드를 구현하기 위해 메인 메서드에 클래스를 기반으로 객체를 생성 후 객체를 활용해서 메서드를 구현한다. 또한 여담으로 인터페이스 내 필드는 기본 제어자에 static라는 정적 키워드가 붙는 만큼 별도의 과정없이 인터페이스명.필드명으로 바로 사용할 수 있다. 물론 상수인만큼 값 변경 내지 재할당은 불가능하다.

(추가로 오버라이딩한 메서드의 접근 지정자는 public으로 되어있는데 이를 다른 접근 지정자로 변경할 수 없다.)

이렇게 인터페이스 (Interface)는 오버라이딩을 통한 상속을 활용해서 추상 메서드를 재정의 (Override)하는 방식으로 사용한다는 점을 알 수 있다.

package classes;

interface Inter_A {
    int a = 5;
    String b = "Hello";
    char c = 'A';

    void mPrint();
    void mPrint2();
}

interface Inter_B extends Inter_A {
    int k = 10;
    boolean tf = false;

    void mPrint_B();
}

public class Interface_Practice implements Inter_B {
    @Override
    public void mPrint() {
        System.out.println("Interface Inter_B's mPrint Override");
    }

    @Override
    public void mPrint2() {
        System.out.println("Interface Inter_B's mPrint2 Override");
    }

    @Override
    public void mPrint_B() {
        System.out.println("Interface Inter_B's mPrint_B Override");
    }

    public static void main(String[] args) {
        Interface_Practice obj = new Interface_Practice();

        System.out.println(Inter_B.a);
        System.out.println(Inter_B.b);
        System.out.println(Inter_B.c);
        System.out.println(Inter_B.k);
        System.out.println(Inter_B.tf);

        obj.mPrint();
        obj.mPrint2();
        obj.mPrint_B();
    }
}

다음과 같이 인터페이스끼리 상속이 가능하다. 상속이라는 개념이 클래스에만 국한되는 점이 아닌 점을 확인할 수 있다.

인터페이스 뒤에 extends 키워드를 작성하고 그 뒤에 바로 상속받을 인터페이스명을 입력해주는 방식으로 상속받을 수 있다. 그리고 클래스에서 상속받은 자식 인터페이스를 implements를 통해 상속받을 때는 자식 인터페이스에 부모 인터페이스의 필드와 메서드가 모두 포함되어 있는 만큼 오버라이딩할 때 기존 부모 인터페이스의 메서드와 자식 인터페이스의 메서드를 모두 적용해야 한다. IDE를 통해 자동 완성 기능을 사용하면 2개의 인터페이스 내 메서드가 모두 적용되는 점을 확인할 수 있을 것이다. 이러한 인터페이스 끼리의 상속 외에도 상속 면에서 다른 관계도 설명할 예정이다.

- 인터페이스와 클래스의 상속 관계

상단 이미지는 기본적인 클래스와 인터페이스의 상속 관계를 나타내고 있다. 이 외에도 하나의 클래스가 두 개이상의 인터페이스를 상속받는 방법하나의 클래스가 다른 클래스를 상속받으면서 인터페이스를 상속받는 방법이 존재한다.

이때 인터페이스가 클래스를 상속받는 것은 불가능하다는 점을 반드시 명심해야 한다.

package classes;

interface Inter_A {
    int a = 5;

    void mPrint();
    void mPrint2();
}

interface Inter_B {
    String b = "Hello";

    void mPrint_B();
}

public class Interface_Practice implements Inter_A, Inter_B {
    @Override
    public void mPrint() {
        System.out.println("Interface Inter_A's mPrint Override");
    }

    @Override
    public void mPrint2() {
        System.out.println("Interface Inter_A's mPrint2 Override");
    }

    @Override
    public void mPrint_B() {
        System.out.println("Interface Inter_B's mPrint_B Override");
    }

    public static void main(String[] args) {
        Interface_Practice obj = new Interface_Practice();

        System.out.println(Inter_A.a); //5
        System.out.println(Inter_B.b); //Hello

        obj.mPrint(); //Interface Inter_A's mPrint Override
        obj.mPrint2(); //Interface Inter_A's mPrint2 Override
        obj.mPrint_B(); //Interface Inter_B's mPrint_B Override
    }
}

먼저 하나의 클래스가 여러 인터페이스를 상속받는 예시에 대한 설명부터 시작하겠다. 상단 소스코드처럼 클래스명 뒤에 implements 키워드를 더하고 뒤에 인터페이스명을 작성하는데 소스코드에서도 확인 가능하다시피 ,를 기점으로 두 개 이상의 인터페이스를 상속할 수 있다는 점을 확인할 수 있다. 물론 이 소스코드는 상단에 인터페이스끼리 상속받는 방법과 거의 유사하다고 봐도 무방하다.

package classes;

interface Inter_A {
    int a = 5;

    void mPrint();
    void mPrint2();
}

interface Inter_B {
    String b = "Hello";

    void mPrint_B();
}

class Other_cls {
    int cls_vI01 = 100;

    void cls_mPrint() {
        System.out.println("Other_cls Class's cls_mPrint() Method");
    }
}

public class Interface_Practice extends Other_cls implements Inter_A, Inter_B {
    @Override
    public void mPrint() {
        System.out.println("Interface Inter_A's mPrint Override");
    }

    @Override
    public void mPrint2() {
        System.out.println("Interface Inter_A's mPrint2 Override");
    }

    @Override
    public void mPrint_B() {
        System.out.println("Interface Inter_B's mPrint_B Override");
    }

    @Override
    void cls_mPrint() { // Override 또한 가능하다
        super.cls_mPrint();
        System.out.println("Other_cls Class's cls_mPrint() Method - Override");
    }

    public static void main(String[] args) {
        Interface_Practice obj = new Interface_Practice();

        System.out.println(obj.cls_vI01); //100

        obj.cls_mPrint(); //Other_cls Class's cls_mPrint() Method

        System.out.println(Inter_A.a); //5
        System.out.println(Inter_B.b); //Hello

        obj.mPrint(); //Interface Inter_A's mPrint Override
        obj.mPrint2(); //Interface Inter_A's mPrint2 Override
        obj.mPrint_B(); //Interface Inter_B's mPrint_B Override
    }
}

다음은 클래스와 인터페이스 모두 상속받는 것이 가능하다는 점을 알려주는 소스코드이다. 이때 클래스와 인터페이스 중 무엇을 먼저 상속받을 지에 대한 순서를 지켜야한다는 점이 매우 중요하다. 먼저 클래스명 뒤에 extends를 통해 상속받을 클래스명을 작성하고 그 뒤에 implement를 통해 인터페이스를 상속받아야 한다. 이때 클래스를 상속받고 나서도 2개 이상의 인터페이스를 상속받아도 된다. (물론 하나의 클래스가 두 개 이상의 클래스를 동시에 상속받는 것은 불가능하다.)

이렇게 extends를 통해 두 개 이상의 클래스를 상속받는 것은 불가능하다. 이는 extends 할 클래스에 또 다른 클래스를 상속하는 방식으로 두 개 이상의 클래스를 상속받을 수 있다.

package classes;

class Other_cls {
    int cls_vI01 = 100;

    void cls_mPrint() {
        System.out.println("Other_cls Class's cls_mPrint() Method");
    }
}

class Other_cls2 extends Other_cls {
    void cls2_mPrint() {
        System.out.println("Other_cls2 Class's cls2_mPrint() Method");
    }
}

public class Interface_Practice extends Other_cls2 {
    @Override
    void cls_mPrint() { // Override 또한 가능하다
        super.cls_mPrint();
        System.out.println("Other_cls Class's cls_mPrint() Method - Override");
    }

    public static void main(String[] args) {
        Interface_Practice obj = new Interface_Practice();

        System.out.println(obj.cls_vI01); //100
        obj.cls_mPrint(); //Other_cls Class's cls_mPrint() Method
        obj.cls2_mPrint(); //Other_cls2 Class's cls2_mPrint() Method
    }
}

다음과 같이 상속받을 클래스가 또 다른 클래스를 상속받은 상태라면 extends 뒤에 붙는 클래스명을 통해 여러 클래스를 상속받는 것과 같이 작용해서 상속받은 멤버들을 사용할 수 있다.

 

다음 글은 예외에 대한 설명입니다.

반응형