[Java] Java의 정석 정리 (인터페이스)

송병훈·2022년 10월 4일
0

자바의 정석

목록 보기
13/15
post-thumbnail

객체지향의 막바지에 도달했습니다.
인터페이스까지 하면 중요한 내용은 다 다뤄요.

인터페이스의 사전적 의미는 다음과 같아요.

서로 다른 두 개의 시스템(or장치) 사이에서 정보(or신호)를 주고받는 경우의 경계면이다.
즉, 사용자가 기기를 쉽게 동작시키는데 도움을 주는 시스템을 의미한다.

그렇다면 Java에서는 이런 뜻이겠죠?

서로 다른 두 객체 사이에서 데이터를 주고받는 경계면,
한 클래스가 다른 클래스를 쉽게 다루도록 도움을 주는 것.

좀 더 알아볼까요?


인터페이스 (interface)

저번에 abstract 키워드가 붙고, 구현부 { }가 없는 메소드를 추상메소드라고 했고,
추상메소드가 있는 클래스를 추상클래스라고 했어요.
다른 클래스에서 상속받아 구현해야 객체를 생성할 수 있다고 했죠.

추상클래스의 장점 기억나시나요?

  • 자손클래스에서 공통으로 구현할 메소드를 미리 선언해 놓는 용도
  • 꼭 필요한 메소드이면서, 자손마다 다르게 구현될 것으로 예상되는 경우에 사용
  • 그렇게 자손클래스를 작성하는 데 도움을 줌

인터페이스는 추상메소드의 집합 입니다.
하지만 추상클래스와 달리 인터페이스에는
인스턴스 변수, static(클래스) 변수, 일반 메소드가 없어요.

추상메소드와 상수만 허락될 뿐이죠.

추상클래스랑 비슷한데,
어떤 장점이 더 있는 걸까요?
인터페이스를 왜 사용하는 걸까요?

코드의 변경을 최소화 할 수 있기 때문입니다.

그 외에도 인터페이스의 특징이 많아요.


1. 개발시간 단축

메소드를 호출하는 쪽에서는
매개변수, 리턴타입, 매개변수명 만 있으면 메소드를 사용할 수 있죠?
인터페이스를 통해 선언과(껍데기와) 구현이(알맹이가) 분리되어서
메소드의 호출을 먼저하고, 구현은 나중에(or 동시에) 할 수 있어요.

/* 인터페이스 */
interface Fightable extends Movable, Attackable { }
interface Movable {	void move(int x, int y);	}
interface Attackable {	void attack(Unit u); }

/* 부모클래스 */
class Unit {
	int x;			// 유닛의 위치(x좌표)
	int y;			// 유닛의 위치(y좌표)
}

/* 인터페이스를 구현하고, 부모클래스를 상속받음 */
class Fighter extends Unit implements Fightable {
	public void move(int x, int y) { /* 메소드의 내용 생략 */ }
	public void attack(Unit u) { /* 메소드의 내용 생략 */ }
}

/* 메소드를 실행하는 클래스 */
class FighterTest {
	public static void main(String[] args) {
    	Fighter f = new Fighter();
   
        f.attack(f);
		f.move(0, 0);
	}
}

인터페이스를 구현한 클래스의 인스턴스를 생성하고,
구현부를 작성하지 않은 메소드를 사용해도 에러가 발생하지 않아요.

Fighter 클래스에서 선언만 해놓은 메소드의 구현과
FighterTest 클래스에서 메소드를 호출하는 코드를 동시에 작성하며
개발시간을 단축할 수 있어요.

2. 표준화 가능

프로젝트에 사용되는 상수와 메소드의 기본 틀을 인터페이스로 작성해놓고,
개발자들에게 인터페이스를 구현하여 프로그램을 개발하도록 하면
보다 일관되고 정형화된 프로그램의 개발이 가능하겠죠.

3. 서로 관계없는 클래스들의 관계를 맺어줌

상속관계가 아닌 클래스들에게
한 인터페이스를 공통적으로 구현하도록 함으로써
관계를 맺어줄 수 있겠죠.

4. 독립적인 프로그래밍

4-1. 다형성

Interface i = new ImplementedClass();

인터페이스도 다형성이 있어요.
이렇게 인터페이스를 구현한 클래스의 인스턴스 주소를(우항)
인터페이스 참조변수(좌항)에 저장할 수 있어요!!

다형성이 있다는 말은, 매개변수로도 인터페이스가 쓰일 수 있다는 건데요.
이때는 매개변수에 위치한 인터페이스를 구현한 클래스의 인스턴스만 올 수 있어요!

그리고 리턴타입으로 인터페이스가 쓰일 때는
인터페이스를 구현한 클래스의 인스턴스만 반환할 수 있다는 말이니
메소드 안에서 인터페이스를 구현한 클래스의 인스턴스를 생성하거나
매개변수로 인스턴스를 받아와야겠죠.

--

4-2. 독립적인 프로그래밍이 가능

클래스와 클래스 사이의 직접적인 관계를 클래스 -> 다른 클래스
인터페이스를 이용해서 간접적인 관계로 변경하면 클래스 -> 인터페이스 -> 다른 클래스
(= 매개변수를 클래스에서 인터페이스 타입으로 바꿈)

한 클래스의 변경이 다른 클래스에 영향을 미치지 않는
독립적인 프로그래밍이 가능해요.

직접적인 관계 예시

  • C클래스를 추가하여 사용하려고 하면, A클래스도 수정이 필요하다.
    매개변수를 변경하거나, 메소드 오버로딩을 하거나
    ( 매개변수가 클래스 타입이기 때문 )
class A {
	public void methodA(B b){	// 매개변수를 C c로 변경할 수도 있음
    	b.methodB();
    }
    /*
    public void methodA(C c){	// 메소드 오버로딩할 수도 있음
    	c.methodB();
    }
    */
}
//==============================================
class B {
	public void methodB(){
    	System.out.println("methodB()");
    }
}

/*
class C {
	public void methodB(){
    	System.out.println("methodB() in C");
    }
}
*/
//==============================================
class InterfaceTest {
	public static void main(String args[]) {
    	A.a = new A();
        a.methodA(new B()); // A클래스의 public void methodA(B b)로 이동
        // a.methodA(new C()); // A클래스의 public void methodA(C c)로 이동
    }
}

--

간접적인 관계 예시

  • C클래스를 추가하여 사용하려고 하면 추가하여 사용하기만 하면 된다. A클래스를 수정할 필요가 없다.
    ( 매개변수를 인터페이스로 했기 때문 - 다형성 )
class A {	// 수정필요 없음
	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");
    }
}
//==============================================
class InterfaceTest {
	public static void main(String args[]) {
    	A.a = new A();
        a.methodA(new B());	// A클래스의 public void methodA(I i)로 이동
        a.methodA(new C()); // A클래스의 public void methodA(I i)로 이동
    }
}


default method

인터페이스에 새로운 메소드를 추가한다고 해봅시다.

이미 구현된 인터페이스에 새로운 추상메소드를 추가하면,
이를 구현한 클래스에서도 메소드를 추가하는 불편함이 있겠죠.

그래서 디폴트 메소드라는 개념을 도입하여
인터페이스에 메소드를 추가해도 클래스에서 구현하지 않아도 되도록 했어요.
하지만 인터페이스가 추상메소드의 집합이라는 원칙을 어기게 되었죠.

몇 가지 특징을 볼게요.

  • 디폴트 메소드에는 구현부 { }가 있다.
    그래서 기존의 메소드와 충돌할 수 있다.

  • 서로 다른 인터페이스의 디폴트 메소드 간의 충돌:
    인터페이스를 구현한 클래스에서 디폴트 메소드를 오버라이딩해야 한다.

  • 디폴트 메소드와 조상클래스의 메소드 간의 충돌:
    조상클래스의 메소드가 상속되고, 디폴트 메소드는 무시된다.

  • 결국 상속이든 구현이든 오버라이딩하면 문제는 해결된다.


오늘은 여기까지입니다.
이제 내부클래스, 익명클래스만 정리하고
예외처리로 넘어가도록 하겠습니다!
화이팅!!

profile
성실하고 꼼꼼하게

0개의 댓글