인터페이스는 통신의 도구 및 수단 혹은, 기능 활용의 방법으로 생각하면 된다.
예를 들어, 커피 자판기를 구성하는 데에는 여러 요소들이 포함되어 있을 것이다. 내부에서 커피를 만드는 기계들도 있지만 사용자가 사용할 수 있도록 하는 버튼과 화면도 있을 것이다. 이런 것들이 인터페이스이다. 따라서 사용자들은 내부의 복잡한 구조를 알 필요 없이, 인터페이스만으로 커피 자판기를 이용할 수 있다.
interface Printable {
public void print(String doc);
}
클래스의 정의와 비슷한 형태로 인터페이스를 정의할 수 있다.
내부적으로 메소드의 몸체를 갖지 않는 추상 메소드를 가져서 외형적으로 어떻게 생겼는지만 보여준다.
클래스가 아니기 때문에 인스턴스를 생성할 수 없다. 하지만 참조 변수는 선언 가능하다.
class Printer implements Printable {
public void print(String doc) {
System.out.println(doc);
}
}
implements
선언으로 Printer
클래스는 인터페이스를 구현하고 완성하는 클래스임을 알 수 있다.
구현하는 메소드와 추상 메소드 사이에도 메소드 오버라이딩 관계가 성립되어 @Override
를 사용할 수 있다.
Printable prn = new Printer();
인터페이스형 참조변수로 인터페이스를 구현한 클래스의 인스턴스를 참조할 수 있다.
이때, prn
참조 변수로 접근할 수 있는 메소드는 인터페이스에서 정의한 메소드만 접근할 수 있다.
앞에서 말했듯, 메소드 오버라이딩 관계가 성립되기 때문에 prn
참조 변수는 Printable
의 print
가 아닌 Printer
의 print
에 접근하게 된다.
즉, 기능이 매우 많고 복잡한 클래스가 있다고 생각해보자. 그 중 일부 기능만 필요하고 제공하고 싶은 경우에는, 인터페이스형 참조 변수로 접근하게 한다면 인터페이스 안에 정의된 메소드만 사용할 수 있다.
상속 ↔ 메소드 오버라이딩 ↔ 인터페이스
클래스는 하나의 클래스만 상속할 수 있지만 둘 이상의 인터페이스를 구현할 수 있다.
interface Printable {
public static final int PAPER_WIDTH = 70;
public static final int PAPER_HEIGHT = 120;
public void print(String doc);
}
인터페이스에 선언되는 메소드는 외부에 노출시키기 위해, 접근 수준 지시자가 없다면 기본적으로 public 선언이 된다.
또한, 인스턴스 선언이 불가하기 때문에, 변수 선언 시에 static final 선언이 없다면 자동으로 선언된다.
인터페이스를 구현하는 클래스는 해당 인터페이스의 모든 추상 메소드를 구현해야 한다.
그래야 에러 없이 인스턴스를 생성할 수 있다.
기존의 기능을 가지면서 새로운 기능이 추가된 클래스를 새로 만들었다고 가정해보자. 그 새 기능을 사용하기 위해 인터페이스에도 추가가 되어야 한다. 하지만 앞에서 말한 이유로 기존에 존재하던 클래스에도 그 새 기능을 구현해야 한다는 문제가 발생한다.
이를 해결하기 위해 나온 것이 인터페이스의 상속이다.
interface Printable {...}
interface ColorPrintable extends Printable {...}
인터페이스의 상속을 통해서 기존의 클래스를 수정할 필요가 없어진다.
1000개의 인터페이스가 존재한다고 생각해보자. 그리고 새로운 기능을 추가해야 한다.
그냥 추가하는 데에는 기존의 클래스를 고쳐야 한다는 문제점이 있다.
그럼 상속을 통해서 해결을 할까? 상속으로 해결은 된다. 하지만 안그래도 많은 인터페이스의 수가 배로 늘어나게 된다.
이를 해결하기 위해 나온 것이 인터페이스의 디폴트 메소드이다.
default void printCMYK(String doc) { }
기존의 인터페이스의 메소드들은 몸체를 가지지 않은 반면에 디폴트 메소드는 몸체를 가진다. 따라서 인터페이스를 구현하는 클래스에서 이를 구현하던 말던 에러를 발생시키지 않는다.
디폴트 메소드의 기능이 필요한 클래스에서만 구현하면 된다.
if(ca instanceof Cake) ...
이때, Cake
는 클래스의 이름 혹은 인터페이스의 이름도 될 수 있다.
ca
가 참조하는 인스턴스를 Cake
형 참조 변수로 참조할 수 있으면 true 반환ca
가 참조하는 인스턴스가 Cake
를 직접 혹은 간접적으로 구현한 클래스의 인스턴스인 경우 true 반환다른 기능없이, 클래스들을 구별하기 위한 표시를 하기 위해 만든 인터페이스를 마커 인터페이스라고 한다.
public abstract class House {
public void methodOne() {
...
}
public abstract void methodTwo(); // 추상 메소드
}
하나 이상의 추상 메소드를 가지는 클래스를 추상 클래스라고 한다.
추상 클래스는 인스턴스 변수나 메소드를 가질 수 있고, 참조 변수 선언이 가능하다. 그러나 인스턴스 생성은 불가능하다.
이 추상 클래스를 상위 클래스로 두고 추상 메소드는 하위 클래스에서 알아서 만들어 쓰기를 원하는 경우에 사용할 수 있다.