인터페이스란?
- 일종의 추상 클래스지만, 추상 클래스보다 추상화 정도가 높다.
- 일반 메소드 또는 멤버변수를 멤버로 가질 수 없고, 추상 메소드와 상수만을 멤버로 가질 수 있다.
- 추상 클래스가 미완성 설계도라면 인터페이스는 일반 설계도라고 할 수 있다.
인터페이스 정의
- 인터페이스는 기본적으로 클래스와 동일한 방식으로 정의한다.
- 클래스와 동일하게 접근제어자로 public 또는 default를 사용한다.
- 하지만, 키워드로 interface를 사용한다는 차이가 있다.
innterface 인터페이스이름 {
public static final 타입 상수이름 = 값;
public abstract 메소드이름 (매개 변수);
}
클래스의 멤버와 달리 인터페이스의 멤버들은 다음과 같은 제약사항이 있다.
- 모든 멤버변수는 public static final이어야 하며, 이를 생략할 수 있다.
- 원칙적으로 모든 메소드는 public abstract이어야 하며, 이를 생략할 수 있다.
- Java8부터는 static과 default 메소드 또한 허용된다.
- Java9부터는 private 메소드 또한 허용된다.
인터페이스 상속
- 인터페이스는 인터페이스로부터만 상속받을 수 있다.
- 클래스와는 달리 다중상속이 가능하다.
다음의 예제를 살펴보자.
abstract class Unit {
int x, y;
abstract void move(int x, int y);
void stop() {}
}
interface Movable {
void move(int x, int y);
}
interface Attackable {
void attack(Unit u);
}
interface Fightable extends Movable, Attackable {
// ...
}
클래스의 상속과 마찬가지로 자손 인터페이스(Fightable)은 조상 인터페이스(Movable. Attackable)에 정의된 멤버를 모두 상속받는다. 따라서 Fightable 자체에는 정의된 멤버가 하나도 없더라도 조상 인터페이스로부터 상속받은 두 개의 추상 메소드를 멤버로 갖게 된다.
인터페이스 구현
- 인터페이스는 그 자체로는 인스턴스를 생성할 수 없다.
- 추상 클래스와 같이 추상 메소드의 몸통을 만들어주는 클래스를 작성해야 한다.
- 클래스는 확장한다는 의미의 'extends'를 쓰지만, 인터페이스는 구현한다는 의미의 'implements'를 사용한다.
class 클래스이름 implements 인터페이스이름 {
// 인터페이스에 정의된 추상메소드 구현
}
class Fighter implements Fightable {
@Override
public void move(int x, int y) {
// ...
}
@Override
public void attack(Unit u) {
//...
}
}
추상 클래스와 유사하게 메소드의 일부만 구현한다면 abstract를 붙여서 추상 클래스로 선언해야 한다.
abstract class Fighter implements Fightable {
@Override
public void move(int x, int y) {
// ...
}
}
또한, 다음과 같이 상속과 구현을 동시에 할 수도 있다.
class Fighter extends Unit implements Fightable {
@Override
public void move(int x, int y) {
// ...
}
@Override
public void attack(Unit u) {
// ...
}
}
인터페이스 참조를 통해 구현체 사용하기
- 자손클래스의 인스턴스를 조상타입의 참조변수로 참조하는 것이 가능하다(다형성).
- 인터페이스 역시 마찬가지로 해당 인터페이스 타입의 참조변수로 이를 구현한 클래스의 인스턴스를 참조할 수 있으며, 인터페이스 타입으로의 형변환도 가능하다.
- 이는 인터페이스 역시 이를 구현한 클래스의 "조상"이기 때문에 가능하다.
인터페이스 Fightable을 클래스 Fighter가 구현했을 때, 다음과 같이 Fighter 인스턴스를 Fightable 타입의 참조변수로 참조하는 것이 가능하다. 다만, Fighter에 추가적으로 작성된 메소드가 있고 이를 사용하려는 경우엔 형변환을 해주어야 한다.
Fightable f = (Fightable) new Fighter();
// 또는
Fightable f = new Fighter();
// 형변환
((Fighter)f).doSomethingElse();
인터페이스 default 메소드
- 추상 메소드와 달리 새로 추가되어도 해당 인터페이스를 구현한 클래스를 변경하지 않아도 된다.
- 조상 클래스에 새로운 메소드를 추가한 것과 동일한 효과를 지닌다.
- 구현한 클래스에서 재정의가 가능하다.
- 참조변수로 호출이 가능하다.
새로 추가된 default 메소드가 기존의 메소드와 이름이 중복되어 충돌하는 경우가 발생한다.
1. 여러 인터페이스의 default 메서드 간의 충돌
- 인터페이스를 구현한 클래스에서 default 메소드를 오버라이딩해야 한다.
2. default 메서드와 조상 클래스의 메서드 간의 충돌
- 조상 클래스와 메소드가 상속되고, default 메소드는 무시된다.
위의 규칙이 다소 복잡하다면, 그냥 필요한 쪽의 메소드와 같은 내용으로 오버라이딩하면 된다.
인터페이스 static 메소드
- 인스턴스와 관계가 없는 독립적인 메소드이다.
- 구현한 클래스에서 재정의가 불가능하다.
- 클래스명으로 호출이 가능하다.
인터페이스 private 메소드
- 인터페이스 내에서만 사용이 가능하다.
- default 메소드나 static 메소드에서 사용된다.
'프로그래밍 언어 > Java + Kotlin' 카테고리의 다른 글
[Java] 스터디 10주차: 쓰레드 (0) | 2021.08.17 |
---|---|
[Java] 스터디 9주차: 예외처리 (0) | 2021.07.29 |
[Java] 스터디 7주차: 패키지 (0) | 2021.07.15 |
[Java] 스터디 6주차: 상속 (0) | 2021.07.07 |
[Java] 스터디 5주차: 클래스 (0) | 2021.07.07 |