[이것이자바다] Chapter 08. 인터페이스

kims·2023년 12월 18일
0

이것이자바다

목록 보기
8/9
post-thumbnail

8.1 인터페이스 역할

  • 인터페이스는 두 객체를 연결하는 역할을 한다.
  • 다형성1 구현에 주된 기술로 이용된다.
1: 사용 방법은 동일하지만 실행 결과가 다양하게 나오는 성질

8.2 인터페이스와 구현 클래스 선언

인터페이스 선언

  • class 키워드 대신 interface 키워드 사용
  • 접근제한자로는 default, public을 붙일 수 있다.
package ch08.sec02;

public interface RemoteControl {
    
    public void turnOn();
}

구현 클래스 선언

  • implements 키워드를 사용해 인터페이스를 구현한다.
package ch08.sec02;

public class Television implements RemoteControl{

    @Override
    public void turnOn() {
        System.out.println("Tv를 켜다.");
    }
}

변수 선언과 구현 객체 대입

  • 인터페이스 벽수는 객체를 참조하고 있지 않다는 뜻으로 null을 대입할 수 있다.
  • 구현 객체를 사용하려면, 인터페이스 변수에 구현 객체를 대입해야 한다.
    즉, 구현 객체의 번지를 대입해야 한다.
package ch08.sec02;

public class RemoteControlEx {

    public static void main(String[] args) {
        RemoteControl remoteControl = null;
        remoteControl = new Television();
        remoteControl.turnOn(); // Tv를 켜다.
    }
}

8.3 상수 필드

  • 인터페이스에 선언된 필드는 모두 public static final 특성을 갖는다.
    즉, 불변의 상수 필드로 대문자로 작성하되, 단어 간 연결은 언더바(_)로 작성하는 것이 관례이다.
package ch08.sec02;

public interface RemoteControl {

	// 상수 필드
    int MAX_VOLUME = 10;
    int MIN_VOLUME = 0;

	// 추상 메서드
    public void turnOn();
}


package ch08.sec02;

public class RemoteControlEx {

    public static void main(String[] args) {
        System.out.println(RemoteControl.MAX_VOLUME);   // 10
        System.out.println(RemoteControl.MIN_VOLUME);   // 0
    }
}

8.4 추상 메서드

  • 인터페이스는 public 추상 메서드2를 멤버로 가질 수 있다.
2: 리턴 타입, 메서드명, 매개변수만 기술되고 중괄호{}를 붙이지 않는 메서드
  • 인터페이스 구현 클래스에서는 모든 추상 메서드를 재정의해야 한다.

8.5 디폴트 메서드

  • 완전한 실행 코드를 가진 메서드로 default 키워드가 리턴 타입 앞에 붙는다.
  • 디폴트 메서드의 실행부에서는 상수 필드를 읽거나 추상 메서드를 호출하는 코드를 작성할 수 있다.
package ch08.sec02;

public interface RemoteControl {

    int MAX_VOLUME = 10;
    int MIN_VOLUME = 0;

    void turnOn();

    void setVolume(int volume);

    default void setMute(boolean mute) {
        if (mute) {
            System.out.println("무음 처리");
            setVolume(MIN_VOLUME);
        } else {
            System.out.println("무음 해제");
        }
    }

    static void changeBattery() {
        // setMute();  // 컴파일 에러 : Non-static method 'setMute(boolean)' cannot be referenced from a static context
        System.out.println("리모콘 건전지를 교환합니다.");
    }
}


  • 구현 클래스는 디폴트 메서드를 재정의할 수 있다.
    단, public 접근 제한자를 반드시 붙여야 하고, default 키워드를 생략해야 한다.
package ch08.sec02;

public class Television implements RemoteControl{

    private int volume;
    private int memoryVolume;

    @Override
    public void turnOn() {
        System.out.println("Tv를 켜다.");
    }

    @Override
    public void setVolume(int volume) {
        if (volume > RemoteControl.MAX_VOLUME) {
            this.volume = RemoteControl.MAX_VOLUME;
        } else if (volume < RemoteControl.MIN_VOLUME) {
            this.volume = RemoteControl.MIN_VOLUME;
        } else {
            this.volume = volume;
        }

        System.out.println("현재 볼륨: " + volume);
    }

    @Override
    public void setMute(boolean mute) {
        if (mute) {
            this.memoryVolume = this.volume;
            System.out.println("무음 처리합니다.");
            setVolume(RemoteControl.MIN_VOLUME);
        } else {
            System.out.println("무음 해제합니다.");
            setVolume(this.memoryVolume);
        }
    }
}

package ch08.sec02;

public class Audio implements RemoteControl{

    private int volume;

    @Override
    public void turnOn() {
        System.out.println("오디오를 켜다.");
    }

    @Override
    public void setVolume(int volume) {
        if (volume > RemoteControl.MAX_VOLUME) {
            this.volume = RemoteControl.MAX_VOLUME;
        } else if (volume < RemoteControl.MIN_VOLUME) {
            this.volume = RemoteControl.MIN_VOLUME;
        } else {
            this.volume = volume;
        }

        System.out.println("현재 볼륨: " + volume);
    }
}

package ch08.sec02;

public class RemoteControlEx {

    public static void main(String[] args) {
        RemoteControl rc;

        rc = new Audio();
        rc.setMute(true);   // 무음 처리 현재 볼륨: 0
        rc.setMute(false);  // 무음 해제

        rc = new Television();
        rc.setVolume(7);    // 현재 볼륨: 7
        rc.setMute(true);   // 무음 처리합니다. 현재 볼륨: 0
        rc.setMute(false);  // 무음 해제합니다. 현재 볼륨: 7

        RemoteControl.changeBattery();  // 리모콘 건전지를 교환합니다.
    }
}

8.6 정적 메서드

  • 정적 메서드는 구현 객체가 없어도 인터페이스만으로 호출할 수 있다.
  • 정적 메서드 실행부에는 상수 필드를 제외한 추상 메서드, 디폴트 메서드, private 메서드 등을 호출할 수 없다.

8.7 private 메서드

구분설명
private 메서드구현 객체가 필요한 메서드
privae 정적 메서드구현 객체가 필요 없는 메서드

1) private 메서드

  • default 메서드에서만 호출 가능

2) private 정적 메서드

  • default 메서드뿐 아니라 정적 메서드 안에서도 호출 가능
package ch08.sec07;

public interface Service {

    default void defaultMethod() {
        System.out.println("default void defaultMethod() --- ");
        privateMethod();
        privateStaticMethod();
        System.out.println("--- default void defaultMethod()");
    }

    private void privateMethod() {
        System.out.println("private void privateMethod() Call!!");
    }

    static void staticMethod() {
        System.out.println("static void staticMethod() ---");
        privateStaticMethod();
        // privateMethod();    // 컴파일 에러 :Non-static method 'privateMethod()' cannot be referenced from a static context
        System.out.println("--- static void staticMethod()");
    }

    private static void privateStaticMethod() {
        System.out.println("private static void privateStaticMethod() Call!");
    }
}


package ch08.sec07;

public class ServiceImpl implements Service{

}

package ch08.sec07;

public class ServiceEx {
    public static void main(String[] args) {
        Service service = new ServiceImpl();
        service.defaultMethod();
        /*
            default void defaultMethod() ---
            private void privateMethod() Call!!
            private static void privateStaticMethod() Call!
            --- default void defaultMethod()
        */

        Service.staticMethod();
        /*
            static void staticMethod() ---
            private static void privateStaticMethod() Call!
            --- static void staticMethod()
        */
    }
}

8.8 다중 인터페이스 구현

  • 구현 객체는 implements 키워드를 사용해 여러 개의 인터페이스를 구현할 수 있다.
package ch08.sec08;

public interface RemoteControl {

    void turnOn();
    void turnOff();
}

package ch08.sec08;

public interface Searchable {
    void search(String url);
}

package ch08.sec08;

public class SmartTelevision implements RemoteControl, Searchable{
    @Override
    public void turnOn() {
        System.out.println("Tv를 켜다");
    }

    @Override
    public void turnOff() {
        System.out.println("Tv를 끄다");
    }

    @Override
    public void search(String url) {
        System.out.println(url + "을 검색하다");
    }
}

package ch08.sec08;

public class MultiInterfaceImplEx {

    public static void main(String[] args) {
        RemoteControl rc = new SmartTelevision();
        rc.turnOn();    // Tv를 켜다
        rc.turnOff();   // Tv를 끄다

        Searchable searchable = new SmartTelevision();
        searchable.search("https://velog.io/");     // https://velog.io/을 검색하다
    }
}

8.9 인터페이스 상속

  • 인터페이스도 다른 인터페이스를 상속할 수 있으며, 클래스와 달리 다중 상속을 허용한다.
package ch08.sec09;

public interface InterfaceC extends InterfaceA, InterfaceB{

    void methodC();
}


package ch08.sec09;

public class InterfaceCImpl implements InterfaceC{

    @Override
    public void methodA() {
        System.out.println("InterfaceCImpl - methodA() Call!");
    }

    @Override
    public void methodB() {
        System.out.println("InterfaceCImpl - methodB() Call!");
    }

    @Override
    public void methodC() {
        System.out.println("InterfaceCImpl - methodC() Call!");
    }
}

package ch08.sec09;

public class ExtendsEx {

    public static void main(String[] args) {
        InterfaceCImpl impl = new InterfaceCImpl();

        InterfaceA ia = impl;
        ia.methodA();   // InterfaceCImpl - methodA() Call!

        InterfaceB ib = impl;
        ib.methodB();   // InterfaceCImpl - methodB() Call!

        InterfaceC ic = impl;
        ic.methodC();   // InterfaceCImpl - methodC() Call!
    }
}

8.10 타입 변환

  • 인터페이스의 타입 변환은 인터페이스와 구현 클래스 간에 발생한다.

자동 타입 변환

  • 부모 클래스가 인터페이스를 구현하고 있다면 자식 클래스도 인터페이스 타입으로 자동 타입 변환될 수 있다.
// A.java
public interface A {
}

// B.java
public class B implements A{
}

// C.java
public class C extends B{
}

public class PromotionEx {
    public static void main(String[] args) {
        B b = new B();
        C c = new C();

        A a;
        a = b;
        a = c;
    }
}

강제 타입 변환

  • 인터페이스 타입을 구현 클래스 타입으로 변환시킨다.
package sec10.exam02;

public class CatingEx {

    public static void main(String[] args) {
        Vehicle vehicle = new Bus();
        vehicle.run();      // 버스가 달린다.
        // vehicle.checkFar();  // 컴파일 에러 : cannot find symbol
        
        // 강제 타입 변환
        Bus bus = (Bus) vehicle;
        bus.run();          // 버스가 달린다.
        bus.checkFare();    // 승차 요금을 확인한다.
    }

}

8.11 다형성

필드의 다형성

  • 필드 타입으로 인터페이스를 선언하면 필드값으로 다양한 구현 객체를 대입할 수 있다.(자동 타입 변환)
// Tire.java
public interface Tire {

    void roll();
}

// HankookTire.java
public class HankookTire implements Tire{

    @Override
    public void roll() {
        System.out.println("한국 타이어가 굴러갑니다.");
    }
}

// KumhoTire.java
public class KumhoTire implements Tire {

    @Override
    public void roll() {
        System.out.println("금호 타이어가 굴러갑니다.");
    }
}

// Car.java
public class Car {

	// 필드
    Tire  tire = new HankookTire();
    
    void run() {
        tire.roll();
    }
}

public class CarEx {

    public static void main(String[] args) {
        Car car = new Car();
        car.run();  // 한국 타이어가 굴러갑니다.

        car.tire = new KumhoTire();
        car.run();  // 금호 타이어가 굴러갑니다.
    }
}

매개변수의 다형성

  • 매개변수 타입을 인터페이스로 선언하면 메서드 호출 시 다양한 구현 객체를 대입할 수 있다. (자동 타입 변환)
// Vehicle.java
public interface Vehicle {

    void run();
}

// Bus.java
public class Bus implements Vehicle{

    @Override
    public void run() {
        System.out.println("버스가 달린다.");
    }

    public void checkFare() {
        System.out.println("승차요금을 확인한다.");
    }
}

// Taxi.java
public class Taxi implements Vehicle{
    @Override
    public void run() {
        System.out.println("택시가 달린다.");
    }
}

// Driver.java
public class Driver {

    void drive(Vehicle vehicle) {
        vehicle.run();
    }
}

public class DriverEx {

    public static void main(String[] args) {
        Driver driver = new Driver();
        driver.drive(new Bus());    // 버스가 달린다.
        driver.drive(new Taxi());   // 택시가 달린다.
    }
}

8.12 객체 타입 확인

  • instanceof 연산자로 객체 타입을 확인한다.
public class DriverEx {

    public static void main(String[] args) {
        Driver driver = new Driver();

        ride(new Bus());    // 승차요금을 확인한다.
    }

    public static void ride(Vehicle vehicle) {
        if (vehicle instanceof Bus) {
            Bus bus = (Bus) vehicle;
            bus.checkFare();
        }

        /* Java 12부터 사용 가능
        if (vehicle instanceof Bus bus) {
            bus.checkFare();
        }
        */
    }
}

8.13 봉인된 인터페이스

  • Java 15부터 도입된 봉인된(sealed) 인터페이스는 무분별한 자식 인터페이스 생성을 방지한다.
  • sealed 키워드를 사용하면 permits 키워드 뒤에 상속 가능한 자식 인터페이스를 지정해야 한다.
  • 자식 인터페이스는 non-sealed, selaed 중 하나의 유형으로 선언되어야 한다.
  • non-sealed 키워드는 봉인을 해제한다는 뜻으로, 자식 인터페이스를 만들 수 있다.

// InterfaceA.java
public sealed interface InterfaceA permits InterfaceB {
    void methodA();
}

// InterfaceB.java
public non-sealed interface InterfaceB extends InterfaceA {
    void methodB();
}

// InterfaceC.java
public interface InterfaceC extends InterfaceB {
    void methodC();
}

//	ImplClass.java
public class ImplClass implements InterfaceC {
    @Override
    public void methodA() {
        System.out.println("methodA() Call!!");
    }

    @Override
    public void methodB() {
        System.out.println("methodB() Call!!");
    }

    @Override
    public void methodC() {
        System.out.println("methodC() Call!!");
    }
}

public class SealedExample {

    public static void main(String[] args) {
        ImplClass implClass = new ImplClass();

        InterfaceA interfaceA = implClass;
        interfaceA.methodA();   // methodA() Call!!

        InterfaceB interfaceB = implClass;
        interfaceB.methodA();   // methodA() Call!!
        interfaceB.methodB();   // methodB() Call!!

        InterfaceC interfaceC = implClass;
        interfaceC.methodA();   // methodA() Call!!
        interfaceC.methodB();   // methodB() Call!!
        interfaceC.methodC();   // methodC() Call!!
    }
}
profile
기술로 세상을 이롭게

0개의 댓글