목차
1. 인터페이스(interface)의 역할
인터페이스란?
- 객체의 사용 방법을 정의한 타입
- 객체의 교환성을 높여줌 → 다형성 구현시 매우 중요한 역할
- 개발 코드와 객체가 서로 통신하는 접점 역할
- 완벽히 추상화된 객체로, 추상 메소드(abstract method)의 집합
개발 코드가 인터페이스의 메소드를 호출하면 인터페이스는 객체의 메소드를 호출시킨다. 그렇기 때문에 개발 코드는 객체의 내부 구조를 알 필요가 없고 인터페이스의 메소드만 알고 있으면 된다.
개발 코드가 직접 객체의 메소드를 호출하면 간단한데, 왜 중간에 인터페이스를 두는 것일까?
→ 개발 코드를 수정하지 않더라도 객체를 변경할 수 있기 위함. 즉, 인터페이스는 여러 객체들과 사용이 가능하므로 어떤 객체를 사용하느냐에 따라 실행 내용과 리턴값이 달라질 수 있다.
인터페이스의 필요성
- 추상 메소드를 사용하여 구현을 강제로 표준화 처리 해준다.
- 인터페이스를 통한 간접적인 클래스 사용으로 모듈 교체를 용이하게 한다.
- 서로 상속의 관계가 없는 클래스들도 인터페이스를 사용하여 다형성을 확장시킬 수 있다.
- 모듈 간 독립적인 프로그래밍이 가능하므로 개발 기간을 단축시킬 수 있다.
2. 인터페이스 선언
인터페이스는 ~.java
형태의 소스 파일로 작성되고, 컴파일러(javac.exe
)를 통해 ~.class
형태로 컴파일 된다. 물리적 형태는 클래스와 동일하나, 소스를 작성할 때 선언하는 방법이 다르다.
2. 1 인터페이스 선언
interface
키워드를 이용하여 선언한다.
public interface MyInterface { ... }
클래스는 필드, 생성자, 메소드를 구성 멤버로 가진다. 반면, 인터페이스는 다음과 같은 특징을 갖고 있다.
인터페이스 특징
1. 상수와 메소드만을 구성 멤버로 가짐
이때, 선언되는 변수는 모두 상수로 적용됨
public static final int MEMBER1 = 10;
int MEMBER2 = 10;
선언되는 메소드 또한 모두 추상 메소드로 적용됨
public abstract void method1(int param);
void method2(int param);
2. 인터페이스는 객체로 생성할 수 없으므로 생성자를 가질 수 없음
interface MyInterface {}
public class Main {
MyInferface m = new MyInterface();
}
3. 자바 8부터 디폴트 메소드(Default Method)와 정적 메소드(Static Method)를 지원함
4. 인터페이스 extends
를 이용해서 상속 가능
이때, 클래스가 인터페이스를 상속할 경우에는 extends
키워드가 아니라 implements
키워드를 사용함.
인터페이스의 멤버 구성은 다음과 같다.
interface 인터페이스명 {
//상수
타입 상수명 = 값;
//추상 메소드
타입 메소드명(매개변수, ...);
//디폴트 메소드
default 타입 메소드명(매개변수,...) { ... }
//정적 메소드
static 타입 메소드명(매개변수) { ... }
}
1. 상수 필드(Constant Field)
인터페이스는 데이터를 저장할 수 없다. 따라서 데이터를 저장할 인스턴스 또는 정적 필드를 선언할 수 없다.
그러나 상수 필드는 선언이 가능하다.
public static final [타입] [CONSTANT_NAME] = [값];
상수명은 대문자로 작성하되, 2개 이상의 단어로 구성되어 있다면 언더바(_
) 로 연결한다.
또한, 인터페이스 상수는 static {}
블록으로 초기화할 수 없기 때문에 반드시 선언과 동시에 초기값을 지정해야 한다.
2. 추상 메소드(Abstract Method)
인터페이스를 통해 호출된 메소드는 최종적으로 객체에서 실행된다. 그렇기 때문에 인터페이스의 메소드는 실행 블록이 필요 없는 추상 메소드로 선언한다.
public abstract [리턴타입] [name](param1, param2 ..);
예시
public interface RemoteControl {
public void turnOn(); //인터페이스에 선언된 추상 메소드는 모두
public void turnOff(); //public abstract의 특성을 갖기 때문에
public void setVolume(int ㅔvolume); //생략하더라도 자동적으로 컴파일 과정에서 붙음
}
3. 디폴트 메소드(Default Method)
형태는 클래스의 인스턴스 메소드와 동일하나, default
키워드가 리턴 타입 앞에 붙는다.
public default [리턴타입] [name](param1, param2 ..) {...}
예시
interface RemoteControl {
default void setMute(boolean mute) {
if(mute) System.out.println("무음 처리합니다.");
else System.out.println("무음 해제합니다.");
}
}
4. 정적 메소드(Static Method)
형태는 클래스의 정적 메소드와 완전히 동일하다.
public static [리턴타입] [name](param1, ..) {...}
예시
public interface RemoteControl {
static void changeBattert() {
System.out.println("건전지를 교환합니다.");
}
}
3. 인터페이스 구현
개발코드가 인터페이스 메서드를 호출하면 인터페이스는 객체의 메소드를 호출한다. 객체는 인터페이스에서 정의된 추상 메소드와 동일한 메소드 이름, 매개 타입, 리턴 타입을 가진 실체 메소드를 가지고 있어야 한다. 이러한 객체를 인터페이스의 구현(implement
) 객체라고 하고, 구현 객체를 생성하는 클래스를 구현 클래스라고 한다.
1. 구현 클래스
구현 클래스는 인터페이스 타입으로 사용할 수 있음을 알려주기 위해 implements
키워드를 추가하고 인터페이스명을 명시해야 한다.
public class [구현클래스명] implements [인터페이스명] {
//인터페이스에 선언된 추상 메소드의 실체 메소드 선언
}
💡 실체 메서드 작성 시 주의할 점
→ public
보다 더 낮은 접근 제한으로 작성할 수 없음
왜냐하면 인터페이스의 모든 메소드는 기본적으로 public 접근 제한을 갖기 때문이다.
만약 인터페이스에 선언된 추상 메소드에 대응하는 실체 메소드를 구현 클래스에 작성하지 않으면 구현 클래스는 자동적으로 추상 클래스가(abstract
) 된다.
public abstract class Television implements RemoteControl {
public void turnOn() {...}
}
즉, 구현하지 않을 경우 abstract 클래스로 표시해야 한다.
이클립스는 인터페이스의 추상 메소드에 대한 실체 메소드를 자동으로 생성해주는 기능을 제공하고 있다.
자동 생성된 실체 메소드는 @Override
가 붙는다. @Override
는 인터페이스의 추상 메소드에 대한 정확한 실체 메소드인지 컴파일러가 체크하도록 지시하는 어노테이션이다.
implements
키워드가 붙은 하위클래스는 추상 메소드를 @Override
해주고 있다.
반면, 구현하지 않은 클래스는 abstract
클래스를 붙인 것을 확인할 수 있다.
인터페이스로 구현 객체를 사용하려면 인터페이스 변수를 선언하고, 구현 객체를 대입해야 한다. 인터페이스 변수는 참조 타입이기 때문에 구현 객체가 대입될 경우 구현 객체의 번지를 저장한다.
RemoteControl rc; //인터페이스 변수 선언
rc = new Television(); //구현 객체 대입1
rc = new Audio(); //구현 객체 대입2
2. 익명 구현 객체
: 소스 파일을 만들지 않고도 구현 객체를 만들 수 있다.
필드와 메소드를 선언할 수 있지만, 익명 객체 안에서만 사용할 수 있고, 인터페이스 변수로 접근할 수 없다.
작성 시 주의할 점은 하나의 실행문이므로 끝에는 세미콜론(;
)을 반드시 붙여야 한다.
선언 방법 예시
인터페이스 변수 = new 인터페이스() {
//인터페이스에 선언된 추상 메소드의 실체 메소드 선언
}; //<----세미콜론!!
3. 다중 인터페이스 구현 클래스
하나의 객체는 다음 그림과 같이 다수의 인터페이스 타입으로 사용할 수 있다.
다중 인터페이스를 구현할 경우, 구현 클래스는 모든 인터페이스의 추상 메소드에 대해 실체 메소드를 작성해야 한다.
다중 인터페이스 구현 예시
public class SmartTelevision implements RemoteControl, Searchable {
private int volume;
// 1) RemoteControl의 추상 메소드에 대한 실체 메소드
public void turnOn() {
System,out.println("TV를 켭니다.");
}
public void turnOff() {
System,out.println("TV를 끕니다.");
}
public void setVolume(int volume) {
if(volume>RemoteControl.MAX_VALUE) {
this.volume = RemoteControl.MAX_VALUE;
} else if(volume<RemoteControl.MIN_VALUE) {
this.volume = RemoteControl.MIN_VALUE;
} else {
this.volume = volume;
}
System.out.println("현재 TV 볼륨: " + volume);
}
// 2) Searchable의 추상 메소드에 대한 실체 메소드
public void search(String url) {
System.out.println(url + "을 검색합니다.");
}
}
4. 인터페이스 사용
인터페이스 변주는 참조 타입이기 때문에 구현 객체가 대입될 경우 구현 객체의 번지를 저장한다.
RemoteControl rc; //인터페이스 변수 선언
rc = new Television(); //구현 객체 대입1
rc = new Audio(); //구현 객체 대입2
1. 추상 메소드 사용
구현 객체가 인터페이스 타입에 대입되면 인터페이스에 선언된 추상 메소드를 호출할 수 있다.
2. 디폴트 메소드 사용
디폴트 메소드는 인터페이스에 선언되지만, 인터페이스에서 바로 사용할 수 없다.
이것은 디폴트 메소드가 추상 메소드가 아니라 인스턴스 메소드이기 때문이며, 구현 객체가 있어야 사용할 수 있다.
- 인터페이스에서 바로 사용 불가
- 오버라이딩해서 자신에게 맞게 수정하면 자신이 재정의한 메소드가 호출 됨
- 인스턴스 메소드이기 때문에 구현 객체가 있어야 사용 가능
//인터페이스에서 바로 사용 불가 (X)
RemoteControl.setMute(true);
//객체를 인터페이스 변수에 대입 후에 호출 (O)
RemoteControl rc = new Television();
rc.setMute(true);
3. 정적 메소드 사용
정적 메소드는 인터페이스로 바로 호출 가능하다.
5. 타입 변환과 다형성
상속은 같은 종류의 하위 클래스를 생성하는 것, 인터페이스는 사용 방법이 동일한 클래스를 생성하는 것이다.
상속과 인터페이스 둘 다 다형성을 구현한다.
프로그램을 개발할 때 인터페이스를 사용해서 메소드를 호출하도록 코딩을 했다면, 구현 객체를 교체하는 것은 쉽다.
💡 인터페이스의 다형성
프로그램 소스 코드 변함 X → 구현 객체 교체 → 다양한 프로그램 실행 결과
인터페이스는 메소드의 매개 변수로 많이 등장한다. 인터페이스 타입으로 매개 변수를 선언하면 메소드 호출 시 매개값으로 여러 가지 종류의 구현 객체를 중 수 있디 때문에 메소드 실행 결과가 다양하게 나온다.
1. 자동 타입 변환(Promotion)
: 인터페이스를 상속한 구현체가 인터페이스의 타입으로 타입 변환되는 것
인터페이스 구현 클래스를 상속해서 자식 클래스를 만들었다면, 자식 객체도 인터페이스 타입으로 자동 타입 변환할 수 있다.
2. 강제 타입 변환(Casting)
: 구현 클래스에 선언된 필드와 메소드를 사용해야 할 경우 강제 타입 변환
자동 타입 변환의 경우, 인터페이스에 선언된 메소드만 사용 가능하다는 제약이 따르기 때문에 그 이상의 필드나 메소드 사용이 필요하면 강제 타입 변환을 해야한다.
인터페이스가 자동 타입 변환된 상태라면, 다시 강제 타입 변환을 이용해 이전의 타입으로 되돌릴 수 있다.
3. 객체 타입 확인(instanceof)
인터페이스를 강제 타입 변환하기 전에 자동 타입 변환이 이루어진 상태인지 판단하기 위해 instanceof
로 확인 가능하다.
instanceof
는 객체 타입을 확인하는 연산자- 형변환 가능여부를 확인하며
true
/false
로 결과를 반환한다. - 주로 상속 관계에서 부모객체인지 자식객체인지 확인하는 데 사용된다.
class Parent{}
class Child extends Parent{}
public class InstanceofTest {
public static void main(String[] args){
Parent parent = new Parent();
Child child = new Child();
System.out.println( parent instanceof Parent ); // true
System.out.println( child instanceof Parent ); // true
System.out.println( parent instanceof Child ); // false
System.out.println( child instanceof Child ); // true
}
}
6. 인터페이스 상속
인터페이스도 다른 인터페이스를 상속할 수 있다. 인터페이스는 클래스와 달리 다중 상속을 허용 한다.
public interface 하위인터페이스 extends 상위인터페이스1, 상위인터페이스2 { ... }
7. 디폴트 메소드와 인터페이스 확장
디폴트 메소드는 인터페이스에 선언된 인스턴스 메소드이기 때문에 구현 객체가 있어야 사용할 수 있다.
디폴트 메소드 선언은 인터페이스에서 하고, 사용은 구현 객체를 통해 한다는 것이 어색하다. 그렇다면 디폴트 메소드는 왜 허용하는 것일까?
1. 디폴트 메소드의 필요성
- 기존 인터페이스를 확장해서 새로운 기능을 추가하기 위함
Q . 만일 인터페이스를 생성하고 구현체를 다 만들어둔 상황에서 해당 인터페이스를 상속한 모든 구현체에 기능 메소드를 추가해야 한다면?
A1 . 추상 메소드 추가
→ 모든 클래스를 돌아다니며 해당 메소드를 구현해야 함 (번거로움)
A2. 디폴트 메소드 추가
→ 디폴트 메소드의 오버라이드를 활용하면 쉽게 해결 가능
디폴트 메소드를 오버라이드해서 원하는 기능을 해당 클래스에 맞게 추가, 수정하면 된다.
'공부 > 이것이 자바다' 카테고리의 다른 글
[이것이 자바다] 16. 스트림과 병렬처리 (0) | 2022.09.14 |
---|---|
[이것이 자바다] 13. 제네릭(Generic) (1) | 2022.09.13 |
[이것이 자바다] 15. 컬렉션 프레임워크 1️⃣ List, Set, Map (1) | 2022.08.09 |
[이것이 자바다] 9. 중첩 클래스와 중첩 인터페이스 (0) | 2022.08.01 |
[이것이 자바다] 04. 조건문과 반복문 (0) | 2022.07.27 |