[Java] 인터페이스
1. 인터페이스
인터페이스란?
일종의 추상 클래스이다. 오직 추상 메서드와 상수만을 멤버로 가질 수 있으며 추상 클래스보다 추상화 정도가 높다. 추상 클래스가 "미완성 설계도"라면 인터페이슨느 "기본 설계도"이다. 인터페이스도 다른 클래스를 도우는데 목적이 있다.
클래스를 이용한 다중 상속은 어느 클래스에 해당 메서드가 있는지 모호하기 때문에 다중 상속을 지원하지 않는다.
하지만 인터페이스는 다중 상속을 지원하고 다른 클래스를 작성할때 기본 틀이 되며 클래스 사이에서 중간 매개 역할을 하는 일종의 추상 클래스이다.
2. 인터페이스의 선언
인터페이스는 class 대신 interface 키워드를 접근 제어자와 같이 사용한다.
public interface 인터페이스 이름 {
...
}
2.1. 인터페이스의 제약 사항
모든 멤버 변수는 public static final 이어야 하며, 생략이 가능하다.
모든 메서드는 public abstract 이어야 하며, 생략이 가능하다. 단 static 과 default 메서드는 제어
생략된 제어자는 컴파일러가 자동으로 추가한다.
3. 인터페이스의 상속 과 구현
3.1. 상속
인터페이스는 인터페이스로 부터만 상속받을 수 있다.
다중 상속, 즉 여러 개의 인터페이스를 상속받을 수 있다.
Object와 같은 최상위 클래스가 없다.
3.2. 구현
인터페이스는 인스턴스를 생성할 수 없다.
인터페이스의 몸통을 구현해주는 클래스가 필요하다.
implements 키워드를 이용하여 인터페이스를 구현한다.
public interface Able {}
public interface Aable extends Able {}
public interface Bable extends Able {}
public class Sample implements Aable, Bable {
...
}
Sample 클래스는 Aable, Bable 인터페이스를 구현한다 라고 표현한다.
인터페이스에 메서드 중 일부만 구현 한다면 나머지 메서드는 하위 클래스에서 abstract를 붙여 추상 클래스로 선언한다.
또한 상속과 구현을 동시에 할수 있다.
public interface Able {}
public class Parent {}
public class Child extends Parent implements Able {}
인터페이스 명은 주로 'able'로 끝나도록 작성한다.
인터페이스도 instaceof를 통해 타입을 확인할 수 있다.
if(f instanceof Aable) {}
4. 인터페이스를 이용한 다중 상속
클래스의 다중 상속이 되지 않아 A는 상속 받고 B는 멤버 변수로 사용하는데 이때 인터페이스를 이용하여 다형적 특성을 유지할 수 있다.
public class A {}
public class B {void method() {}}
public interface Bable {
void method() {}
}
public Sample extends A implements Bable {
B b = new B(); // 멤버 변수로 B 인스턴스 생성
void method() { // interface 구체화
b.method(); // B 클래스의 메소드 실행
}
}
5. 인터페이스를 이용한 다형성
상속 받은 하위 클래스를 인터페이스 타입으로 형 변환이 가능하다.
// 1. 형 변환
Bable b = (Bable) new B();
// 2. 메서드의 매개변수로 인터페이스 사용가능
public void method(Bable b) {}
method(new B()); // 인자 값으로 B 클래스 사용
// 3. return 타입으로 인터페이스 지정 가능
public Bable method() {}
// 4. Manager 클래스를 통한 다형성
public class ParseManager {
public static Parseable getParse(String type) {
if(type.equals("1")) {
return new Parse1();
} else {
return new Parse2();
}
}
}
public class Parse1 implements Parseable {}
public class Parse2 implements Parseable {}
public static void main(String[] args) {
Parseable parser = ParseManager.getParse("2");
}
Manager의 getParse 함수로 Parseable로 구현된 클래스 인스턴스를 받아서 사용할 수 있다.
6. 인터페이스의 장점
- 개발 시간을 단축 시킬 수 있다.
인터페이스와 이를 구현하는 클래스를 동시에 구현 가능하며 인터페이스를 구현하는 클래스의 개발을 기다리지 않아도 된다. - 표준화가 가능하다.
표준화 된 인터페이스를 개발자들에게 전달함으로써 일관되고 정형화된 개발이 가능하다. - 서로 관계 없는 클래스들에게 관계를 맺어 줄 수 있다.
관계없는 클래스들에게 공통의 인터페이스를 구현하도록 하여 관계를 맺어 줄 수 있다. - 독립적인 프로그래밍 가능하다.
클래스와 클래스의 직접적인 관계를 인터페이스를 이용해서 간접적인 관계로 변경하면 클래스 간 서로 영향을 미치지 않아 독립적인 프래그래밍이 가능하다.
public class Marig {}
public Tank implements Repairable {}
public Scv implements Repairable {
public int repair(Repairable r) {
...
}
}
public static void main(String[] args) {
Scv s = new Scv();
s.repair(new Marin()); // Marin 클래스에 repair가 없어 에러
s.repair(new Tank()); // 성공
}
위의 예제에서 Repariable에 아무런 멤버가 없지만 기계 유닛에 Repairable을 상속하면서 repair 메서드를 다양한 매개변수로 이용할 수 있다.
만약 4개의 클래스 중 2개의 클래스만 건물이동 메서드를 만들고 싶은 경우 각 클래스에 똑같은 메서드를 넣으면 중복이 생긴다. 그래서 인터페이스를 구현한 새로운 클래스를 각 클래스에서 멤버로 사용할 수 있다.
public class A implements Moveable {
MoveImpl move = new MoveImpl();
}
public class B implements Moveable {
MoveImpl move = new MoveImpl();
}
MoveImpl에서 Move메서드를 구현하여 A, B 클래스에서 호출만 하도록 한다.
7. 인터페이스의 이해
인터페이스의 이해 2가지 조건
- 클래스를 사용하는 쪽(User)와 제공하는 쪽(Provider)이 있다.
- 메서드를 사용(호출)하는 쪽(User)에서는 사용하려는 메서드(Provider)의 선언 부만 알고 내용은 몰라도 된다.
아래의 예제는 좋지 못한 예와 좋은 예이다.
// 좋지 못한 예
public class B {}
public class A {
public void method(B b) {
b.method();
}
}
// 좋은 예
public interface Able {}
public class C implements Able {}
public class B implements Able {}
public class A implements Able {
public void method(Able a) {
a.method();
}
}
public static void main(String[] args) {
A a = new A();
a.method(new B());
a.method(new C());
}
좋지 못한 예의 경우 B클래스가 A클래스보다 먼저 작성되어 있어야 하고 B가 C로 변경되면 A도 변경해줘야 하는 단점이 있다.
좋은 예의 경우 A는 Able만 알고 있으면 되고 A의 method가 안에서 어떤 인스턴스의 메서드를 호출하는지 상관이 없는 장점이 있다.
8. default method와 static method
JDK 1.8부터 인터페이스에 default method와 static method를 추가할 수 있게 되었다.
Collection Interface와 Collections 클래스를 예제로 들 수 있으며 인터페이스의 static 메서드의 접근제어자는 항상 public이다.
8.1. default method
인터페이스에 변경이 생기면 인터페이스를 구현하는 모든 클래스에 변경이 생긴다. 그래서 default 메서드가 고안되었다.
default 메서드는 몸통이 있어야 하기 때문에 상속 클래스에서 변경점이 생기지 않는다. 즉, 상위 클래스에 메서드가 추가되는 것과 같다.
defulat메서드가 다른 메서드와 이름 충돌이 있을 수 있어 아래와 같은 해결책이 필요하다.
- 여러 인터페이스의 default 메서드 간 충돌
- 인터페이스를 구현한 클래스에서 메서드를 오버라이딩 해야한다.
public interface A { public int getAge(); } public interface B { public int getAge(); } public class Sample implements A, B { public int getAge() { .... } }
default 메서드와 상위 클래스의 메서드 간 충돌
- 상위 클래스의 메서드가 상속되고 default 메서드는 무시된다.