인터페이스 (Interface)
인터페이스는 선언 시 interface라 선언하고, 인터페이스를 구현(상속)하는 클래스에서는 implements 키워드를 사용해 구현(상속)한다.
인터페이스는 모든 메서드가 public abstract로 선언되고, 모든 멤버 변수가 public static final로 선언된 특별한 종류의 추상 클래스이다.
메서드 선언 시 public abstract는 생략 가능하고, 멤버 변수 선언 시 public static final 또한 생략 가능하다.
인터페이스는 다른 클래스와는 달리 다중 상속이 가능하다.
💻 예제 1
📝 소스 코드
public class Main {
public static void main(String[] args) {
TestImpl test1 = new TestImpl();
test1.testA();
test1.testB();
System.out.println();
TestImpl2 test2 = new TestImpl2();
// System.out.println(test2.MIN); // 오류
System.out.println(TestInterface1.MIN);
System.out.println(TestInterface2.MIN);
test2.testA();
test2.testB();
test2.testC();
}
}
interface TestInterface1 {
public static final int MIN = 1;
public abstract void testA();
public abstract void testB();
}
interface TestInterface2 {
public static final int MIN = 0;
int MAX = 10;
public static final String JAVA_STRING = "Java";
String CSHARP_STRING = "C#";
public abstract void testA();
void testC();
}
class TestImpl implements TestInterface1 {
@Override
public void testA() {
System.out.println("testA()");
}
@Override
public void testB() {
System.out.println("testB()");
}
}
class TestImpl2 implements TestInterface1, TestInterface2 {
@Override
public void testA() {
System.out.println("testA() 2");
}
@Override
public void testB() {
System.out.println("testB() 2");
}
@Override
public void testC() {
System.out.println("testC() 2");
}
}
TestInterface1 인터페이스를 구현한 TestImpl 클래스로 인스턴스를 생성했다.
test1 인스턴스로 testA() 메서드와 testB() 메서드 호출이 가능하다.
TestInterface1와 TestInterface2를 구현한 TestImpl2 클래스로 인스턴스를 생성했다.
test2 인스턴스로 MIN을 호출하려 하면 The filed test2.MIN is ambiguous 오류가 발생한다. 이는 testA() 메서드를 호출할 때도 마찬가지이다.
TestInterface1와 TestInterface2에 testA() 메서드와 MIN 변수가 똑같이 선언되어있어서 test2가 어느 인터페이스에 있는 걸 호출하는 것인지 모호한 것이다.
static으로 변수와 메서드를 선언하면 클래스명 또는 인터페이스명으로 접근할 수 있다. 따라서 위와 같이 모호한 경우가 발생한다면 인터페이스명으로 접근해주면 된다.
TestInterface2에서 int MAX와 String SHARP_STRING은 앞에 public static final이 붙지 않았다.
인터페이스 내에 있는 멤버 변수이므로 생략하여도 자동으로 public static final이 붙는 것으로 취급한다.
이는 testC() 메서드도 마찬가지이다. 인터페이스 내에 있는 모든 메서드는 public abstract임으로 생략 가능하다.
📄 실행 결과
💻 예제2
📝 소스 코드
public class Main {
public static void main(String[] args) {
TestImple t1 = new TestImple();
t1.testA();
t1.testB();
System.out.println();
Interface1 t2 = new TestImple();
t2.testA();
// t2.testB();
((TestImple) t2).testB();
System.out.println();
Interface2 t3 = new TestImple();
t3.testB();
((TestImple) t3).testA();
}
}
public interface Interface1 {
public abstract void testA();
}
public interface Interface2 {
public abstract void testB();
}
public class TestImple implements Interface1, Interface2 {
@Override
public void testB() {
System.out.println("Interface2 : test 구현");
}
@Override
public void testA() {
System.out.println("Interface1 : test 구현");
}
}
t1 인스턴스는 참조 변수의 선언이 TestImple 타입으로 생성되었기 때문에 testA() 메서드와 testB() 메서드에 모두 접근할 수 있다.
그러나 t2는 인스턴스는 TestImple 타입으로 생성되었지만 참조 변수가 Interface1로 선언되었다.
그래서 testA()는 인식할 수 있지만 testB()는 없는 것으로 간주한다.
이때 형변환(casting)을 사용해서 Interface2에 선언된 메서드를 사용할 수 있다.
📄 실행 결과