Study/Java

[Java] 인터페이스 선언 및 구성 멤버

욘아리 2024. 5. 25. 09:46

인터페이스

인터페이스는 두 객체를 연결하는 역할을 한다. 

객체 A가 인터페이스의 메서드를 호출하면, 인터페이스는 객체 B의 메서드를 호출하고 그 결과를 받아 객체 A로 전달해 준다. 

 

그렇다면 직접 호출하지 않고 왜 중간에 인터페이스를 거치도록 하는 것일까?🤔

 

만약 객체 B가 객체 C로 교체된다고 가정해보자.

객체 A는 인터페이스의 메서드만 사용하므로 객체 B가 객체 C로 변경되는 것에 관심이 없다.

인터페이스 없이 객체 A가 객체 B를 직접 사용했다면 객체 A의 소스 코드를 객체 B를 객체 C로 변경하는 작업이 추가로 필요했을 것이다.

 

인터페이스 사용으로 객체 A가 인터페이스의 메서드를 호출하면 실제로 실행되는 것은 인터페이스 뒤편의 객체 B 또는 객체 C의 메서드가 될 것이다.

 

이 특징으로 인해 인터페이스는 다형성 구현에 주된 기술로 이용된다.

상속을 이용해서 다형성을 구현할 수도 있지만, 인터페이스를 이용해서 다형성을 구현하는 경우가 더 많다.

 

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

인터페이스는 '~.java' 형태의 소스 파일로 작성되고 '~.class' 형태로 컴파일되기 때문에 물리적 형태는 클래스와 동일하다.

단, 소스를 작성할 때 선언하는 방법과 구성 멤버가 클래스와 다르다.

 

인터페이스 선언

interface 인터페이스명 {} 	// default 접근 제한
public interface 인터페이스명 {} // public 접근 제한

 

중괄호 안에는 아래와 같이 인터페이스가 가지는 멤버들을 선언할 수 있다. (밑에서 하나씩 설명)

public interface 인터페이스명 {
    //public 상수 필드
    //public 추상 메서드
    //public 디폴트 메서드
    //public 정적 메서드
    //private 메서드
    //private 정적 메서드
}

 

구현클래스 선언

 public class B implements 인터페이스명 {}

 

객체 B는 인터페이스에 선언된 추상 메서드와 동일한 선언부를 가진(재정의된) 메서드를 가지고 있어야 한다.

여기서 객체 B를 인터페이스를 구현한 객체라고 한다. 객체 B에는 추상 메서드에 대한 실행 내용이 구현되어 있기 때문이다.

 

변수 선언과 구현 객체 대입

 

인터페이스도 하나의 타입이므로 변수의 타입으로 사용할 수 있다.

인터페이스를 통해 구현 객체를 사용하려면, 인터페이스 변수에 구현 객체를 대입해야 한다.

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

        rc = new Television();
        rc.turnOn();

        rc = new Audio();
        rc.turnOn();
    }
}

 

상수 필드

인터페이스에 선언된 필드는 모두 public static final 특성을 갖기 때문에 생략하더라도 자동적으로 붙게 된다.

[public static final] 타입 상수명 = 값;

 

상수는 구현 객체와 관련 없는 인터페이스 소속 멤버이므로 인터페이스에 바로 접근 가능하다.

public class RemoteControlExample {
    public static void main(String[] args) {
        System.out.println("리모콘 최대 볼륨: " + RemoteControl.MAX_VOLUME);
        System.out.println("리모콘 최저 볼륨: " + RemoteControl.MIN_VOLUME);
    }
}

 

추상 메서드

추상 메서드는 리턴 타입, 메서드명, 매개변수만 기술되고 중괄호 { }를 붙이지 않는 메서드를 말한다.

[public abstract] 리턴타입 메서드명(매개변수);

 

추상 메서드는 객체 A가 인터페이스를 통해 어떻게 메서드를 호출할 수 있는지 방법을 알려주는 역할을 한다. 

 

디폴트 메서드

추상 메서드는 실행부가 없지만, 디폴트 메서드는 실행부가 있다. 선언 방법은 클래스 메서드와 동일한데, 차이점은 default 키워드가 리턴 타입 앞에 붙는다.

[public] default 리턴타입 메서드명(매개변수) {...};

 

디폴트 메서드의 실행부에는 상수 필드를 읽거나 추상 메서드를 호출하는 코드를 작성할 수 있고, 구현 객체가 필요하다.

 

구현 클래스는 디폴트 메서드를 재정의해서 자신에게 맞게 수정할 수도 있다. 재정의 시 public 접근 제한자를 반드시 붙여야 하고, default 키워드를 생략해야 한다.

// 인터페이스에 있는 디폴트 메서드
default void setMute(boolean mute) {
    if (mute) {
        System.out.println("무음 처리합니다.");
        // 추상 메소드 호출하면서 상수 필드 사용
        setVolume(MIN_VOLUME);
    } else {
        System.out.println("무음 해제합니다.");
    }
}

// 디폴트 메서드 재정의
@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);
    }
}


정적 메서드

추상 메서드와 디폴트 메서드는 구현 객체가 필요하지만, 정적 메서드는 구현 객체가 없어도 인터페이스만으로 호출할 수 있다.

[public | private] static 리턴타입 메서드명(매개변수) {...};

 

정적메서드는 실행부에서 상수 필드를 제외한 구현 객체가 필요한 추상 메서드, 디폴트 메서드, private 메서드 등을 호출할 수 없다.

// 인터페이스에 있는 정적 메서드
static void changeBattery() {
    System.out.println("리모콘 건전지를 교환합니다.");
}

// 정적 메서드 호출
RemoteControl.changeBattery();

 

private 메서드

인터페이스의 상수 필드, 추상 메서드, 디폴트 메서드, 정적 메서드는 모두 public 접근 제한을 갖고, 생략하더라도 컴파일 과정에서 public 접근 제한자가 붙어 항상 외부에서 접근이 가능하다.

또한 인터페이스에 외부에서 접근할 수 없는 private 메서드 선언도 가능하다.

private 메서드는 디폴트 메서드 안에서만 호출이 가능하고, private 정적 메서드는 디폴트 메서드 뿐만 아니라 정적 메서드 안에서도 호출이 가능하다. private 메서드는 디폴트와 정적 메서드들의 중복 코드를 줄이기 위해 사용된다.

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

 

 

출처

신용권, 임경균, 이것이 자바다

'Study > Java' 카테고리의 다른 글

[Java] 인터페이스 타입 변환과 다형성  (0) 2024.06.01
[Java] 다중 인터페이스와 인터페이스의 상속  (0) 2024.05.30
[Java] 상속  (0) 2024.05.09
[Java] 객체와 클래스  (0) 2024.05.06
[Java] 컴파일 과정  (0) 2024.04.29