[Java] 인터페이스

thingzoo·2023년 6월 14일
0

Java

목록 보기
11/20
post-thumbnail

인터페이스

일종의 추상클래스로 추상클래스보다 추상화정도가 높음

  • 실제 구현된것이 전혀없는 기본 설계도
  • 추상메소드와 상수만을 멤버로 가짐
  • 인스턴스 생성 불가

역할

  • 두 객체를 연결해주는 다리 역할
    • 사람과 삼성티비, 엘지티비 객체가 존재한다고 가정
    • 사람 객체는 멀티 리모컨 인터페이스를 통해서 삼성티비 객체의 채널을 변경 가능
    • 이때 삼성티비가 아니라 엘지티비로 객체가 교체되어도 채널 변경 가능
  • 상속 관계가 없는 다른 클래스들이 서로 동일한 행위(메소드)를 구현해야할 때, 인터페이스는 구현 클래스들의 동일한 사용 방법과 행위를 보장
    • 인터페이스는 스팩이 정의된 메서드들의 집합
    • 인터페이스의 구현 클래스들은 반드시 정의된 메서드들 구현
    • 따라서 구현 클래스들의 동일한 사용 방법과 행위를 보장
    • 이러한 특징은 인터페이스에 다형성을 적용할 수 있게 함

선언

interface 키워드로 인터페이스 선언

public interface 인터페이스명 { 

}
  • 인터페이스도 클래스와 마찬가지로 public, default 접근 제어자 지정 가능

구성(멤버)

  • 모든 멤버변수는 public static final 이어야 함
    • 생략 가능
  • 모든 메서드는 public abstract 이어야 함
    • 생략 가능 (static 메서드와 default 메서드 예외)
  • 생략되는 제어자는 컴파일러가 자동으로 추가
public interface 인터페이스명 { 
	public static final char A = 'A';
    static char B = 'B';
    final char C = 'C';
    char D = 'D';

    void turnOn(); // public abstract void turnOn();
}

구현

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

상속

인터페이스간의 상속이 가능

  • 인터페이스는 클래스와는 다르게 다중 상속 가능
  • 인터페이스의 구현은 상속과 함께 사용 가능
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();
        main.b();
        main.d();
    }
}

interface A {
    void a();
}

interface B {
    void b();
}

interface C extends A, B {
}

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

디폴트 메소드와 static 메소드

디폴트 메소드

추상 메서드의 기본적인 구현을 제공하는 메서드

  • 메서드 앞에 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 메서드 선언 가능

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();

        // static 메서드 aaa() 호출
        A.aaa();
    }
}

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

다형성

타입변환

자동 타입변환(up-casting)

인터페이스 변수 = 구현객체;

public class Main {
    public static void main(String[] args) {
        
        // A 인터페이스에 구현체 B 대입
        A a1 = new B();
        
        // A 인터페이스에 구편체 B를 상속받은 C 대입
        A a2 = new C();
        
    }
}

interface A { }
class B implements A {}
class C extends B {}

강제 타입변환(down-casting)

구현객체타입 변수 = (구현객체타입) 인터페이스변수;

public class Main {
    public static void main(String[] args) {

        // A 인터페이스에 구현체 B 대입
        A a1 = new B();
        a1.a();
        // a1.b(); // 불가능

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

        // A 인터페이스에 구편체 B를 상속받은 C 대입
        A a2 = new C();
        a2.a();
        //a2.b(); // 불가능
        //a2.c(); // 불가능

        System.out.println("\nC 강제 타입변환");
        C c = (C) a2;
        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()");
    }
}

인터페이스의 다형성

인터페이스를 사용하여 다형성을 구현해보자

// LG TV 구현체를 조작
MultiRemoteController mrc = new LgTv("LG");
mrc.turnOnOff();
mrc.volumeUp();

// 조작 대상을 Samsung TV로 교체
mrc = new SamsungTv("Samsung");
mrc.turnOnOff();
mrc.channelUp();
  • 멀티리모컨인터페이스 변수 = TV구현객체; 를 선언하여 자동 타입변환된 인터페이스 변수를 사용하여 TV구현객체의 기능 조작 가능
  • TV구현객체를 교체해도 멀티리모컨인터페이스 변수는 전혀 수정작업 없이 그대로 기능 호출 가능
  • 다형성은 ‘여러 가지 형태를 가질 수 있는 능력’
  • 사용 방법은 동일하지만 다양한 특징과 결과를 가질 수 있는 것이 바로 다형성
    • 즉, 멀티리모컨으로 티비를 사용하는 방법은 동일하지만 어떤 TV구현객체가 대입되었느냐에 따라 실행 결과가 다르게 나옴을 통해 다형성이 적용되었음을 확인 가능!
// 매개변수와 반환타입 다형성 확인 메서드
default MultiRemoteController getTV(Tv tv) {
    if(tv instanceof SamsungTv) {
        return (SamsungTv) tv;
    } else if(tv instanceof LgTv){
        return (LgTv) tv;
    } else {
        throw new NullPointerException("일치하는 Tv 없음");
    }
}
  • 또한 인터페이스도 마찬가지로 매개변수와 반환타입에서 다형성 적용
  • 위에서는 반환타입에는 인터페이스, 매개변수에는 추상클래스로 다형성이 적용
    • 인터페이스의 default 메서드

🤷🏻‍♀️ 추상클래스 vs 인터페이스

  • 추상클래스: 미완성 설계도, 상속을 받아 공통된 기능을 제공하기 위함
  • 인터페이스: 기본 설계도, 다중 구현 가능, 상속과 관계없이 공통된 기능

Reference

🔗 스파르타코딩클럽 Java 문법 종합반
🔗 자바의 정석

profile
공부한 내용은 바로바로 기록하자!

0개의 댓글