interface
Interface
- 객체 사용 방법을 정의한 타입
- 개발 코드와 객체가 서로 통신하는 접점 역할
개발 코드가 인터페이스의 메소드를 호출하면 인터페이스는 객체의 메소드를 호출, 그렇기 때문에 개발 코드는 객체의 내부 구조를 알 필요가 없이 인터페이스의 메소드만 알고있으면 됨.
개발코드가 직접 객체의 메소드를 호출하면 간단하지만, 왜 중간에 인터페이스를 두는지 의문점이 생김 그 이유는 개발코드를 수정하지 않고, 사용하는 객체를 변경할 수 있도록 하기 위해서 임.
- 인터페이스는 하나의 객체가 아닌 여러 객체들과 사용이 가능하므로, 사용 객체에 따라 실행 내용과 리턴값이 다름 (코드 변경없이 다양화 가능하다는 장점있음.)
인터페이스 선언
접근제한자 interface 인터페이스명 {...}
- 영어 대소문자를 구분 하며, 첫문자를 대문자로 하고 나머지는 소문자로 작성하는것이 관례
- 인터페이스는 상수와 메소드만을 구성 멤버로 가짐.
interface 인터페이스명{ //상수 타입 상수명 = 값; //추상 메소드 타입 메소드명(매개변수, ...); //디폴트 메소드 default 타입 메소드명(매개변수){...} //정적 메소드 static 타입 메소드명(매개변수){...} }
상수 필드(Constant Field)
- 인터페이스는 객체 사용 설명서이므로 런타임 시 데이터를 저장할 수 있는 필드 선언 불가함.
- 상수 필드는 선언 가능, 상수는 인터페이스에 고정된 값으로 런타임 시 데이터를 바꿀수 없음
- 상수 선언 시 반드시 초기값 대입 해야함.
추상 메소드(Abstract Method)
- 추상 메소드는 객체가 가지고있는 메소드를 설명한 것, 호출 시 어떤 매개값이 필요하고, 리턴 타입이 무엇인지만 알려줌
- 실제 실행부는 객체(구현 객체) 가 가지고있음.
디폴트 메소드(Default Method)
- 디폴트 메소드는 인터페이스에 선언, 사실은 객체(구현 객체)가 가지고 있는 인스턴스 메소드라고 생각해야 함.
정적 메소드 (Static Method)
- 디폴트 메소드와는 달리 객체가 없어도 인터페이스만으로 호출이 가능
상수 필드 선언
public static final 타입 상수명 = 값;
- 상수명은 대문자로 작성하되, 서로 다른 단어로 구성되어 있을 경우에는 언더바(_)로 연결하는것이 관례
- 인터페이스 상수는 static{} 블록으로 초기화할 수 없기 때문에선언과 동시에 초기값을 지정
추상 메소드 선언
- 인터페이스를 통해 호출된 메소드는 최종적으로 객체에서 실행하므로 실행 블록이 필요없는 추상메소드로 선언
- 추상메소드는 리턴타입, 메소드명, 매개변수만 기술되고 중괄호 {} 를 붙이지 않는 메소드
- 인터페이스에 선언된 추상 메소드는 모두 public abstract 특성을 갖기 때문에 생략 되더라도 자동적으로 컴파일 과정에서 생성함.
디폴트 메소드 선언
- 클래스의 인스턴스 메소드와 동일한 형태, default 키워드가 리턴 타입 앞에 붙음.
- 디폴트 메소드는 public 특성을 갖기때문에 public을 생략하더라도, 자동적으로 컴파일 과정에서 붙게 됨.
[public] default 리턴타입 메소드명(매개변수,...){...}
정적 메소드 선언
- 클래스의 정적 메소드와 동일한 형태, 적적 메소드는 public 특성을 갖기 때문에 public 을 생략 하더라도, 자동적으로 컴파일 과정에서 붙게 됨.
[public] static 리턴타입 메소드명(매개변수){...}
인터페이스 구현
- 개발코드가 인터페이스 메소드 호출 시 인터페이스는 객체의 메소드를 호출함.
- 객체는 인터페이스에서 정의된 추상 메소드와 동일한 메소드 이름, 매개 타입, 리턴 타입을 가진 실체 메소드를 가지고 있어야 함.
- 이러한 객체를 인터페이스의 구현(implement) 객체 라고 하고, 구현 객체를 생성하는 클래스를 구현 클래스라고 함.
구현 클래스
- 인터페이스 타입으로 사용할 수 있음을 알려주기 위해 클래스 선언부에 implements 키워드를 추가하고 인터페이스명을 명시
public class 구현클래스명 implements 인터페이스명{
//인터페이스에 선언된 추상 메소드의 실체 메소드 선언
}
- 구현 클래스에서 인터페이스의 추상 메소드들에 대한 실체 메소드를 작성할 때 주의할점은 인터페이스의 모든 메소드는 기본적으로 public 접근 제한을 갖기 때문에 public 보다 더 낮은 접근 제한으로 작성 불가
- 인터페이스에 선언된 추상메소드에 대응하는 실체 메소드를 구현 클래스가 작성하지 않으면 구현 클래스는 자동적으로 추상클래스가 됨. 그렇기 때문에 클래스 선언부에 abstract 키워드를 추가해야함.
public abstract class Television implements RemoteControl{
public void turnOn(){...}
public void turnOff(){...} // setVolume() 실체 메소드가 없고, 일부만 구현
}
- 인터페이스 변수는 참조 타입이기때문에 구현 객체가 대입될 경우 구현 객체의 번지를 저장
- 구현객체를 인터페이스 변수에 대입해서 사용 ```java 인터페이스 변수; 변수 = 구현객체;
인터페이스 변수 = 구현객체;
## 익명 구현 객체
```java
인터페이스 변수 = new 인터페이스(){
//인터페이스에 선언된 추상메소드의 실체 메소드 선언
}; // 하나의 실행문이므로 끝에는 세미콜론(;)을 반드시 붙여야 함.
- UI 프로그래밍, 임시 작업 스레드를 만들기 위해 익명 구현 객체를 많이 활용
-
하나의 실행문 이므로 끝에는 세미콜론’;’ 을 반드시 붙여야 함.
- 인터페이스 (){}는 인터페이스를 구현해서 중괄호 {}와 같이 클래스를 선언하라는 뜻, new 연산자는 이렇게 선언 된 클래스를 객체로 생성함.
- 중괄호 {}에는 인터페이스에 선언된 모든 추상 메소드들의 실체 메소드를 작성해야 함
필드와 메소드를 선언할 수 있지만, 익명 객체 안에서만 사용할 수 있고 인터페이스 변수로 접근할 수 없음.
다중 인터페이스 구현 클래스
public class 구현클래스명 implements 인터페이스A, 인터페이스B{
// 인터페이스 A에 선언된 추상 메소드의 실체 메소드 선언
// 인터페이스 B에 선언된 추상 메소드의 실체 메소드 선언
}
- 인터페이스 A와 인터페이스 B가 객체의 메소드를 호출할 수 있으려면 객체는 이 두 인터페이스를 모두 구현해야함.
- 다중 인터페이스를 구현할 경우, 구현 클래스는 모든 인터페이스의 추상 메소드에 대해 실체 메소드를 작성 (만약 하나라도 없으면 추상 클래스로 선언해야 함)
인터페이스 사용
- 인터페이스 변수는 참조 타입이기때문에 구현 객체가 대입될 경우 구현 객체의 번지를 저장
인터페이스 변수;
변수 = 구현 객체;
---------------------
인터페이스 변수 = 구현 객체;
public class Myclass {
RemoteControl rc = new Television();
Myclass(RemoteControl rc){ //Myclass mc = new Myclass(new Television());
this.rc = rc;
}
void methodA(){
RemoteControl rc = new Audio();
}
void methodB(RemoteControl rc){...} // mc.methodB(new Audio());
}
추상 메소드 사용
- 구현 객체가 인터페이스 타입에 대입되면 인터페이스에 선언된 추상 메소드를 개발코드에서 호출할 수 있게됨.
RemoteControl rc = null; //인터페이스 변수 선언
rc = new Te(); // Te 객체를 인터페이스타입에 대입
rc.turnOn();
rc.turnOff();
디폴트 메소드 사용
RemoteControl rc = new Te();
rc.setMute(true);
- 디폴트 메소드는 인터페이스에 선언되지만, 인터페이스에서 바로 사용불가함. 구현 객체가 있어야 사용 가능함
- 구현 객체는 디폴트 메소드를 재정의(오버라이딩) 해서 자신에게 맞게 수정하면 디폴트 메소드가 호출될때 자신을 재정의한 메소드가 호출됨.
정적 메소드 사용
- 인터페이스의 정적 메소드(static method) 는 인터페이스로 바로 호출이 가능
RemoteControl.changeBattery();
타입 변환과 다형성
- 프로그램 소스 코드는 변함이 없는데, 구현 객체를 교체함으로써 프로그램의 실행결과가 달라짐
- 인터페이스의 다형성
- 상속은 같은 종류의 하위클래스를 만드는 기술, 인터페이스는 사용방법이 동일한 클래스를 만드는 기술
- 인터페이스 타입으로 매개변수를 선언하면 메소드 호출 시 매개값으로 여러가지 종류의 구현 객체를 줄 수 있기때문에 메소드 실행결과가 다양하게 나옴
- 매개변수의 다형성
자동타입 변환(Promotion)
인터페이스 변수 = 구현객체;
- 구현 객체가 인터페이스 타입으로 변환되는것은 자동타입변환에 해당함.
프로그램 실행 중 자동적으로 타입 변환이 일어나는것
- 필드와 매개변수의 타입을 인터페이스로 선언하면 다양한 구현 객체를 대입해서 실행결과를 다양하게 만들 수 있음.
인터페이스 배열로 구현 객체 관리
Tire[] tires = {
new HankookTire();
new HankookTire();
new HankookTire();
new HankookTire();
}
- 4개의 필드를 인터페이스로 각각 선언 할수도 있지만 위와같이 인터페이스 배열로 관리할 수도 있음.
매개변수의 다형성
- 자동 타입 변환은 필드의 값을 대입할 때에도 발생하지만, 주로 메소드를 호출할 때 많이 발생함.
- 매개변수를 인터페이스 타입으로 선언하고 호출할때는 구현 객체를 대입한다.
public class Driver{
public void drive(Vehicle vehicle) {
vehicle.run();
}
}
public interface Vehicle{
public void run();
}
Driver driver = new Driver();
Bus bus = new Bus();
dirver.drive(bus);
- 만약 Bus 가 구현 클래스라면 다음과 같이 Driver drive() 메소드를 호출할 때 Bus 객체를 생성해서 매개값으로 줄수 있음. drive() 메소드는 Vehicle 타입을 매개변수로 선언했지만, Vehicle을 구현한 Bus 객체가 매개값으로 사용되면 자동타입 변환이 발생함
Vehicle vehicle = bus
- 매개변수의 타입이 인터페이스일 경우, 어떠한 구현 객체도 매개값으로 사용할 수 있고, 어떤 구현 객체가 제공되느냐에 따라 메소드의 실행결과가 다양해짐(매개변수의 다형성)
강제 타입 변환(Casting)
- 구현 객체가 인터페이스 타입으로 자동 변환 시 인터페이스에 선언된 메소드만 사용 가능하다는 제약사항이 따름.
- 인터페이스에는 3개의 메소드가 있고 클래스에는 다섯개의 메소드가 선언되어있다면, 인터페이스로 호출 가능한 메소드는 3개뿐임.
구현클래스 변수 = (구현클래스) 인터페이스 변수;
객체 타입 확인(instanceof)
- 강제 타입 변환은 구현 객체가 인터페이스 타입으로 변환되어 있는 상태에서 가능
if( vehicle instanceof Bus){ Bus bus = (Bus) vehicle; }
인터페이스 상속
- 인터페이스도 다른 인터페이스를 상속 가능하며, 클래스와는 달리 다중 상속을 허용함.
public interface 하위인터페이스 extends 상위인터페이스1, 상위인터페이스2 {...}
- 하위 인터페이스를 구현하는 클래스는 하위 인터페이스의 메소드뿐만 아니라 상위 인터페이스의 모든 추상메소드에 대한 실체 메소드를 가지고있어야 함.
하위인터페이스 변수 = new 구현클래스(..); 상위인터페이스1 변수 = new 구현클래스(..); 상위인터페이스2 변수 = new 구현클래스(..);
하위 인터페이스로 타입 변환이 되면 상, 하위 인터페이스에 선언된 모든 메소드를 사용할 수 있으나, 상위 인터페이스로 타입 변환되면 상위 인터페이스에 선언된 메소드만 사용 가능하고, 하위 인터페이스에 선언된 메소드는 사용할 수 없다.
디폴트 메소드와 인터페이스 확장
- 디폴트 메소드는 인터페이스에 선언된 인스턴스 메소드이기 때문에 구현 객체가 있어야 사용할 수 있다.
디폴트 메소드의 필요성
- 인터페이스에서 디폴트 메소드를 허용한 이유는 기존 인터페이스를 확장해서 새로운 기능을 추가하기 위해서 임.
- 기존 인터페이스의 이름과 추상 메소드의 변경 없이 디폴트 메소드만 추가할 수 있기 때문에 이전에 개발한 구현 클래스를 그대로 하용할 수 있으면서 새롭게 개발하는 클래스는 디폴트 메소드를 활용할 수 있다.
public default void method(){ 실행문; }
디폴트 메소드가 있는 인터페이스 상속
- 부모 인터페이스에 디폴트 메소드가 정의되어 있을 경우, 자식인터페이스에서 디폴트 메소드를 활용하는 방법
- 디폴트 메소드를 단순히 상속만 받는다.
- 디폴트 메소드를 재정의(Override) 해서 실행 내용을 변경한다.
- 디폴트 메소드를 하위 인터페이스에서 추상 메소드로 재선언 한다.