[자바의 정석] 인터페이스

June·2021년 1월 3일
0

자바

목록 보기
20/36

인터페이스란?

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

interface 인터페이스이름 {
    public static final 타입 상수이름 =;
    public abstract 메서드이름(매개변수목록);
}
  • 모든 멤버변수는 public static final 이어야 하며, 이를 생략할 수 있다.
  • 모든 메서드는 public abstract 이어야 하며, 이를 생략할 수 있다.
    단, static메서드와 디폴트 메서드는 에외 (JDK1.8부터)

인터페이스는 인터페이스로부터만 상속받을 수 있으며, 클래스와는 달리 다중상속, 즉 여러 개의 인터페이스로부터 상속을 받는 것이 가능하다.

원래는 인터페이스의 모든 메서드는 추상메서드여야 하는데, JDK1.8부터 인터페이스에서 static 메서드와 디폴트 메서드의 추가를 허용하는 방향으로 변경되었다.

인터페이스의 구현

인터페이스도 추상클래스처럼 그 자체로는 인스턴스를 생성할 수 없으며, 추상클래스가 상속을 통해 추상메서드를 완성하는 것과 비슷하다. 다만 인터페이스는 구현한다는 의미의 키워드 'implements'를 사용한다.

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

class 클래스이름 implements 인터페이스이름 {
    //인터페이스에 정의된 추상메서드를 구현해야 한다.
}
class Fighter implements Fightable {
    public void move(int x, int y) { }
    public void attack(Unit u) { }
}   

참고로 인터페이스의 이름에는 주로 Fightable과 같이 able로 끝나는 것들이 많은데, 그 이유는 어떠한 기능 또는 행위를 하는데 필요한 메서드를 제공한다는 의미를 강조하기 위해서이다.

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

다형성에서 자손클래스의 인스턴스를 조상타입의 참조변수로 참조하는 것이 가능하다는 것을 배웠다.
인터페이스 역시 이를 구현한 클래스의 조상이라 할 수 있으므로 해당 인터페이스 타입의 참조변수로 이를 구현한 클래스의 인스턴스를 참조할 수 있으며, 인터페이스 타입으로 형변환도 가능하다.

인터페이스 Fightable을 클래스 Fighter가 구현했을 때, 다음과 같이 Fighter 인스턴스를 Fightable타입의 참조변수로 참조하는 것이 가능하다.

Fightable f = (Fightable) new Fighter();
또는
Fightable f = new Fighter();

따라서 인터페이스는 다음과 같이 메서드의 매개변수 타입으로 사용될 수 있다.

void attack(Fightable f) {
	//...
}

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

그리고 다음과 같이 메서드의 리턴타입으로 인터페이스의 타입을 지정하는 것 역시 가능하다.

Fightable method() {
    ...
    Fighter f = new Fighter();
    return f;
}

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

인터페이스의 이해

  • 클래스를 사용하는 쪽(User)과 클래스를 제공하는 쪽(Provider)이 있다.
  • 메서드를 사용(호출)하는 쪽(User)에서는 사용하려는 메서드(Provider)의 선언부만 알면 된다.
class A {
    public void mehotdA(B b) {
        b.methodB();
    }
}
class B {
    public void methodB() {
        System.out.println("methodB()");
    }
}
class IntefaceTest {
    public static void main(String[] args) {
        A a = new A();
        a.methodA(new B());
    
    }
}

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

이와 같이 직접적인 관곙의 두 클래스는 한 쪽(Provider)이 변경되면 다른 한쪽(User)도 변경되어야 한다는 단점이 있다.

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

interface I {
    public abstract void methodB();
}

class B implements I {
    public void methodB() {
        System.out.println("methodB in B class");
    }
}
 class A  {
     public void mehotdA(I i ) {
         i.methodB();
     }
 }

클래스 A를 작성하는데 있어서 클래스 B가 사용되지 않았다는 점에 주목하자. 'A-B'의 직접적인 관계에서 'A-I-B'의 간접적인 관계로 바뀐것이다.

디폴트 메서드

원래는 추상 메서드만 선언할 수 있는데, JDK1.8부터 디폴트 메서드static 메서드도 추가할 수 있게 되었다.
static 메서드는 인스턴스와 관계없는 독립적인 메서드이기 때문에 예전부터 인터페이스에 추가하지 못할 이유가 없었다. 그러나 인터페이스의 모든 메서드는 추상 메서드이어야 한다는 규칙에 예외를 두지 않았다.

그래서 java.util.Collection과 관련된 static 메서드들이 인터페이스에는 추상 메서드만 선언할 수 있다는 원칙 때문에 별도의 클래스, Collecitons라는 클래스에 들어가게 되었다. 만약 인터페이스에 static 메서드를 추가할 수 있었다면, Collections 클래스는 존재하지 않았을 것이다. 인터페이스의 static 메서드 역시 접근 제어자가 항상 public이며, 생략할 수 있다.

디폴트 메서드
조상 클래스에 새로운 메서드를 추가하는 것은 별 일이 아니지만, 인터페이스의 경우에는 보통 큰 일이 아니다. 인터페이스에 메서드를 추가한다는 것은, 추상 메서드를 추가한다는 것이고, 이 인터페이스를 구현한 기존의 모든 클래스들이 새로 추가된 메서드를 구현해야하기 때문이다.

JDK의 설계자들은 고심 끝에 디폴트 메서드라는 것을 고안해 내었다. 디폴트 메서드는 추상 메서드의 기본적인 구현을 제공하는 메서드로, 추상메서드가 아니기 때문에 디폴트 메서드가 추가되어도 해당 인터페이스를 구현한 클래스를 변경하지 않아도 된다.

디폴트 메서드 역시 접근 제어자가 public이며, 생략가능하다.

Do it 자바

만약 이미 인터페이스에 구현되어 있는 디폴트 메서드가 새로 생성한 클래스에서 원하는 기능과 맞지 않는다면, 하위 클래스에서 디폴트 메서드를 재정의할 수 있습니다.

다형성

가상 메서드의 원리에 따라 animal.move() 메서드가 호출하는 메서드는 Animal의 move가 아닌 매개변수로 넘어온 실제 인스턴스의 메서드입니다. 이것이 다형성입니다

배열 요소가 Animal형이므로 각 클래스에서 제공하는 readBook(), hunting(), flying() 메서드를 사용할 수 없습니다. 다시 말해 자료형이 Animal형인 상태에서는 Human 클래스가 제공하는 readBook()메서드를 호출할 수 없는 것이지요. 각각의 클래스에 선언된 readBook(), hunting(), flying()을 호출하기 위해서는 다시 원래 자료형으로 다운 캐스팅되어야 합니다.

이들 메서드는 public abstract 예약어를 명시적으로 쓰지 않아도 컴파일 과정에서 자동으로 추상 메서드로 변환됩니다. 그리고 인터페이스에서 선언한 변수는 모두 컴파일 과정에서 값이 변하지 않는 상수로 자동 변환됩니다. Public static final 예약어를 쓰지 않아도 무조건 상수로 인식하는 것입니다.

Calc calc = new CompleteCalc();
Calc newCalc = calc;

newCalc에서는 CompleteCalc 클래스에서 추가로 구현한 showInfo()메서드 사용 못한다. 즉 Calc형으로 선언한 변수에서 사용할 수 있는 메서드는 Calc 인터페이스에 선언한 메서드뿐입니다.

인터페이스를 구현한 클래스가 있을 때 그 클래스는 해당 인터페이스형으로 묵시적 형 변환이 이루어지며, 형 변환되었을 때 사용할 수 있는 메서드는 인터페이스에서 선언한 메서드 뿐입니다.

인터페이스의 역할은 인터페이스를 구현한 클래스가 어떤 기능의 메서드를 제공하는지 명시하는 것입니다.

private 메서드

자바 9부터 인터페이스에 private 메서드를 구현할 수 있습니다. Private 메서드는 인터페이스를 구현한 클래스에서 사용하거나 재정의할 수 없습니다. 따라서 기존에 구현된 코드를 변경하지 않고 인터페이스를 구현한 클래스에서 공통으로 사용하는 경우에 private 메서드로 구현하면 코드 재사용성을 높일 수 있습니다.

Private 메서드는 코드를 모두 구현해야 하므로 추상 메서드에 private 예약어를 사용할 수는 없지만, static 예약어는 함께 사용할 수 있습니다. Private static 메서드는 정적 메서드에서 호출하여 사용합니다.

0개의 댓글