인터페이스란?
인터페이스는 추상메서드의 집합이다. 인터페이스는 추상클래스처럼 추상메서드를 갖지만 추상클래스보다 추상화 정도가 높아서 추상클래스와 달리 몸통을 갖춘 일반 메서드 또는 멤버변수를 구성원으로 가질 수 없다. 오직 추상메서드와 상수만을 멤버로 가빌 수 있으며, 그 외의 다른 어떠한 요소도 허용하지 않는다.
인터페이스 작성
interface 인터페이스이름 {
public static final 타입 상수이름 = 값;
public abstract 메서드이름(매개변수목록);
}
- 인터페이스의 멤버들의 제약사항
- 모든 멤버변수는 public static final 이어야 하며, 이를 생략할 수 있다.
- 모든 메서드는 public abstract 이어야 하며, 이를 생략할 수 있다.
단, static메서드와 디폴트 메서드는 예외(JDK 1.8부터)
- 인터페이스의 상속
인터페이스는 인터페이스로부터만 상속받을 수 있으며, 클래스와는 달리 다중상속. 즉, 여러 개의 인터페이스로부터 상속을 받는 것이 가능하다. 인터페이스는 클래스와 달리 Object 클래스와 같은 최고 조상이 없다.
interface Movable {
void move(int x, int y);
}
interface Attackable {
void attack(Unit u);
}
interface Fightable extends Movable, Attackable { } // 다중상속
인터페이스를 이용한 다형성
다형성은 자손클래스의 인스턴스를 조상타입의 참조변수로 참조하는 것이다. 인터페이스 역시 이를 구현한 클래스의 조상이라 할 수 있으므로 해당 인터페이스 타입의 참조변수로 이를 구현한 클래스의 인스턴스를 참조할 수 있으며 인터페이스 타입으로의 형변환도 가능하다.
- 인터페이스 Fightable을 클래스 Fighter가 구현했을 때, 다음과 같이 Fighter 인스턴스를 Fightable 타입의 참조변수로 참조하는 것이 가능하다.
Fightable f = (Fightable)new Fighter();
// 또는
Fightable f = new Fighter();
* Fighterable 타입의 참조변수로는 인터페이스 Fightable에 정의된 멤버들만 호출이 가능하다.
- 인터페이스는 메서드의 매개변수의 타입으로 사용될 수 있다.
void attack(Fightable f) { .... }
=> 인터페이스 타입의 매개변수가 갖는 의미는 메서드 호출 시 해당 인터페이스를 구현한 클래스의 인스턴스를 매개변수로 제공해야한다는 것이다.
- 그리고 메서드의 리턴타입으로 인터페이스의 타입을 지정하는 것도 가능하다.
Fightable method() {
// ...
// 아래 두 문장을 한 문장으로 하면 return new Fighter();
Fighter f = new Fighter();
return f; // 인터페이스를 구현한 객체를 반환
}
리턴타입이 인터페이스라는 것은 메서드가 해당 인터페이스를 구현한 클래스의 인스턴스를 반환한다는 것을 의미한다.(중요!!!) 위의 코드에서 method()의 리턴타입이 Fightable 인터페이스이기 때문에 메서드의 return문에서 Fightable인터페이스를 구현한 Fighter 클래스의 인스턴스를 반환한다.
이해가 되었는가...!! 아래 코드를 보며 완벽하게 이해해보자....
abstract class Unit {
int x;
int y;
abstract void move(int x, int y);
void stop(){
System.out.println("멈춥니다.");
}
}
interface Fightable {
void move(int x, int y);
void attack(Fightable f);
}
class Fighter extends Unit implements Fightable{
public void move(int x, int y){
System.out.println("["+x+","+y+"] 로 이동");
}
public void attack(Fightable f){
System.out.println(f+"를 공격");
}
Fightable getFightable(){
Fighter f = new Fighter();
return f; // return (Fightable)f; 형변환 생략
}
}
public class FighterTest {
public static void main(String[] args) {
Unit u = new Fighter();
u.move(100,300);
u.stop();
// attack() 없음
Fighter f = new Fighter();
f.move(100,200);
//f.attack(new Fighter()); 을 두 줄로 만들면
Fighter f2 = new Fighter();
f.attack(f2);
Fightable ft = f.getFightable(); // 타입 주의! 반환타입과 일치하는지 확인하기
ft.move(10,10);
ft.attack(ft);
Fightable t = new Fighter();
t.move(100,400);
t.attack(new Fighter());
// stop() 없음
}
}
[실행 결과]
[100,300] 로 이동
멈춥니다.
[100,200] 로 이동
Fighter@17550481를 공격
[10,10] 로 이동
Fighter@735f7ae5를 공격
[100,400] 로 이동
Fighter@180bc464를 공격
인터페이스 장점
- 개발 시간을 단축할 수 있다.
- 변경에 유리한 유연한 설계가 가능하다.
- 표준화가 가능하다.
- 서로 관계없는 클래스들을 관계를 맺어줄 수 있다.
디폴트 메서드와 static메서드
- 인터페이스에 디폴트 메서드, static메서드 추가 기능(JDK 1.8부터)
- static 메서드는 인스턴스와 관계가 없는 독립적인 메서드이기 때문에 인터페이스에 추가하지 못할 이유가 없음.
- 인터페이스에 새로운 메서드(추상 메서드)를 추가하기 어려움. => 해결책 : 디폴트 메서드
- 디폴트 메서드는 인스턴스 메서드(인터페이스 원칙 위반)
<디폴트 메서드와 기존의 메서드와 충돌할 때의 해결책>
1. 여러 인터페이스의 디폴트 메서드 간의 충돌
- 인터페이스를 구현한 클래스에서 디폴트 메서드를 오버라이딩해야 한다.
2. 디폴트 메서드와 조상 클래스의 메서드 간의 충돌
- 조상 클래스의 메서드가 상속되고, 디폴트 메서드는 무시된다.
-> 그냥 직접 오버라이딩하면 해결
[참고]
Java의 정석(남궁성 저)
'공부 > 자바' 카테고리의 다른 글
예외 처리(Exception handling) (0) | 2024.06.04 |
---|---|
내부 클래스(inner class) (0) | 2024.06.01 |
추상 클래스(abstract class) (0) | 2024.05.31 |
다형성(polymorphism) (0) | 2024.05.31 |
제어자(modifier) (0) | 2024.05.31 |