Java/객체지향프로그래밍

[Java] 인터페이스(interfce) - 코딩밥상

코딩밥상 2023. 2. 11. 16:45

인터페이스란?

 일종의 추상클래스로 추상메서드를 갖지만 추상클래스보다 추상화 정도가 높아서 몸통을 갖춘 일반 메서드 또는 멤버변수를 구성원으로 가질 수 없다. 오직 추상 메서드와 상수만을 멤버로 가질 수 있으며, 그 외의 다른 어떠한 요소도 허용하지 않는다.

 인터페이스도 추상클래스처럼 완성되지 않은 불완전한 것이기 때문에 그 자체만으로 사용되기 보다는 다른 클래스를 작성하는데 도움 줄 목적으로 작성된다.

 

인터페이스의 작성

 클래스를 작성하는 것과 같은데 다만 키워드로 class 대신 interface를 사용한다는 것만 다르다.

그리고 interface에도 접근제어자로 public 또는 default를 사용할 수 있다.

interface 인터페이스이름{
	public static final 타입상수이름 = 값;
        public abstract 메서드이름(매개변수목록);
}

일반적인 클래스의 멤버들과 달리 인터페이스 멤버들은 다음과 같은 제약사항이 있다.

-모든 멤버변수는 public static final이어야 하며, 이를 생략할 수 있다.
-모든 메서드는 public abstract 이어야 하며, 이를 생략할 수 있다. (단, static메서드와 디폴트 메서드는 예외)

모든 멤버에 예외없이 적용되는 사항이기 때문에 제어자를 생략할 수 있는것이다.


인터페이스의 상속

 인터페이스는 인터페이스로부터만 상속받을 수 있으며, 다중상속이 가능하다.

인터페이스도 추상클래스처럼 그 자체로는 인스턴스를 생성할 수 없으며, 자신에 정의된 추상메서드의 몸통을 만들어주는 클래스를 작성해야한다. 이 때 클래스 앞에 키워드 'implements'를 사용한다.

 

 만일 구현하는 인터페이스의 메서드 중 일부만 구현한다면, abstract를 붙여서 추상클래스로 선언해야 한다.

 상속과 구현을 동시에 할 수도 있다.

 

인터페이스를 이용한 다형성

 인터페이스 역시 해당 인터페이스 타입의 참조변수로 이를 구현한 클래스의 인스턴스를 참조할 수 있으며, 인터페이스 타입으로 형변환도 가능하다.

따라서 인터페이스 타입의 매개변수가 갖는 의미는 메서드 호출 시 해당 인터페이스를 구현한 클래스의 인스턴스를 매개변수로 제공하야한다는 것이다.

 

또한 메서드의 리턴타입으로 인터페이스의 타입을 지정하는 것 역시 가능하다.

리턴타입이 인터페이스라는 것은 메서드가 해당 인터페이스를 구현한 클래스의 인스턴스를 반환한다는 것을 의미한다.

 

인터페이스의 장점

-개발 시간을 단축시킬 수 있다.

-표준화가 가능하다.

-서로 관계없는 클래스들에게 관계를 맺어 줄 수 있다.
=> 서로 상속관계나 같은 조상 클래스를 가지고 있지 않은 아무런 관계도 없는 클래스들에게 하나의 인터페이스를 공통적으로 구현하도록 함으로써 관계를 맺어 줄 수 있다.

-독립적인 프로그래밍이 가능하다.
=> 클래스와 클래스간의 직접적인 관계를 인터페이스를 이용해서 간접적인 관계로 변경하면, 한 클래스의 변경이 다른 클래스에 영향을 미치지 않는 독립적인 프로그래밍이 가능하다.

 

인스턴스의 이해

 인터페이스의 규칙이나 활용은 모두 알아봤고, 본질적인 측면에 대해 정리해보자.

다음 두 가지는 자명한 사실이며, 이를 염두에 두고 인터페이스에 대해 이해해보자.

-클래스를 사용하는 쪽(User)과 클래스를 제공하는 쪽(Provider)이 있다.
-메서드를 사용(호출)하는 쪽(User)에서는 사용하려는 메서드(Provider)의 선언부만 알면 된다.(내용은 몰라도 된다.)

 

class A {
	public void methodA(B b){
    	b.method();
    }
}

class B {
	public void methodB(){
    	System.out.println("methodB()");
    }
}

 다음 A,B 두 클래스에서 A(User)는 클래스 B(Provider)의 인스턴스를 생성하고 메서드를 호출한다. 즉 두 클래스는 서로 직접적인 관계에 있다.

이 경우 A를 작성하려면 B가 이미 작성되어 있어야 하고 methodB( )의 선언부가 변경되면, 이를 사용하는 클래스 A도 변경되어야 한다.

 

 그러나 인터페이스를 매개체로 해서 A가 인터페이스를 통해 B와 메서드에 접근하도록 하면, B에 변경사항이 생기거나 B와 같은 기능의 다른 클래스로 대체 되어도 A는 전혀 영향을 받지 않도록 하는 것이 가능하다.

interface I{
	public abstract void methodB();
}

class B implement I{
	public void methodB(){
		System.out.println("methodB in B class");
    }
}

class A{
	public void methodA(I i){
    	i.methodB();
    }
}

 클래스 A를 작성하는데 있어서 클래스 B가 사용되지 않았다는 점에 주목하자. 

이제 A와 B는 직접적인 관계에서 간접적인 관계로 바뀐 것이다.

A는 여전히 B의 메서드를 호출하지만, A는 인터페이스 I하고만 직접적인 관계에 있기 때문에 B의 변경에 영향을 받지 않는다.

 

 따라서 A는 인터페이스를 통해 실제로 사용하는 클래스의 이름을 몰라도 되고 심지어는 실제로 구현된 클래스가 존재하지 않아도 문제되지 않는다. A는 오직 직접적인 관계에 있는 인터페이스 I의 영향만 받는다.

 

디폴트 메서드와 static 메서드

 원래는 인터페이스에 추상 메서드만 선언할 수 있었는데, JDK1.8부터 디폴트 메서드와 static메서드도 추가할 수 있게 되었다.

static메서드는 인스턴스와 관계가 없는 독립적인 메서드이기 때문에 예전부터 인터페이스에 추가하지 못할 이유가 없었다.

 

 인터페이스에 메서드를 추가한다는 것은, 추상 메서드를 추가한다는 것이고, 이 인터페이스를 구현한 기존의 모든 클래스들이 새로 추가된 메서드를 구현해야 한다는 것이다.

이에 디폴트 메서드(default method)기능이 도입되었다. 추상 메서드의 기본적인 구현을 제공하는 메서드로, 추상 메서드가 아니기 때문에 디폴트 메서드가 새로 추가되어도 해당 인터페이스를 구현한 클래스를 변경하지 않아도 된다.

 

 디폴트 메서드는 앞에 키워드 default를 붙이며, 추상 메서드와 달리 일반 메서드처럼 구현부(몸통){ }이 있어야 한다. 접근 제어자가 public이며, 생략가능하다.