인터페이스

김설영·2022년 4월 2일
0

인터페이스란?

  • 추상 메서드의 집합 << 핵심 개념!

  • 구현된 것이 전혀 없는 설계도. 껍데기임! (모든 멤버가 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 생략 가능
}

인터페이스 상속

  • 인터페이스의 조상은 인터페이스만 가능하다. (Object가 최고 조상 아님!)
  • 다중 상속이 가능. (추상 메서드는 충돌해도 문제가 없기 때문)
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");
    }
}

장점

  1. 개발 시간을 단축할 수 있다.

  2. 변경에 유리한, 유연한 설계가 가능하다.

  3. 표준화가 가능하다. (JDBC : interface 집합)

  4. 서로 관계 없는 클래스들의 관계를 맺어줄 수 있다.


Default 메서드, static 메서드

  • 인터페이스에 Default 메서드, static 메서드 추가 가능

  • 인터페이스에 새로운 메서드(추상 메서드)를 추가하기 어려움
    -> 추상 메서드를 추가하는 경우, 인터페이스를 상속받는 모든 클래스에 추상메서드를 구현 해줘야 함.
    -> 해결책 : 디폴트 메서드

  • 디폴트 메서드는 인스턴스 메서드 (인터페이스 원칙 위반, but 예외 사항)

  • 디폴트 메서드가 기존의 메서드와 충돌할 때의 해결책
    1. 여러 인터페이스의 디폴트 메서드 간의 충돌
    -> 인터페이스를 구현한 클래스에서 디폴트 메서드를 오버라이딩 해야 한다.
    2. 디폴트 메서드와 조상 클래스의 메서드 간의 충돌
    -> 조상 클래스의 메서드(우선)가 상속되고, 디폴트 메서드는 무시된다.

=> 충돌이 애초에 나지 않도록, 디폴트 메서드는 기본적으로 오버라이딩을 해주자!

interface MyInterface {
	void method();
    void newMethod1();	// 추상메서드
    default void newMethod2() {}  // 몸통이 있는 디폴트 메서드 추가 가능 
}
profile
블로그 이동하였습니당! -> https://kimsy8979.tistory.com/

0개의 댓글