추상 메서드의 집합 << 핵심 개념!
구현된 것이 전혀 없는 설계도. 껍데기임! (모든 멤버가 public)
인터페이스 vs 추상클래스
- 추상클래스 : 추상 메서드를 갖고있는 "일반 클래스" + 생성자, iv.. 등을 갖고 있음
- 인터페이스 : 추상 메서드의 집합이다. 생성자, iv, 인스턴스 메서드를 가질 수 없음.
-> 상수, static 메서드, 디폴트 메서드는 기능 보완으로 사용 가능
캡슐화 : 메서드를 통해서 iv에 접근하게 함 (접근 제어자 이용)
-> 캡슐화의 가장 바깥을 이루는게 인터페이스!
// 모든 멤버는 public!
interface 인터페이스이름 { // 변수 선언 안됨. (iv, cv 다 안됨)
public static final 타입 상수이름 = 값; // 상수 (가능)
public abstract 메서드이름(매개변수 목록); // 추상메서드
}
// ex
interface PlayingCard {
// 상수 (항상 public static final 취급을 받는다)
public static final int SPADE = 4;
final int DIAMOND = 3;
static int HEART = 2;
int CLOVER = 1; // public, static, final, 생략 가능
// 추상메서드 (항상 public abstract 취급을 받는다)
public abstract String getCardNumber();
String getCardKind(); // public, abstract 생략 가능
}
interface Fightable extends Movable, Attackable {} // 멤버 2개
interface Movable {
//** 지정된 위치(x, y)로 이동하는 기능의 메서드 */
void move(int x, int y);
}
interface Attackable {
//** 지정된 대상(u)을 공격하는 기능의 메서드 */
void attack(Unit u);
}
// 클래스의 상속 : extends 클래스이름
// 인터페이스의 구현
class 클래스이름 implements 인터페이스이름 {
// 인터페이스에 정의된 추상메서드를 모두 구현해야 한다.
}
// ex
interface Fightable {
void move(int x, int y); // public abstract가 생략되어 있음
void attack(Unit u);
}
// Fighter class는 Fightable 인터페이스를 구현했다! 라고 함
class Fighter implements Fightable {
public void move(int x, int y) { /* 내용 생략 */ } // 몸통{}을 완성하여 인터페이스 구현
public void attack(Unit u) { /* 내용 생략 */ }
}
// 일부만 구현하는 경우, 클래스 앞에 abstract를 붙여야 함
abstract class Fighter implements Fightable {
public void move(int x, int y) { /* 내용 생략 */ }
}
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) { /* 내용 생략 */ }
public void attack(Fightable f) { /* 내용 생략 */ }
}
// Unit <- Fighter -> Fightable
Unit u = new Fighter();
Fightable f = new Fighter();
f.move(100, 200);
f.attack(new Fighter()); // Fightable 인터페이스를 구현한 클래스의 인스턴스만 매개변수로 넘길 수 있다.
// 인터페이스를 리턴타입으로 지정하기
Fightable method() { // Fightable 인터페이스를 구현한 클래스의 인스턴스를 반환
...
Fighter f = new Fighter();
return f; // Fightable 인터페이스를 구현한 Fighter 객체 반환
// FIghter(자손)는 Fightable(조상)로 형변환이 가능하다.
// 즉, 형변환이 되어 Fightable 타입으로 반환되는 것!
}
// 아래 두 문장은 같다
Fightable f = method(); // 받는 타입이 반환 타입과 일치해야 함
Fightable f = new Fighter();
// 코드가 유연하지 않아, 변경에 불리함.
class B {
public void method() {
System.out.println("methodInB");
}
}
// 선언부와 구현부를 분리
// 코드가 유연하여, 변경에 유리함
interface I { // 새로운 interface 선언
public void method(); // 선언부만 떼어냄
}
class B implements I {
public void method() {
System.out.println("methodInB");
}
}
// 직접적인 관계의 두 클래스 (A-B)
class A {
// B 클래스를 "직접" 사용 -> A가 B에 의존하고 있다.
public void methodA(B b) {
b.methodB();
}
}
class B {
public void methodB() {
System.out.println("methodB()");
}
}
class InterfaceTest {
public static void main(String args[]) {
A a = new A();
a.methodA(new B());
}
}
// 간접적인 관계의 두 클래스 (A-I-B)
class A {
// A는 B와 관계 없음.
public void methodA(I i) {
i.methodB();
}
}
interface I { void methodB(); }
class B implements I {
public void methodB() {
System.out.println("methodB()");
}
}
class C implements I {
public void methodB() {
System.out.println("methodB() in C");
}
}
개발 시간을 단축할 수 있다.
변경에 유리한, 유연한 설계가 가능하다.
표준화가 가능하다. (JDBC : interface 집합)
서로 관계 없는 클래스들의 관계를 맺어줄 수 있다.
인터페이스에 Default 메서드, static 메서드 추가 가능
인터페이스에 새로운 메서드(추상 메서드)를 추가하기 어려움
-> 추상 메서드를 추가하는 경우, 인터페이스를 상속받는 모든 클래스에 추상메서드를 구현 해줘야 함.
-> 해결책 : 디폴트 메서드
디폴트 메서드는 인스턴스 메서드 (인터페이스 원칙 위반, but 예외 사항)
디폴트 메서드가 기존의 메서드와 충돌할 때의 해결책
1. 여러 인터페이스의 디폴트 메서드 간의 충돌
-> 인터페이스를 구현한 클래스에서 디폴트 메서드를 오버라이딩 해야 한다.
2. 디폴트 메서드와 조상 클래스의 메서드 간의 충돌
-> 조상 클래스의 메서드(우선)가 상속되고, 디폴트 메서드는 무시된다.
=> 충돌이 애초에 나지 않도록, 디폴트 메서드는 기본적으로 오버라이딩을 해주자!
interface MyInterface {
void method();
void newMethod1(); // 추상메서드
default void newMethod2() {} // 몸통이 있는 디폴트 메서드 추가 가능
}