[Java] 인터페이스
순수 추상 클래스를 편리하게 사용할 수 있도록 "인터페이스" 라는 기능을 제공한다.
인터페이스는 class 가 아니라 interface 키워드를 사용하면 된다.
순수 추상 클래스는 다음과 같은 특징을 가졌다
1. 인스턴스를 생성할 수 없다.
2. 상속 시 모든 메서드를 오버라이딩 해야 한다.
3. 주로 다형성을 위해 사용된다.
인터페이스의 특징은
1. 인터페이스의 메서드는 모든 public, abstract 이다.
2. 메서드에 public abstract 를 생략할 수 있다.
3. 인터페이스는 다중 구현을 지원한다.
인터페이스에는 모든 멤버 변수는 public static final 이 모두 포함되었다고 간주한다.
#final 은 변수를 한번 설정하면 수정 불가
인터페이스는 구현이라고 정의하기 때문에 점선으로 표현한다.
package poly.basic.ex4;
public interface InterfaceAnimal {
void sound();
void move();
}
인터페이스는 일반적으로 상속할 목적으로 사용되기 때문에 pubilc이 기본으로 사용된다.
package poly.basic.ex4;
public class Dog implements InterfaceAnimal {
@Override
public void sound() {
System.out.println("Dog sound");
}
@Override
public void move() {
System.out.println("Dog move");
}
}
package poly.basic.ex4;
public class Cat implements InterfaceAnimal{
@Override
public void sound() {
System.out.println("cat sound");
}
@Override
public void move() {
System.out.println("cat move");
}
}
인터페이스는 extends 라는 용어를 사용하지 않는 implements를 사용한다.
package poly.basic.ex4;
public class InterfaceMain {
public static void main(String[] args) {
//인터페이스 생성불가
Cat cat = new Cat();
Dog dog = new Dog();
soundMain(cat);
soundMain(dog);
}
public static void soundMain(InterfaceAnimal animal) {
System.out.println("동물소리 테스트 시작");
animal.sound();
System.out.println("동물소리 테스트 종료");
animal.move();
}
}
cat sound
동물소리 테스트 종료
cat move
동물소리 테스트 시작
Dog sound
동물소리 테스트 종료
Dog move
그럼 상속 vs 구현 용어 차이
부모 클래스의 기능을 자식 클래스가 상속 받을 때 상속이다.
부모 인터페이스의 기능을 자식이 상속 받을 때는 인터페이스를 구현한다고 한다.
이렇게 다르게 사용하는 이유는 "상속" 은 부모의 기능을 물려받는 것이 목적이지만, "구현" 은 모든 메서드가 추상 메서드이며,
따라서 물려받을 수 있는 기능이 없고, 오히려 인터페이스에 정의한 모든 메서드를 자식이 오버라이딩 해서 기능을 구현해야 한다.
상속과 구현은 사람이 표현하는 단어만 다를 뿐이지 자바 입장에서는 같다.
인터페이스를 사용하는 이유
1. 제약: 인터페이스를 구현하는 곳에서 인터페이스의 메서드를 반드시 구현해라는 제약을 해주기 때문에 사용한다. 인터페이스는 모든 메서드가 추상 메서드이기 때문에 이 제약이 틀어질 일이 없다.
2. 다중 구현: 자바에서 클래스 상속은 부모를 하나만 지정할 수 있다. 반면, 인터페이스는 부모를 여러명 두는 다중 구현이 가능하다.
그러나 자바8에서 default 를 사용하면 인터페이스에서도 메서드를 구현할 수 있다.
인터페이스 다중구현
자바는 다중 상속을 지원하지 않는다. 그래서 extends 대상은 하나만 선택할 수 있다. 부모를 하나만 선택할 수 있다는 뜻이다.
만약 비행기와 자동차를 상속 받아서 하늘을 나는 자동차를 만든다고 가정해보자.
만약 그림과 같이 다중 상속을 사용하게 되면 AirplaneCar 입장에서 move()를 호출할 때 어떤 부모의 move()를 사용해야 할지, 애매한 문제가 발생한다. 이것을 다이아몬드 문제라고 한다.
그리고 다중 상속을 사용하면 클래스 계층 구조가 매우 복잡해질 수 있다.
하지만, 인터페이스는 다중 상속이 허용된다. 왜냐면 인터페이스는 모두 추상메서드로 이루어져 있기 때문이다.
위와 같이 methodCommon() 을 둘다 가지고 있을 때, 어느 부모의 메서드를 사용할지 고민하는 것이 아닌,
Child 인터페이스 안에 있느 methodCommon()을 호출하면 된다. 따라서 다이아몬드 문제는 발생하지 않는다.
package poly.basic.diamond;
public interface InterfaceA {
void methodA();
void methodCommon();
}
package poly.basic.diamond;
public interface InterfaceB {
void methodB();
void methodCommon();
}
package poly.basic.diamond;
public class Child implements InterfaceA, InterfaceB {
@Override
public void methodB() {
System.out.println("Child methodB");
}
@Override
public void methodA() {
System.out.println("Child.methodA");
}
@Override
public void methodCommon() {
System.out.println("Child.methodCommon");
}
}
package poly.basic.diamond;
public class DiamondMain {
public static void main(String[] args) {
InterfaceA a = new Child();
a.methodA();
a.methodCommon();
InterfaceB b = new Child();
b.methodB();
b.methodCommon();
}
}
어차피 오버라이딩된 애들이 호출된다.