Java/Java 문법

Java 문법 8 (인터페이스)

열심히 해 2024. 9. 6. 10:20
  • 인터페이스 : 상속 관계가 없는 다른 클래스들이 서로 동일한 행위 즉, 메서드를 구현해야 할 때 인터페이스는 구현 클래스들의 동일한 사용 방법과 행위를 보장한다.
      두 객체를 연결해 주는 다리 역할을 한다.
    • 사람과 삼성티비, 엘지티비 객체가 존재한다고 생각해 보겠습니다.
    • 사람 객체는 멀티 리모컨 인터페이스를 통해서 삼성티비 객체의 채널을 변경할 수 있습니다.
    • 이때 삼성티비가 아니라 엘지티비로 객체가 교체된다고 해도 채널을 변경할 수 있습니다.
    • 인터페이스는 스팩이 정의된 메서드들의 집합입니다.
    • 인터페이스의 구현 클래스들은 반드시 정의된 메서드들을 구현해야 합니다.
    • 따라서 구현 클래스들의 동일한 사용 방법과 행위를 보장해 줄 수 있습니다.
    • 이러한 특징은 인터페이스에 다형성을 적용할 수 있게 만들어 줍니다.


  • interface 키워드를 사용하여 인터페이스를 선언할 수 있습니다.
    public interface 인터페이스명 { }
    인터페이스는 클래스와 마찬가지로 public, default 접근 제어자를 지정할 수 있습니다.

  • 인터페이스의 멤버
    • 모든 멤버 변수는 public static final이어야 합니다.
      • 생략 가능합니다.
    • 모든 메서드는 public abstract이어야 합니다.
      • 생략 가능합니다. (static 메서드와 default 메서드는 구현 부분이 있어야 하기 때문에 예외)
    • 생략되는 제어자는 컴파일러가 자동으로 추가해줍니다.


  • 인터페이스는 추상 클래스와 마찬가지로 직접 인스턴스를 생성할 수 없기 때문에 클래스에 구현되어 생성됩니다.
    • implements 키워드를 사용하여 인터페이스를 구현할 수 있습니다.
    public class 클래스명 implements 인터페이스명 { 
    			// 추상 메서드 오버라이딩
    			@Override
    	    public 리턴타입 메서드이름(매개변수, ...) {
    			       // 실행문
    	    }
    }
    
    • 인터페이스의 추상 메서드는 구현될 때 반드시 오버라이딩 되어야 합니다.
    • 만약 인터페이스의 추상 메서드를 일부만 구현해야 한다면 해당 클래스를 추상 클래스로 변경해 주면 됩니다.


  • 인터페이스 간의 상속이 가능합니다.
    • 인터페이스 간의 상속은 implements 가 아니라 extends 키워드를 사용합니다.
    • 인터페이스는 클래스와는 다르게 다중 상속이 가능합니다.
    public class Main implements C {
    
        @Override
        public void a() {
            System.out.println("A");
        }
    
        @Override
        public void b() {
    				System.out.println("B");
        }
    }
    
    interface A {
        void a();
    }
    interface B {
        void b();
    }
    interface C extends A, B { }
    
    • 인터페이스 C는 아무것도 선언되어 있지 않지만 인터페이스 A, B를 다중 상속받았기 때문에 추상 메서드 a, b를 갖고 있는 상태입니다.
    • 따라서 Main 클래스에서 인터페이스 C가 구현될 때 a, b 추상 메서드가 오버라이딩됩니다.
    • 또한 인터페이스의 구현은 상속과 함께 사용될 수 있습니다.
더보기
package week03.interfaceSample;
// Main은 D를 상속 받고 c와 인터페이스 됨
public class Main extends D implements C {

    @Override
    public void a() {
        System.out.println("A");
    }

    @Override
    public void b() {
        System.out.println("B");
    }

    @Override
    void d() {
        super.d();
    }

    public static void main(String[] args) {
        Main main = new Main();
        main.a();  // extends D 에 의 해 A 출력
        main.b();  // extends D 에 의 해 B 출력
        main.d();  // implements C 에 의해 D 출력
    }
}

interface A {
    void a();
}

interface B {
    void b();
}

interface C extends A, B {
}

class D {
    void d() {
        System.out.println("D");
    }
}

 

 

  • 디폴트 메서드 : 추상 메서드의 기본적인 구현을 제공
    • 메서드 앞에 default 키워드를 붙이며 블럭{ }이 존재해야 합니다.
    • default 메서드 역시 접근 제어자가 public이며 생략이 가능합니다.
    • 추상 메서드가 아니기 때문에 인터페이스의 구현체들에서 필수로 재정의 할 필요는 없습니다.
더보기
public class Main implements A {

    @Override
    public void a() {
        System.out.println("A");
    }


    public static void main(String[] args) {
        Main main = new Main();
        main.a();

        // 생성된 객체에서 , 디폴트 메서드 재정의 없이 바로 사용가능합니다.
        main.aa();
    }
}

interface A {
    void a();
    default void aa() {
        System.out.println("AA");
    }
}

 

  • static 메서드 : 인터페이스에서 static 메서드 선언이 가능
    • static의 특성 그대로 인터페이스의 static 메서드 또한 객체 없이 호출이 가능합니다.
    • 선언하는 방법과 호출하는 방법은 클래스의 static 메서드와 동일합니다.
      • 접근 제어자를 생략하면 컴파일러가 public을 추가해 줍니다.
더보기
public class Main implements A {

    @Override
    public void a() {
        System.out.println("A");
    }

    public static void main(String[] args) {
        Main main = new Main();
        main.a();
        main.aa();
        System.out.println();

        // 인터페이스 A에서 static 메서드 aaa() 호출
        A.aaa();
    }
}

interface A {
    void a();
    default void aa() {
        System.out.println("AA");
    }
    static void aaa() {
        System.out.println("static method");
    }
}

 

  • 다형성 

    1. 자동 타입 변환 : 인터페이스 변수 = 구현객체;
    2. 강제 타입 변환 : 구현객체타입 변수 = (구현객체타입) 변수;
더보기
public class Main {
    public static void main(String[] args) {

        // A 인터페이스에 구현체 B 대입
        A a1 = new B();  // 자동 형변환
        a1.a();
        // a1.b(); // 불가능 , a1은 A타입이기 떄문에 a()메서드만 가지고 있음

        System.out.println("\nB 강제 타입변환");
        B b = (B) a1;  // a1의 타입`A`를 B로 변환
        b.a();
        b.b(); // 강제 타입변환으로 사용 가능
        System.out.println();

        // A 인터페이스에 구현체 B를 상속받은 C 대입
        A a2 = new C();
        a2.a();
        //a2.b(); // 불가능 , 클래스 C가 메서드 a(), b(), c() 전부 가지고 있다고 하더라도
        //a2.c(); // 불가능 , a2은 A타입이기 떄문에 a()메서드만 가지고 있음

        System.out.println("\nC 강제 타입변환");
        C c = (C) a2;  // a2의 타입`A`를 C로 변환
        c.a();
        c.b(); // 강제 타입변환으로 사용 가능
        c.c(); // 강제 타입변환으로 사용 가능


    }
}

interface A {
    void a();
}

class B implements A {
    @Override
    public void a() {
        System.out.println("B.a()");
    }

    public void b() {
        System.out.println("B.b()");
    }
}

class C extends B {
    public void c() {
        System.out.println("C.c()");
    }
}

인터페이스도 마찬가지로 매개변수와 반환 타입에서 다형성이 적용될 수 있습니다.