Language/JAVA

[JAVA] 인터페이스

IT수정 2024. 9. 4. 21:24

인터페이스(Interface)는 사전적인 의미로 두 장치를 연결하는 접속기를 말한다. 여기서 두 장치를 서로 다른 객체로 본다면, 인터페이스는 이 두 객체를 연결하는 역할을 한다. 두 객체를 직접 호출하면 간단할 텐데 왜 중간에 인터페이스를 거치도록 하는 것일까? 예를 들어 객체 A와 객체 B가 있다고 가정해 보자. 객체 B가 객체 C로 교체됐을 때 직접 호출한 상태였다면 객체 A의 소스 코드를 객체 B에서 객체 C로 변경하는 작업이 추가로 필요할 것이다. 하지만 인터페이스의 메서드로 호출한다면 교체된다 해도 소스 코드를 고칠 필요가 없다. 객체 A는 인터페이스의 메서드만 사용하므로 B와 C가 변경되는 것에 관심이 없기 때문이다.

 

이 특징으로 인해 인터페이스는 다형성 구현에 주된 기술로 이용된다. 상속을 이용해서 다형성을 구현할 수 있지만, 인터페이스를 이용해서 다형성을 구현하는 경우가 더 많다.

 

인터페이스 선언

인터페이스 선언은 class 키워드 대신 interface 키워드를 사용한다.

interface 인터페이스명 {...}

 

구현 클래스 선언

객체 A가 인터페이스의 추상 메서드를 호출하면 인터페이스는 객체 B의 메서드를 실행한다. 그렇다면 객체 B는 인터페이스에 선언된 추상 메서드와 동일한 선언부를 가진(재정의된) 메서드를 가지고 있어야 한다. 여기서 객체 B를 구현한(implement) 객체라고 한다. 인터페이스에 정의된 추상 메서드에 대한 실행 내용이 구현(작성)되어 있기 때문이다. 객체 B와 같은 구현 객체는 다음과 같이 인터페이스를 구현하고 있음을 선언부에 명시해야 한다.

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

 

implements 키워드는 해당 클래스가 인터페이스를 통해 사용할 수 있다는 표시이며, 인터페이스의 추상 메서드를 재정의한 메서드가 있다는 뜻이다.

 

변수 선언과 구현 객체 대입

인터페이스도 하나의 타입이므로 변수의 타입으로 사용할 수 있다. 인터페이스는 참조 타입에 속하므로 null값을 대입할 수 있다.

RemoteControl rc;
RemoteControl rc = null;

 

인터페이스를 통해 구현 객체를 사용하려면, 인터페이스 변수에 구현 객체를 대입해야 한다. 정확히 말하면 구현 객체의 번지를 대입해야 한다.

public interface RemoteControl {
	public void turnOn();
    }
public class Television implements RemoteControl {
	@Override
    public void turnOn() {
    	System.out.println("TV를 켭니다");
        }
     }
public class RemoteControlExample {
	public static void main(String[] args) {
    	RemoteControl rc;
        rc = new Television;
        rc.turnOn();
        }
     }

 

상수 필드

인터페이스는 public static final 특성을 갖는 불변의 상수 필드를 멤버로 가질 수 있다. 인터페이스에 선언된 필드는 모두 public static final 특성을 갖기 때문에 public static final을 생략하더라도 자동적으로 컴파일 과정에서 붙게 된다. 상수명은 대문자로 작성하되, 서로 다른 단어로 구성되어 있을 경우에는 언더바(_)로 연결하는 것이 관례이다.

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

 

추상 메서드

인터페이스는 구현 클래스가 재정의해야 하는 추상 메서드를 멤버로 가질 수 있다. 추상 메서드는 리턴 타입, 메서드명, 매개변수만 기술되고 중괄호 {}를 붙이지 않는 메서드를 말한다. public abstract 를 생략하더라도 컴파일 과정에서 자동으로 붙게 된다.

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

 

구현 클래스에서 추상 메서드를 재정의할 때 주의할 점은 인터페이스의 추상 메서드는 기본적으로 public 접근 제한을 갖기 때문에 public 보다 더 낮은 접근 제한으로 재정의 할 수 없다. 그래서 재정의 되는 메서드에는 모두 public이 추가되어 있다.

 

디폴트 메서드

인터페이스에는 완전한 실행 코드를 가진 디폴트 메서드를 선언할 수 있다. 추상 메서드는 실행부(중괄호 {})가 없지만, 디폴트 메서드는 실행부가 있다. 선언 방법은 클래스 메서드와 동일한데, 차이점은 default 키워드가 리턴 타입 앞에 붙는다.

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

 

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

 

정적 메서드

인터페이스에는 정적 메서드도 선언이 가능하다. 추상 메서드와 디폴트 메서드는 구현 객체가 필요하지만, 정적 메서드는 구현 객체가 없어도 인터페이스만으로 호출할 수 있다. 선언 방법은 클래스 정적 메서드와 동일하다. 단, public을 생략하더라도 자동으로 컴파일 과정에서 붙는 것이 차이점이다.

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

 

정적 메서드의 실행부(중괄호 {})를 작성할 때 주의할 점은 상수 필드를 제외한 추상 메서드, 디폴트 메서드, private 메서드 등을 호출할 수 없다는 것이다. 이 메서드는 구현 객체가 필요한 메서드이기 때문이다.

 

private 메서드

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

 

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

 

다중 인터페이스 구현

구현 객체는 여러 개의 인터페이스를 implements 할 수 있다. 구현 객체가 인터페이스 A와 인터페이스 B를 구현하고 있다면, 각각의 인터페이스를 통해 구현 객체를 사용할 수 있다. 구현 클래스는 다음과 같이 쉼표로 구분해서 작성해, 모든 인터페이스가 가진 추상 메서드를 재정의 해야 한다.

public class 구현클래스명 implements 인터페이스A, 인터페이스B { ... }

 

인터페이스 상속

인터페이스도 다른 인터페이스를 상속할 수 있으며, 클래스와는 달리 다중 상속을 허용한다. 다음과 같이 extends 키워드 뒤에 상속할 인터페이스들을 나열하면 된다.

public interface 자식인터페이스 extends 부모인터페이스1, 부모인터페이스2 {...}

 

자식 인터페이스의 구현 클래스는 자식 인터페이스의 추상 메서드 뿐만 아니라 부모 인터페이스의 모든 추상 메서드를 재정의 해야 한다. 또한 구현 객체가 자식 인터페이스 변수에 대입되면 자식 및 부모 인터페이스의 추상 메서드를 모두 호출할 수 있으나, 부모 인터페이스 변수에 대입되면 해당 부모 인터페이스에 선언된 추상 메서드만 호출 가능하다.

 

타입 변환

인터페이스의 타입 변환은 인터페이스와 구현 클래스 간에 발생한다. 인터페이스 변수에 구현 객체를 대입하면 구현 객체는 인터페이스 타입으로 자동 타입 변환된다. 반대로 인터페이스 타입을 구현 클래스 타입으로 변환시킬 수 있는데, 이때는 강제 타입 변환이 필요하다.

 

자동 타입 변환

부모 클래스가 인터페이스를 구현하고 있다면 자식 클래스도 인터페이스 타입으로 자동 타입 변환 될 수 있다.

 

강제 타입 변환

구현 객체가 인터페이스 타입으로 자동 타입 변환 되면, 인터페이스에 선언된 메서드만 사용 가능하다. 구현 클래스의 메서드를 호출 하고 싶다면 캐스팅 연산자를 이용해 강제 타입 변환을 해야 한다.

 

봉인된 인터페이스

Java 15 부터는 무분별한 자식 인터페이스 생성을 방지하기 위해 봉인된(sealed) 인터페이스를 사용할 수 있다.

public sealed interface InterfaceA permits InterfaceB {...}

 

sealed 키워드를 사용하면 permits 뒤에 상속 가능한 자식 인터페이스를 지정해야 한다. 봉인된 InterfaceA를 상속하는 InterfaceB는 non-sealed 키워드로 다음과 같이 선언하거나, sealed 키워드를 사용해서 또 다른 봉인 인터페이스로 선언해야 한다.

public non-sealed interface InterfaceB extends InterfaceA {...}

 

non-sealed 는 봉인을 해제한다는 뜻이다. 따라서 InterfaceB는 다른 자식 인터페이스를 만들 수 있다.

'Language > JAVA' 카테고리의 다른 글

[JAVA] 예외 처리  (0) 2024.09.05
[JAVA] 중첩 클래스  (1) 2024.09.04
[JAVA] 봉인된 클래스  (0) 2024.09.04
[JAVA] 추상 클래스  (1) 2024.09.04
[JAVA] 상속2  (0) 2024.09.04