인터페이스는 일종의 추상클래스이다. 추상클래스처럼 추상메서드를 갖지만 추상클래스보다 추상화 정도가 높아서 추상클래스와 달리 몸토을 갖춘 일반 메서드 또는 멤버변수를 구성원으로 가질 수 없다.
오직 추상메서드와 상수만을 멤버로 가질 수 있다.
추상 클래스를 부분적으로만 완성된 '미완성 설계도'라고 한다면, 인터페이스는 구현된것은 아무것도 없고 밑그림만 그려져있는 '기본 설계도'라고 할 수 있다.
interface 인터페이스이름 { public static final 타입 상수이름 = 값; public abstract 메서드이름(매개변수목록); }
일반 클래스의 멤버들과 달리 인터페이스의 멤버들은 다음과 같은 제약사항이 있다.
- 모든 멤버변수는
public static final
이어야 하며, 이를 생략할 수 있다.- 모든 메서드는
public abstract
이어야 하며, 이를 생략할 수 있다.
(단, static메서드와 디폴트 메서드는 jdk1.8이후로는 사용가능하다.)- 이러한 제어자는 생략가능하며, 생략한 제어자는 컴파일시에 컴파일러가 자동적으로 추가해준다.
인터페이스는 추상클래스처럼 그 자체로 인스턴스를 생성할 수 없다. 그래서 인터페이스에 정의된 추상메서드의 몸통을 만들어주는 클래스를 작성해야 한다. 이때 구현한다는 의미의 키워드 'implements' 를 사용한다.
//인터페이스 구현 형식
class 클래스이름 implements 인터페이스이름 {
//인터페이스에 정의된 추상메서드를 구현해야 한다.
}
//상속과 구현을 동시에 하는 예시
class Fighter extends Unit implements FIghtable {
public void move(int x, int y) { /*내용생략*/ }
public void attack(Unit u) { /*내용생략*/ }
}
조상 인터페이스에 있는 메서드를 자손 인터페이스를 구현하는 클래스에서 오버라이딩할 때 조상의 메서드보다 넓은 범위의 접근제어자를 지정해야 한다.
인터페이스는 static상수만 정의할 수 있으므로 조상클래스의 멤버변수와 충돌하는 경우는 겨의 없고 충돌된다하더라도 클래스의 이름을 붙여서 구분이 가능하다. 그리고 추상메서드는 구현내용이 전혀 없으므로 조상클래스의 메서드와 선언부가 일치하는 경우에는 당연히 조상클래스쪽의 메서드를 상속받으면 되므로 문제되지 않는다.
하지만, 이렇게 하면 상속받는 멤버의 충돌은 피할 수 있지만, 다중상속의 장점을 잃게 된다. 만일 두개의 클래스로부터 상속을 받아야 할 상황이라면, 두 조상클래스 중에서 비중이 높은 쪽을 선택하고 다른 한쪽은 클래스 내부에 멤버로 포함시키는 방식으로 처리하거나 어느 한쪽의 필요한 부분을 뽑아서 인터페이스로 만든 다음 구현하도록 한다.
인터페이스는 이를 구현한 클래스의 조상이라 할 수 있으므로 해당 인터페이스 타입의 참조변수로 이를 구현한 클래스의 인스턴스를 참조할 수 있으며, 인터페이스 타입으로의 형변환도 가능하다.
Fightable f = (Fightable)new Fighter(); 또는 Fightable f = new Fighter();
void attack(Fightable f) {
//....
}
그래서 attack메서드를 호출할 떄는 매개변수로 Fightable인터페이스를 구현한 클래스의 인스턴스를 넘겨줘야 한다.
Fightable method() {
...
Fighter f = new Fighter();
return f;
}
리턴타입이 인터페이스라는 것은 메서드가 해당 인터페이스를 구현한 클래스의 인스턴스를 반환한다는 것을 의미한다.
위의 코드에서는 method()의 리턴타입이 Fightable인터페이스이기 때문에 메서드의 return문에서 Fightable인터페이스를 구현한 Fighter클래스의 인스턴스를 반환한다.
인터페이스를 사용하는 이유와 장점
- 개발 시간을 단축시킬 수 있다.
- 표준화가 가능하다.
- 서로 관계없는 클래스들에게 관계를 맺어줄 수 있다.
- 독립적인 프로그래밍이 가능하다.
인터페이스의 기본 사항
- 클래스를 사용하는 쪽(User)과 클래스를 제공하는 쪽(Provider)이 있다.
- 메서드를 사용(호출)하는 쪽(User)에서는 사용하려는 메서드의 선언부만 알면 된다. (내용은 몰라도 된다.)
원래 인터페이스에 추상메서드만 선언할 수 있는데 jdk1.8부터 디폴트 메서드와 static메서드도 추가할 수 있게 되었다.
새로 추가된 디폴트 메서드가 기존의 메서드와 이름이 중복되어 충돌하는 경우의 규칙
1. 여러 인터페이스의 디폴트 메서드간의 충돌
-> 인터페이스를 구현한 클래스에서 디폴트 메서드를 오버라이딩해야 한다.
2. 디폴트 메서드와 조상 클래스의 메서드 간의 충돌
-> 조상 클래스의 메서드가 상속되고, 디폴트 메서드는 무시된다.
- 위의 규칙들이 귀찮으면, 그냥 필요한 쪽의 메서드와 같은 내용으로 오버라이딩 해버리면 된다.