IT 개발노트
인터페이스란? 본문
1. 인터페이스
1.1 인터페이스란?
- 인터페이스(interface)는 일종의 추상클래스이며 추상클래스보다 추상화 정 도가 더 높다.
- 추상클래스를 미완성 설계도로 비유한다면 인터페이스는 기본 설계도에 비 유할 수 있다.
- 인터페이스는 다른 클래스 작성에 도움을 주거나 서로 무관한 클래스들에게 관계를 맺어줄 목적으로 사용된다.
1.2 인터페이스의 특징
- class 키워드 대신 interface 키워드를 사용하여 정의
- 인스턴스 생성 불가
- 모든 멤버변수에는 public static final 제어자가 붙는다(생략 가능)
- 모든 메서드는 public abstract 제어자가 붙는다(생략 가능)
* 제어자 생략시 컴파일러가 자동으로 추가
1.3 인터페이스 생성
1 2 3 4 | interface [인터페이스명] { public static final 타입 상수명 = 값; public abstract 메서드명(매개변수); } |
1.4 인터페이스의 상속
- 인터페이스는 인터페이스만 상속받을 수 있으며 추상클래스와는 달리 여러 개의 인터페이스에 대해 다중 상속이 가능하다.
- 인터페이스의 상속에는 extends 키워드를 사용한다.
예시 1
1 2 3 4 5 6 7 8 9 10 11 12 | interface Movable { void move(int x, int y); } interface Attackable { void attack(Unit unit); } interface Fightable extends Movable, Attackable { int x = 10; int y = 20; } |
* 인터페이스는 Object클래스와 같은 최고 조상이 없다.
* 인터페이스는 다중 상속을 위한 기능이 아니므로 자바에서 인터페이스로 다 중 상속을 구현하는 경우는 거의 없다.
1.5 인터페이스의 구현
- 인터페이스는 추상클래스처럼 그 자체로 인스턴스 생성이 불가능하다.
-> 추상메서드를 모두 구현하여 사용해야 한다.
- implements 키워드를 사용하여 인터페이스를 구현한다.
예시 2
1 2 3 | class [클래스명] implements [인터페이스명] { // 추상 메서드를 모두 구현. } |
* 인터페이스의 추상 메서드를 모두 구현하지 않은 경우, 해당 클래스에 abstract 키워드를 붙여서 추상클래스로 만들어야 한다.
예시 3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | interface Fightable { void move(int x, int y); void attack(Unit unit); } class Fighter implements Fightable { public void move(int x, int y) { // 내용 } public void attack(Unit unit) { // 내용 } } |
- 구현한 메서드에는 public제어자를 붙여줬는데 인터페이스의 추상 메서드 제어자가 public abstract이므로 이에 맞춰서 붙여준 것이다.
예시 4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | interface Movable { void move(int x, int y); } abstract class Attack { abstract public void attack(Unit unit); } class Fighter extends Attack implements Movable { public void move(int x, int y) { // 내용 } public void attack(Unit unit) { // 내용 } } |
- 예시 4처럼 상속과 구현을 동시에 하는 것도 가능하다.
1.6 인터페이스의 다형성
- 다형성의 특징처럼 부모타입의 참조변수로 자식 클래스의 인스턴스를 참조 하는 것이 인터페이스에서도 가능하다.
1.6.1 인터페이스 다형성의 특징
- 인터페이스 타입의 참조변수로 구현한 클래스의 인스턴스 참조 가능
- 인터페이스 타입으로 형변환 가능
- 인터페이스에도 매개변수의 다형성 특징이 적용
- 메서드의 리턴타입으로 인터페이스 타입 지정 가능
예시 5
1 | Fightable fightable = (Fightable) new Fighter(); |
- 인터페이스 타입의 참조변수로 구현한 클래스의 인스턴스 참조가 가능하다.
- Fightable 타입의 참조변수로는 Fightable 인터페이스에 정의된 멤버만 호출 이 가능하다.
예시 6
1 2 3 4 | Fightable method() { Fighter fighter = new Fighter(); return fighter; } |
- 메서드의 리턴타입으로 인터페이스 타입을 지정하는 것이 가능하다.
- 예시 6의 경우 메서드의 리턴타입이 인터페이스이기 때문에 return문에서 인터페이스를 구현한 클래스의 인스턴스를 반환하는 것을 확인할 수 있다.
* 메서드의 리턴타입이 인터페이스인 경우, 해당 인터페이스를 구현한 클래스 의 인스턴스를 반환한다는 것을 의미한다.
1.7 인터페이스의 장점
- 개발시간 단축된다.
-> 메서드 내용과 관계없이 선언부만 알면 개발이 가능하다.
-> 양쪽에서 동시에 개발 진행이 가능하다.
- 표준화가 가능하다.
-> 기본 틀을 인터페이스로 작성한다.
-> 일관되고 정형화된 프로그램 개발이 가능하다.
- 서로 무관한 클래스들에게 관계를 맺어준다.
-> 하나의 인터페이스를 공통으로 구현하도록 하여 관계를 맺어준다.
- 독립적인 프로그래밍이 가능
-> 인터페이스를 이용하면 클래스 간의 관계를 간접적으로 구성하는 것이 가능하다.
1.8 인터페이스의 이해
- 클래스에 대해서 User(사용자)와 Provider(제공자)가 있다.
- User(사용자)에서는 Provider(제공자) 메서드의 선언부만 알면된다.
- 클래스간의 관계에서 직접적인 관계의 경우 한쪽이 변경되면 다른 한쪽도 변경되어야 하는 단점을 가지고 있다. 그러나 인터페이스를 매개체로 한 간 접적인 관계의 경우 한쪽이 변경되어도 다른 한쪽은 전혀 영향을 받지 않는 다.
예시 7
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // 클래스 B의 메서드를 추상 메서드로 정의한 인터페이스 I interface I { public abstract void methodB(); } // 클래스 B가 인터페이스 I를 구현 class B implements I { public void methodB() { System.out.println("methodB in B class"); } } // 클래스 A는 인터페이스 I를 사용 가능 class A { public void methodA(I i) { i.methodB(); } } |
- 예시 7은 'A-I-B'의 간접적인 관계로 구성되었으며 클래스 A는 인터페이스 I 하고만 직접적인 관계가 있기 때문에 클래스 B의 변경에 영향을 받지 않는 다.
- 클래스 A는 직접적인 관계의 인터페이스 I의 영향만 받기 때문에 실제로 사 용되는 클래스명을 몰라도 되고 구현된 클래스가 존재하지 않아도 문제가 되지 않는다.
- 인터페이스 I는 클래스 B를 감싸고 있는 껍데기이며 클래스 A는 인터페이스 I 안에 어떤 클래스가 구현되어 있는지 몰라도 되는 관계가 된다.
1.9 디포틀 메서드와 static 메서드
- 인터페이스는 원래 상수와 추상메서드만을 멤버로 가졌었는데 JDK1.8 이후 디폴트 메서드와 static 메서드도 추가할 수 있게 되었다.
- static 메서드는 인스턴스와 무관한 독립적인 메서드이기 때문에 인터페이스 에 추가 못할 이유가 없었지만, 자바의 규칙을 단순화하기 위해 인터페이스 의 모든 메서드를 추상 메서드로 제한하여 제외되었습니다.
- 인터페이스의 static 메서드는 접근제어자도 마찬가지로 항상 public이며 생 략이 가능하다.
- 디폴트 메서드는 추상 메서드와 static 메서드 외에 인터페이스에 추가할 수 있는 메서드이며, 인터페이스에 추가해도 구현한 클래스의 변경없이 사용이 가능하다.
예시 8
1 2 3 4 5 | interface I { public abstract void abstractMethod(); public default void defaultMethod() { } } |
- 예시 8과 같이 default 키워드를 앞에 붙여서 사용한다.
- 디폴트 메서드 역시 접근제어자가 public이며 생략이 가능하다.
- 디폴트 메서드의 경우 기존의 메서드와 메서드명이 중복되는 경우가 발생하는데, 이에 대한 규칙은 아래와 같다.
1. 여러 인터페이스 구현시 디폴트 메서드명이 같은 경우
-> 인터페이스를 구현한 클래스에서 디폴트 메서드를 오버라이딩 해야한다.
2. 부모 클래스의 메서드와 디폴트 메서드명이 같은 경우
-> 부모 클래스의 메서드가 상속되고 디폴트 메서드는 무시된다.
예제 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | class DefaultMethodTest { public static void main(String[] args) { Child child = new Child(); child.method1(); child.method2(); Interface1.staticMethod(); Interface2.staticMethod(); } } class Parent { // 인터페이스 Inteface1의 디폴트 메서드와 메서드명이 같으므로 // 부모 클래스의 메서드가 상속됨. public void method2() { System.out.println("method2() in Parent"); } } class Child extends Parent implements Interface1, Interface2{ // 인터페이스 Interface1, Interface2의 디폴트 메서드명이 같으므로 // 아래와 같이 구현한 클래스에서 오버라이딩 해줌. public void method1() { System.out.println("method1() in Child"); } } interface Interface1 { default void method1() { System.out.println("method1() in Interface1"); } default void method2() { System.out.println("method2() in Interface1"); } static void staticMethod() { System.out.println("staticMethod() in Interface1"); } } interface Interface2 { default void method1() { System.out.println("method1() in Interface2"); } static void staticMethod() { System.out.println("staticMethod() in Interface2"); } } |