역할
두 객체를 연결해주는 다리 역할
상속 관계가 없는 다른 클래스들이 서로 동일한 메서드를 구현해야 할 때, 인터페이스는 구현 클래스들의 동일한 사용 방법과 행위를 보장해 줄 수 있음
인터페이스는 스펙이 정의된 메서드들의 집합
인터페이스 구현 클래스들은 반드시 정의된 메서드를 구현해야함
➡️ 구현 클래스들의 동일한 사용 방법과 행위 보장
➡️ 인터페이스에 다형성 적용 가능
선언
interface
키워드 사용
public, default 접그 제어자 지정 가능
구성
모든 멤버 변수 : pulblic static final
모든 메서드 : public abstract
(제어자 생략 가능)
public interface 인터페이스명 {
public static final char A = 'A';
char D = 'D'; // = public static final char
void turnOn(); // = public abstract void turnOn();
}
구현
직접 인스턴스 생성 불가 ➡️ 클래스에 구현되어 생성
implements
키워드 사용
public class 클래스명 implements 인터페이스명 {
@Override
public 리턴타입 메서드이름(매개변수) { // 추상 메서드 오버라이딩
// 실행문
}
}
상속
인터페이스간의 상속은 extends
키워드 사용
다중 상속 가능
interface A {
void a();
}
interface B {
void b();
}
interface C extends A, B { } // 다중 상속
public class Main implements C { // 구현시 a, b 추상 메서드 오버라이딩
@Override
public void a() {
System.out.println("A");
}
@Override
public void b() {
System.out.println("B");
}
}
인터페이스 구현과 상속 함께 사용 가능
class D {
void d() {
System.out.println("D");
}
}
public class Main extends D implements C {
}
추상 메서드의 기본적인 구현을 제공하는 메서드
메서드 앞에 default
키워드를 붙이며 블럭{}이 존재해야함
추상 메서드가 아니므로 구현체에서 필수로 재정의 할 필요 없음
public class Main implements A {
public static void main(String[] args) {
Main main = new Main();
main.aa(); // 디폴트 메서드 재정의 없이 바로 사용가능
}
}
interface A {
default void aa() {
System.out.println("AA");
}
}
인터페이스의 static 메서드 또한 객체 없이 호출 가능
public class Main implements A {
public static void main(String[] args) {
A.aaa(); // static 메서드 호출
}
}
interface A {
static void aaa() {
System.out.println("static method");
}
}
여러 가지 형태를 가질 수 있는 능력
사용 방법은 동일하지만 다양한 특징과 결과를 가질 수 있음
자동 타입 변환
인터페이스 변수 = 구현객체
: 자동으로 타입 변환이 일어남
public class Main {
public static void main(String[] args) {
A a1 = new B(); // A 인터페이스에 구현체 B 대입
A a2 = new C(); // A 인터페이스에 구편체 B를 상속받은 C 대입
}
}
interface A { }
class B implements A {}
class C extends B {}
강제 타입 변환
구현객체타입 변수 = (구현객체타입) 인터페이스변수
: 이미 자동 타입 변환된 인터페이스 변수를 다시 객체 타입으로 강제 변환
public class Main {
public static void main(String[] args) {
A a1 = new B(); // A 인터페이스에 구현체 B 대입
a1.a();
// a1.b(); // B의 메서드 사용 불가
System.out.println("\nB 강제 타입변환");
B b = (B) a1;
b.a();
b.b(); // 강제 타입변환으로 사용 가능
System.out.println();
A a2 = new C(); // A 인터페이스에 구편체 B를 상속받은 C 대입
a2.a();
//a2.b(); // 불가능
//a2.c(); // 불가능
System.out.println("\nC 강제 타입변환");
C c = (C) a2;
c.a();
c.b(); // 강제 타입변환으로 사용 가능
c.c(); // 강제 타입변환으로 사용 가능
}
}
interface A {
void a();
}
class B implements A {
@Override
public void a() {
System.out.println("B.a()");
}
public void b() {
System.out.println("B.b()");
}
}
class C extends B {
public void c() {
System.out.println("C.c()");
}
}