인터페이스

📌 Java에서 클래스가 구현해야 하는 메서드들의 목록을 정의하는 일종의 계약입니다.

 

인터페이스 선언

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

 

  • 모든 멤버 변수는 public static final이어야 합니다.
    • 생략 가능합니다.
  • 모든 메서드는 public abstract이어야 합니다.
    • 생략 가능합니다. (static 메서드와 default 메서드 예외)
  • 생략되는 제어자는 컴파일러가 자동으로 추가해줍니다.

 

  • 단순하게 클래스는 설계도, 인터페이스는 장보기 리스트 라고 생각하면 편하다
  • 클래스는 내용과 기능에 대한 부분이 상세히 기록되지만, 인터페이스는 필요한 기능만 제시되어 있고 구현은 안되어 있다.

 

인터페이스 구현

📌 인터페이스는 추상 클래스와 마찬가지로 직접 인스턴스를 생성할 수 없기 때문에 클래스에 구현되어 생성됩니다.

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

 

인터페이스 상속

📌 인터페이스 간의 상속이 가능합니다.

  • 인터페이스 간의 상속은 implements 가 아니라 extends 키워드를 사용합니다.
  • 인터페이스는 클래스와는 다르게 다중 상속이 가능합니다.
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");
    }
}
  • 인터페이스 C는 아무것도 선언되어 있지 않지만 인터페이스 A, B를 다중 상속받았기 때문에 추상 메서드 a, b를 갖고 있는 상태입니다.
  • 따라서 Main 클래스에서 인터페이스 C가 구현될 때 a, b 추상 메서드가 오버라이딩됩니다.
  • 또한 인터페이스의 구현은 상속과 함께 사용될 수 있습니다.

 

  • 뭔가 이쯤 오면 추상 클래스가 생각날건데, 둘이 비슷한 건 맞으나, 사용 목적과 특징이 다르다.
특성 추상 클래스 (Abstract Class) 인터페이스 (Interface)
메서드 구현 메서드 구현이 가능 (일부는 구현되고, 일부는 추상 메서드로) 메서드는 구현 없이 선언만 가능 (Java 8 이후 default 메서드 예외)
상속 방식 단일 상속 (하나의 추상 클래스만 상속 가능) 다중 상속 (여러 개의 인터페이스를 구현 가능)
클래스는 내용이 구현되어 있고 변수나 메소드 명이 같으면
구분이 안되는 일이 있는데 인터페이스는 내용이 없어서
다중 상속이 가능하다.
필드 **상태(필드)**를 가질 수 있으며, 인스턴스 변수로 사용 가능 상수 가질 수 있으며, public static final이어야 함
사용 목적 공통된 기능의 구현과 일부 기능의 재사용을 위해 사용 여러 클래스가 공통된 기능을 갖게 하며, 다형성을 구현하기 위해 사용
인스턴스화 인스턴스화 불가 (추상 클래스 자체로 객체 생성 불가) 인스턴스화 불가 (인터페이스 자체로 객체 생성 불가)

 

 

 

디폴트 메서드와 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 메서드 선언이 가능합니다.

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

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

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

 

 

다형성

 

자동 타입 변환

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 {}

 

강제 타입 변환

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 메서드입니다.
  • 전체 예제를 통해 더 자세하게 확인해 보겠습니다.
public abstract class Tv {

    private String company; // 티비 회사
    private int channel = 1; // 현재 채널 상태
    private int volume = 0;  // 현재 볼륨 상태
    private boolean power = false; // 현재 전원 상태

    public Tv(String company) {
        this.company = company;
    }

    public void displayPower(String company, boolean power) {
        if(power) {
            System.out.println(company + " Tv 전원이 켜졌습니다.");
        } else {
            System.out.println(company + " Tv 전원이 종료되었습니다.");
        }
    }

    public void displayChannel(int channel) {
        System.out.println("현재 채널은 " + channel);
    }

    public void displayVolume(int volume) {
        System.out.println("현재 볼륨은 " + volume);
    }

    public String getCompany() {
        return company;
    }

    public int getChannel() {
        return channel;
    }

    public int getVolume() {
        return volume;
    }

    public boolean isPower() {
        return power;
    }

    public void setChannel(int channel) {
        this.channel = Math.max(channel, 0);
    }

    public void setVolume(int volume) {
        this.volume = Math.max(volume, 0);
    }

    public void setPower(boolean power) {
        this.power = power;
    }
}

public class SamsungTv extends Tv implements MultiRemoteController{

    public SamsungTv(String company) {
        super(company);
    }

    @Override
    public void turnOnOff() {
        setPower(!isPower());
        displayPower(getCompany(), isPower());
    }

    @Override
    public void channelUp() {
        setChannel(getChannel() + 1);
        displayChannel(getChannel());
    }

    @Override
    public void channelDown() {
        setChannel(getChannel() - 1);
        displayChannel(getChannel());
    }

    @Override
    public void volumeUp() {
        setVolume(getVolume() + 1);
        displayVolume(getVolume());
    }

    @Override
    public void volumeDown() {
        setVolume(getVolume() - 1);
        displayVolume(getVolume());
    }
}

public class LgTv extends Tv implements MultiRemoteController {

    public LgTv(String company) {
        super(company);
    }

    @Override
    public void turnOnOff() {
        setPower(!isPower());
        displayPower(getCompany(), isPower());
    }

    @Override
    public void channelUp() {
        setChannel(getChannel() + 1);
        displayChannel(getChannel());
    }

    @Override
    public void channelDown() {
        setChannel(getChannel() - 1);
        displayChannel(getChannel());
    }

    @Override
    public void volumeUp() {
        setVolume(getVolume() + 1);
        displayVolume(getVolume());
    }

    @Override
    public void volumeDown() {
        setVolume(getVolume() - 1);
        displayVolume(getVolume());
    }
}

public interface MultiRemoteController {

    void turnOnOff();
    void channelUp();
    void channelDown();
    void volumeUp();
    void volumeDown();

    // 매개변수와 반환타입 다형성 확인 메서드
    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 없음");
        }
    }

}

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

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

        // 조작 대상을 Samsung TV로 교체
        System.out.println("\n<Samsung TV로 교체>");
        mrc = new SamsungTv("Samsung");
        mrc.turnOnOff();
        mrc.channelUp();
        mrc.volumeDown();
        mrc.volumeUp();
        mrc.turnOnOff();

        // 매개변수, 반환타입 다형성 체크
        System.out.println("\n<매개변수, 반환타입 다형성 체크>");

        MultiRemoteController samsung = mrc.getTV(new SamsungTv("Samsung"));
        samsung.turnOnOff();

        SamsungTv samsungTv = (SamsungTv) samsung;
        samsungTv.turnOnOff();


        System.out.println();
        MultiRemoteController lg = mrc.getTV(new LgTv("LG"));
        lg.turnOnOff();

        LgTv lgTv = (LgTv) lg;
        lgTv.turnOnOff();

    }
}

'Back-End (Web) > JAVA' 카테고리의 다른 글

[JAVA] Generic  (3) 2024.11.12
[JAVA] 오류 및 예외에 대한 이해  (1) 2024.11.12
[JAVA] 클래스 간의 관계와 상속  (0) 2024.11.12
[JAVA] package와 import  (0) 2024.11.12
[JAVA] 접근 제어자  (1) 2024.11.12

+ Recent posts