-
[Java] 인터페이스[Java] 2023. 10. 26. 12:42
<목차>
8.1 인터페이스의 역할
8.2 인터페이스 선언
8.3 인터페이스 구현
8.4 인터페이스 사용
8.5 타입 변환과 다형성
8.6 인터페이스 상속
8.7 디폴트 메소드와 인터페이스 확장
8.1 인터페이스의 역할
객체의 사용 방법을 정의한 타입.
=> 객체의 호환성을 높여주기 때문에 다형성을 구현하는 매우 중요한 역할.
(자바 8 의 람다식은 함수적 인터페이스의 구현 객체를 생성하기 때문에 더 중요 뭐시기 라는데 뭐라는지 이해 안됨)
"개발 코드와 객체가 서로 통신하는 접점 역할을 함."
인터페이스는 하나의 객체가 아니라 여러 객체들과 사용이 가능함.
개발 코드와 객체 사이에 인터페이스를 두는 이유
=> 개발 코드를 수정하지 않고 사용하는 객체를 변경할 수 있도록 하기 위해서이다.
8.2 인터페이스 선언
[ public ] interface 인터페이스명 { ... }
interface 인터페이스명 { // 상수 타입 상수명 = 값; // 추상 메소드 타입 메소드명(매개변수, ...); // 디폴트 메소드 default 타입 메소드명 (매개변수, ...) {...} // 정적 메소드 static 타입 메소드명 (매개변수) {...} }
- 상수 필드 (Constant Field)
런타임 시 데이터를 저장할 수 있는 필드를 선언 가능하다. (원래 안됨)
상수는 인터페이스에 고정된 값으로 런타임 시에 데이터를 바꿀 수 X => 상수를 선언할 때 반드시 초기값 대입할 것
- 추상 메소드 (Abstract Method)
객체가 가지고 있는 메소드를 설명한 것
=> 어떤 매개값이 필요하고 리턴 타입이 무엇인지만 알려줌.
실제 실행부는 구현 객체가 가지고 있음.
- 디폴트 메소드 (Default Method)
인터페이스에 선언되지만 객체가 가지고 있는 인스턴스 메소드라고 생각할 것.
=> 기존 인터페이스를 확장해서 새로운 기능을 추가하기 위함.
- 정적 메소드 (Static Method)
디폴트 메소드와는 달리 객체가 없어도 인터페이스만으로 호출이 가능함.
상수 필드 선언
// [ public static final ] 타입 상수명 = 값;
public interface RemoteControl {public int MAX_VOLUME = 10;public int MIN_VOLUME = 0;}상수 필드만 데이터를 저장할 인스턴스 또는 정적 필드를 선언할 수 있음.
상수는 public static final 로 선언하는데 인터페이스에 선언된 필드는 모두 public static final 의 특성을 갖는다.
public static final 을 생략하더라도 자동적으로 컴파일 과정에서 붙게 됨.
상수명은 대문자로 작성, 서로 다른 단어로 구성되어 있을 경우 언더바로 연결하는 것이 관례.
ex) MODEL, MAX_VALUE ..
static { } 블록으로 초기화할 수 없기 때문에 반드시 위와 같이 선언과 동시에 초기값을 지정해야 한다.
추상 메소드 선언
"메소드 선언부만 작성"
public interface RemoteControl {// 상수public int MAX_VOLUME = 10;public int MIN_VOLUME = 0;
// 추상 메소드public void turnOn();public void turnOff();public void setVolume (int volume);}리턴 타입이 void, 메소드는 호출 시 매개값 필요 X, setVolume() 메소드만 int 매개값이 필요함을 알려주고 있음.
인터페이스의 메소드는 실행 블록이 필요 없는 추상 메소드로 선언한다.
public abstract 를 생략하더라도 자동적으로 컴파일 과정에서 붙게 된다.
디폴트 메소드 선언
"실행 내용까지 작성"
public interface RemoteControl {// 상수public int MAX_VOLUME = 10;public int MIN_VOLUME = 0;
// 추상 메소드public void turnOn();
public void turnOff();
public void setVolume(int volume);
// 디폴트 메소드 [public] default 리턴타입 메소드명 (매개변수, ...) {...}default void setMute(boolean mute) {if (mute) {System.out.println("무음 처리합니다.");} else {System.out.println("무음 해제합니다.");}}}형태는 클래스의 인스턴스 메소드와 동일, default 키워드가 리턴 타입 앞에 붙는다.
public 의 특성을 가져 생략하더라도 자동적으로 컴파일 과정에 붙음.
정적 메소드 선언
public interface RemoteControl {// 상수public int MAX_VOLUME = 10;public int MIN_VOLUME = 0;
// 추상 메소드public void turnOn();
public void turnOff();
public void setVolume(int volume);
// 디폴트 메소드default void setMute(boolean mute) {if (mute) {System.out.println("무음 처리합니다.");} else {System.out.println("무음 해제합니다.");}}
// 정적 메소드static void changeBattery() {System.out.println("건전지를 교체합니다.");}}정적 메소드도 public 의 특성을 갖기 때문에 생략해도 컴파일 과정에서 붙게 됨.
8.3 인터페이스 구현
개발 코드가 인터페이스 메소드를 호출하면 인터페이스는 객체의 메소드를 호출.
(객체는 인터페이스에서 정의된 추상 메소드와 동일한 메소드 이름, 매개 타입, 리턴 타입을 가진 실체 메소드를 가지고 있어야됨)
이러한 객체를 인터페이스의 구현 객체라고 하고, 구현 객체를 생성하는 클래스를 구현 클래스 라고 한다.
구현 클래스
보통의 클래스와 동일한데, 클래스 선언부에 implements 키워드가 있냐 없냐 와 인터페이스명을 명시하냐 안 하냐의 차이다.
public class 구현클래스명 implements 인터페이스명 { // 인터페이스에 선언된 추상 메소드의 실체 메소드 선언 }
public class Television implements RemoteControl { // 필드 private int volume; // turnOn() 추상 메소드의 실체 메소드 public void turnOn() { System.out.println("TV 를 켭니다. "); } // turnOff() 추상 메소드의 실체 메소드 public void turnOff() { System.out.println("TV 를 끕니다. "); } // setVolume() 추상 메소드의 실체 메소드 public void setVolume(int volume) { if (volume > RemoteControl.MAX_VOLUME) { this.volume = RemoteControl.MAX_VOLUME; } else if (volume < RemoteControl.MIN_VOLUME) { this.volume = RemoteControl.MIN_VOLUME; } else { this.volume = volume; } System.out.println("현재 TV 볼륨 :: " + volume); } }
+ 실체 메소드를 작성할 때 주의할 점 : public 보다 더 낮은 접근 제한으로 작성할 수 없다. public 을 생략하면 컴파일 에러 발생함.
인터페이스에 선언된 추상 메소드에 대응하는 실체 메소드를 구현 클래스가 작성 X => 구현 클래스는 자동적으로 추상 클래스가 됨.
=> 그렇기 때문에 클래스 선언부에 abstract 키워드를 추가 한다.
public abstract class Television implements RemoteControl { public void turnOn() {...} public void turnOff() {...} }
익명 구현 객체
익명 구현 객체란?
소스 파일을 만들지 않고도 구현 객체를 만들 수 있는 방법이다.
=> UI 프로그래밍에서 이벤트를 처리하기 위해, 그리고 임시 작업 스레드를 만들기 위해 많이 사용한다.
인터페이스 변수 = new 인터페이스() { // 인터페이스에 선언된 추상 메소드의 실제 메소드 선언 };
중괄호 안에는 인터페이스에 선언된 모든 추상 메소드들의 실체 메소드를 작성해야 한다. 그렇지 않으면 컴파일 에러가 발생한다.
+ 필드와 메소드를 선언할 수 있지만, 익명 객체 안에서만 사용할 수 있고 인터페이스 변수로 접근할 수 없다.
public class RemoteControllerExample { public static void main(String[] args) { RemoteControl rc = new RemoteControl() { public void turnOn() { /* 실행문 */} public void turnOff() { /* 실행문 */} public void setVolume(int volume) { /* 실행문 */ } }; } }
위의 코드를 컴파일하면
RemoteControllerExample$1.class 라는 클래스 파일이 만들어진다.
이름 뒤에 $ 가 붙고 생성 번호가 붙는데 번호는 1번부터 시작한다.
다중 인터페이스 구현 클래스
인터페이스 A 와 B 가 객체의 메소드를 호출할 수 있으려면 객체는 이 두 인터페이스를 모두 구현해야 한다.
public class 구현클래스명 implements 인터페이스A, 인터페이스B { // 인터페이스 A에 선언된 추상 메소드의 실체 메소드 선언 // 인터페이스 B에 선언된 추상 메소드의 실체 메소드 선언 }
다중 인터페이스를 구현할 경우, 구현 클래스는 모든 인터페이스의 추상 메소드에 대해 실체 메소드를 작성해야 함.
만약 하나라도 없으면 추상 클래스로 선언해야 한다.
여기서 추상 클래스란?
- 실체클래스의 공톡적이 부분을 추출해서 선언한 클래스
- 추상클래스는 객체를 생성할 수 X. 아직은 실체성이 없고 구체적이지 않기 때문.
- 추상클래스와 실체클래스 => 상속관계
public interface Searchable { void search(String url); }
search () 추상 메소드는 매개값으로 URL 을 받는다.
ex)
public class SmartTelevision implements RemoteControl, Searchable { private int volume; public void turnOn() { System.out.println("TV 를 켭니다. "); } public void turnOff() { System.out.println("TV 를 끕니다. "); } public void setVolume(int volume) { if (volume > RemoteControl.MAX_VOLUME) { this.volume = RemoteControl.MAX_VOLUME; } else if (volume < RemoteControl.MIN_VOLUME) { this.volume = RemoteControl.MIN_VOLUME; } else { this.volume = volume; } System.out.println("현재 TV 볼륨: " + volume); } public void search(String url) { System.out.println(url + " 을 검색합니다. "); } }
8.4 인터페이스 사용
인터페이스로 구현 객체를 사용하려면 인터페이스 변수를 선언하고 구현 객체를 대입해야 한다.
인터페이스 변수는 참조 타입이기 때문에 구현 객체가 대입될 경우 구현 객체의 번지를 저장.
인터페이스 변수;
변수 = 구현객체;
인터페이스 변수 = 구현객체;
개발 코드에서 인터페이스는 클래스의 필드 / 생성자 / 메소드의 매개 변수 / 메소드의 생성자 / 메소드의 로컬 변수로 선언 가능.
public class MyClass { // 필드 RemoteControl rc = new Television(); // 생성자 MyClass(RemoteControl rc) { this.rc = rc; } // 메소드 void methodA() { // 로컬 변수 RemoteControl rc = new Audio(); } void methodB(RemoteControl rc) { } }
추상 메소드 사용
구현 객체가 인터페이스 타입에 대입되면 인터페이스에 선언된 추상 메소드를 개발 코드에서 호출 할 수 있게 된다.
public class RemoteControllerExample { public static void main(String[] args) { RemoteControl rc = null; // 인터페이스 변수 선언 rc = new Television(); // Television 객체를 인터페이스 타입에 대입 rc.turnOn(); // 인터페이스 호출 rc.turnOff(); // 인터페이스 호출 rc = new Audio(); // Audio 객체를 인터페이스 타입에 대입 rc.turnOn(); // 인터페이스 호출 rc.turnOff(); // 인터페이스 호출 } }
<결과>
TV 를 켭니다.
TV 를 끕니다.
Audio 를 켭니다.
Audio 를 끕니다.
디폴트 메소드 사용
디폴트 메소드는 인터페이스에서 바로 사용할 수 없다. => 구현 객체가 있어야 사용 가능
디폴트 메소드 = ' 인터페이스의 모든 구현 객체가 가지고 있는 기본 메소드 ' 라고 생각하면 편함.
만약 구현 객체가 수정이 필요할 때, 구현 클래스를 작성하면서 디폴트 메소드를 오버라이딩 해서 자신에게 맞게 수정하면 자신을 재정의한 메소드가 호출된다.
public class RemoteControllerExample { public static void main(String[] args) { RemoteControl rc = null; rc = new Television(); rc.turnOn(); rc.setMute(true); rc = new Audio(); rc.turnOn(); rc.setMute(true); } }
<결과>
TV 를 켭니다.
무음 처리합니다.
Audio 를 켭니다.
Audio 무음 처리합니다.
정적 메소드 사용
인터페이스의 정적 메소드는 인터페이스로 바로 호출이 가능하다.
ex)
RemoteControl.changeBattery();
8.5 타입 변환과 다형성
다형성 : 하나의 타입에 대입되는 객체에 따라 실행 결과가 다양한 형태로 나오는 성질
상속 : 같은 종류의 하위 클래스를 만드는 기술
인터페이스 : 사용 방법이 동일한 클래스를 만드는 기술
=> 개념적 차이는 있지만 둘 다 다형성을 구현하는 기술임.
인터페이스를 사용해서 메소드를 호출하도록 코딩을 하면 좋은 점
- 보다 간단하게 메소드 호출 코드는 수정할 필요 없이 객체 생성 부분만 교체가 가능함.
- 메소드의 매개 변수로 활용 가능 => 메소드 호출 시 매개값으로 여러 가지 종류의 구현 객체를 줘 메소드 실행 결과가 다양해짐.
ex)
Ex i = new A(); //=> Ex i = new B(); 로 수정Ex i = new B();
i.method1();
i.method2();
interface Ex {
void method01();
void method02();
}
자동 타입 변환
인터페이스 구현 클래스를 상속해서 자식 클래스를 만들었다면 자식 객체 역시 인터페이스 타입으로 자동 타입 변화시킬 수 있다.
Ex)
B b = new B();
C c = new C();
D d = new D();
E e = new E();
ㄴ>
A a1 = b; (가능)
A a2 = c; (가능)
A a3 = d; (가능)
A a4 = e; (가능)
'[Java]' 카테고리의 다른 글
예외 처리 (6) 2023.07.03 [JAVA] 매개 변수의 수를 모를 때 (4) 2023.07.03 this 와 this() 의 용도 (6) 2023.05.21