영권's
8주자 과제: 인터페이스 본문
목표
자바의 인터페이스에 대해 학습하세요.
학습할 것 (필수)
- 인터페이스 정의하는 방법
- 인터페이스 구현하는 방법
- 인터페이스 레퍼런스를 통해 구현체를 사용하는 방법
- 인터페이스 상속
- 인터페이스의 기본 메소드 (Default Method), 자바 8
- 인터페이스의 static 메소드, 자바 8
- 인터페이스의 private 메소드, 자바 9
인터페이스란?
자바에서 인터페이스는 객체의 사용 방법을 정의한 타입이다.
인터페이스는 객체의 교환성을 높여주기 때문에 다형성을 구현한느 매우 중요한 역할을 한다.
인터페이스의 역할
인터페이스는 개발 코드와 객체가 서로 통신한는 접점 역할을 한다.
개발 코드가 인터페이스의 메소드를 호출하면 인터페이스는 객체의 메서드를 호출시킨다. 그렇기 때문에 개발 코드는 객체의 내부 구조를 알 필요가 없고 인터페이스의 메서드만 알고 있으면 된다.
개발 코드가 직접 객체의 메서드를 호출하면 간단한데, 왜 중간에 인터페이스를 둘까?
=> 그 이유는 개발 코드를 수정하지 않고, 사용하는 객체를 변경할 수 있도록 하기 위해서이다.
인터페이스는 하나의 객체가 아니라 여러 객체들과 사용이 가능하므로 어떤 객체를 사용하느냐에 따라 실행 내용과 리턴값이 다를 수 있다. 따라서 개발 코드 측면에서는 코드 변경 없이 실행 내용과 리턴값을 다양화 할 수 있다는 장점을 가지게 된다.
-
인터페이스 정의하는 방법
인터페이스는 "~java" 형태의 소스 파일로 작성되고 컴파일러를 통해 "~.class" 형태로 컴파일되기 때문에 물리적 형태는 클래스와 동일하다.
차이점은 소스를 작성할 때 선언하는 방법이 다르다.
인터페이스 선언
- 인터페이스 선언은 class 대신에 interface 키워드를 사용한다.
[public] interface 인터페이스명 {...}
인터페이스 이름은 클래스 이름을 작성하는 방법과 동일하며 public 접근제한자는 다른 패키지에서도 인터페이스를 사용할 수 있도록 해준다.
클래스는 필드, 생성자, 메서드를 구성 멤버로 가지는데 비해, 인터페이스는 상수와 메서드만을 구성 멤버로 가진다.
인터페이스는 객체로 생성할 수 없기 때문에 생성자를 가질 수 없다.
자바7 이전까지는 인터페이스의 메서든느 실행 블록이 없는 추상 메서드로만 선언이 가능했지만 자바8부터는 디폴트 메서드와 정적(static) 메서드, 자바9부터는 private 메서드도 선언이 가능하다.
상수 필드
- 인터페이스는 객체 사용 설명서 이므로 런타임 시 데이터를 저장할 수 있는 필드를 선언할 수 없다.
- 그러나, 상수 필드는 선언이 가능하다.
- 상수는 인터페이스에 고정된 값으로, 런타임 시에 데이터를 바꿀 수 없기 때문이다.
- 상수 선언하려면, 반드시 선언 할 때 초기값을 대입해야 한다.
추상 메서드
- 객체가 가지고 있는 메소드를 설명한 것으로,
- 호출 시, 어떤 매개값이 필요하고, 리턴 타입이 무엇인지만 알려준다
- 실제 실행부는 객체 (구현 객체)가 가지고 있다
디폴트 메서드
- 디폴트 메서드는 인터페이스에 선언되지만 사실을 객체(구현 객체)가 가지고 있는 인스턴스 메서드라고 생각해야 한다.
- 자바8 에서 디폴트 메서드를 허용한 이유는 기존 인터페이스를 확장해서 새로운 기능을 추가하기 위해서이다.
정적 메서드
- 정적 메서드 역시 자바8부터 작성할 수 있는데, 디폴트 메서드와는 달리 객체가 없어도 인터페이스 만으로 호출이 가능하다.
Private Method
- 자바 9부터 작성 가능
Private Static Method
- 자바 9부터 작성 가능
인터페이스는 추상 클래스와 같이 추상 메소드를 가지므로 추상 클래스와 매우 흡사하다.
인터페이스는 추상 클래스와 같이 인스턴스를 생성할 수 없고, 상속받은 클래스에서 구현한 뒤 자깃 클래스를 인스턴스화 하여 사용한다.
추상클래스와 인터페이스의 차이점
-
추상 클래스는 일반 메서드와 추상 메서드 둘다 가질 수 있다.
→ 인터페이스는 오로지 추상 메서드와 상수만을 가진다. (구현 로직을 작성할 수 없다.)
but. java8 부터는 default method와 static method를 작성할 수 있다.
-
인터페이스 내에 존재하는 메서드는 무조건 "public abstract" 로 선언되며, 이를 생략할 수 있다.
-
인터페이스 내에 존재하는 변수는 무조건 "public static final" 로 선언되며, 이를 생략할 수 있다.
추상클래스와 인터페이스의 의미를 명확히 알고 접근하자.
(기본적인 것 부터...)
추상클래스는 "is-a : ~는 ~이다" 의 개념이다.
인터페이스는 "has-a : ~는 ~를 할 수 있다." 의 개념이다.
ex. SSON은 사람 Person 이면서 코딩(Developable)을 할 수 있다.
- class SSON extends Person implements Developable
인터페이스 사용의 이유
-
개발 기간을 단축 시킬 수 있다.
이러한 특징을 가진 인터페이스를 사용하면 다른 개발자들이 각각의 부분을 완성할 때까지 기다리지 않고 서로 규약만 정해두어 각자의 부분만 따로 나눠서 작성된 코드를 컴파일 할 수 있다.
-
클래스간 결합도를 낮출 수 있다.
코드의 종속성을 줄이고 유지보수성을 높이도록 해준다.
-
표준화가 가능하다.
클래스의 기본틀을 제공하여 개발자들에게 정형화된 개발을 강요할 수 있다.
→ 자바의 다형성을 극대화 하여 코드의 수정을 줄이고 유지보수성을 높인다.
인터페이스 선언
package me.study.week8;
public interface RemoteControl {
// 상수
int MAX_VALUE = 10;
int MIN_VALUE = 0;
// 추상 메서드
void turnOn();
void turnOff();
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 static final로 선언합니다.
- 인터페이스는 선언된 필드는 모두 public static final의 특성을 갖는다.
- public, static, final을 생략하더라도 자동적으로 컴파일 과정에서 붙게된다.
- 상수는 대문자로 작성하되, 서로 다른 단어로 구성되어 있을 경우 언더바(_)로 연결하는 것이 관례이다.
추상 메서드 선언
- 인터페이스를 통해 호출된 메서드는 최종적으로 객체에서 실행된다.
- 그렇기 때문에 메서드는 실행 블록이 필요없는 추상 메서드로 선언한다.
- 인터페이스에서 선언된 추상 메서드는 모두 public abstract의 특성을 갖기 때문에 public abstract를 생략하더라도 자동적으로 컴파일 과정에서 붙게된다.
디폴트 메서드 선언
- 디폴트 메서드는 자바8에서 추가된 인터페이스의 새로운 멤버이다.
- 형태는 클래스의 인스턴스 메서드와 동일한데, default 키워드가 리턴 타입 앞에 붙는다.
- 디폴트 메서드는 public 특성을 갖기 때문에 생략하더라도 자동적으로 컴파일 과정에서 붙게된다.
정적 메서드 선언
- 정적 메서드는 자바8에서 추가된 인터페이스의 새로운 멤버이다.
- 형태는 클래스의 정적 메서드와 완전 동일하다.
- 정적 메서드는 public 특성을 갖기 때문에 생략하더라도 자동적으로 컴파일 과정에서 붙게된다.
인터페이스와 그에 해당하는 바이트코드를 보면 상수, 추상 메서드, 디폴트 메서드에 각각 public final static, public abstract, public 키워드가 붙은것을 볼 수 있다.
-
인터페이스 구현하는 방법
개발 코드가 인터페이스 메서드를 호출하면 인터페이스는 객체의 메서드를 호출한다.
객체는 인터페이스에서 정의된 추상 메서드와 동일한 메서드 이름, 매개 타입, 리턴 타입을 가진 실체 메서드를 가지고 있어야한다.
이러한 객체를 인터페이스의 구현(implement) 객체라고하고, 구현 객체를 생성하는 클래스를 구현 클래스라고 한다.
구현 클래스
구현 클래스는 보통의 클래스와 동일한데, 인터페이스 타입으로 사용할 수 있을을 알려주기 위해 클래스 선언부에 implements 키워드를 추가하고 인터페이스 명을 명시해야 한다.
예시)
package me.study.week8;
public class Television implements RemoteControl{
private int volume;
// 추상 메서드의 실체 메서드
@Override
public void turnOn() {
System.out.println("TV를 켭니다");
}
@Override
public void turnOff() {
System.out.println("TV를 끕니다.");
}
@Override
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 볼륨 : " + this.volume);
}
}
메서드 구현시 주의점
- 구현 클래스에서 인터페이스의 추상 메서드들에 대한 실체 메서드는 public 접근 제한을 갖기 때문에 public 보다 낮은 접근 제한으로 작성할 수 없다.
- public 을 생략하면 컴파일 에러가 발생한다.
-
만약, 인터페이스에 선언된 추상 메소드에 대응하는 실체 메소드를 구현 클래스가 작성하지 않으면
- 구현 클래스는 자동적으로 추상 클래스가 되기 때문에 abstract 키워드를 클래스 선언부에 추가해야한다.
익명 구현 객체
구현 클래스를 만들어 사용하는 것이 일반적이고, 재사용할 수 있기 때문에 편리하지만, 일회성의 구현 객체를 만들기 위해 소스 파일을 만들고 클래스를 선언하는 것은 비효율적이다. 자바는 소스 파일을 만들지 않고도 구현 객체를 만들 수 있는 방법을 제공하는데, 그것이 익명 구현 객체이다.
자바는 UI 프로그래밍에서 이벤트를 처리하기 위해, 그리고 임시 작업 스레드를 만들기 위해 익명 구현 객체를 많이 활용한다.
자바8에서 지원하는 람다식은 인터페이스의 익명 구현 객체를 만드는 것이다.
예시)
인터페이스 변수명 = new 인터페이스(){
// 인터페이스에 선언된 추상 메서드의 실체 메서드 선언
};
다중 인터페이스 구현 클래스
객체는 다수의 인터페이스 타입으로 사용될 수 있다.
인터페이스A 와 인터페이스B 가 객체의 메서드를 호출 할 수 있으려면 객체는 두 인터페이스를 모두 구현해야 한다.
따라서 구현 클래스는 다음과 같이 작성되어야 한다.
public class 구현클래스명 implements 인터페이스A, 인터페이스B{
// 인터페이스 A에 선언된 추상 메서드의 실체 메서드
// 인터페이스 B에 선언된 추상 메서드의 실체 메서드
}
-
인터페이스 레퍼런스를 통해 구현체를 사용하는 방법
인터페이스로 구현 객체를 사용하려면 다음과 같이 인터페이스 변수를 선언하고 구현 객체를 대입해야 한다.
인터페이스 변수는 참조 타입이기 때문에 구현 객체가 대입 될 경우 구현 객체의 번지를 저장한다.
// 인터페이스 변수 = 구현객체;
// ex
RemoteControl rc = new Television();
rc.turnOn();
rc.turnOff();
구현 객체가 인터페이스 타입에 대입되면 인터페이스에 선언된 추상 메서드를 개발 코드에서 호출 할 수 있게 된다.
개발 코드에서 인터페이스의 추상 메서드를 호출하게 되면 해당 구현 객체의 실체 메서드가 실행된다.
디폴트 메서드는 인터페이스에 선언되지만, 인터페이스에서 바로 사용할 수 없다.
디폴트 메서드는 추상 메서드가 아닌 인스턴스 메서드이므로 구현 객체가 있어야 사용할 수 있다.
예를 들어 RemoteControl 인터페이스는 setMute() 라는 디폴트 메서드를 가지고 있지만, 이 메서드를 다음과 같이 호출 할 수는 없다.
RemoteControl.setMute(true);
setMute 메서드를 호출하려면 RemoteControl의 구현 객체를 인터페이스 변수에 대입 후 호출 할 수 있다.
디폴트 메서드는 인터페이스의 모든 구현 객체가 가지고 있는 기본 메서드라고 생각하면 되며 내용이 맞지 않아 수정이 필요하면 오버라이딩을 통해 자신에게 맞는 메서드로 수정 후 호출하면 된다.
왜 사용할까?
사실 인터페이스는 기능에 대한 구현보다는, 기능에 대한 '선언'에 초점을 맞추어서 사용 하는데, 디폴트 메소드는 왜 등장했을까?
...(중략)... 바로 "하위 호환성"때문이다. 예를 들어 설명하자면, 여러분들이 만약 오픈 소스코드를 만들었다고 가정하자. 그 오픈소스가 엄청 유명해져서 전 세계 사람들이 다 사용하고 있는데, 인터페이스에 새로운 메소드를 만들어야 하는 상황이 발생했다. 자칫 잘못하면 내가 만든 오픈소스를 사용한 사람들은 전부 오류가 발생하고 수정을 해야 하는 일이 발생할 수도 있다. 이럴 때 사용하는 것이 바로 default 메소드다. (자바의 신 2권)
기존에 존재하던 인터페이스를 이용하여서 구현된 클래스를 만들고 사용하고 있는데,
인터페이스를 보완하는 과정에서 추가적으로 구현해야 할, 혹은 필수적으로 존재해야 할 메소드가 있다면,
이미 이 인터페이스를 구현한 클래스와의 호환성이 떨어지게 된다. 이러한 경우 default 메소드를 추가하게 된다면
하위 호환성은 유지되고 인터페이스의 보완을 진행 할 수 있다.
정적 메서드는 인터페이스에서 바로 호출이 가능하다.
인스턴스 생성과 상관없이 인터페이스 타입으로 호출하는 메소드이다.
static 예약어를 사용하고, 접근제어자는 항상 public이며 생략 할 수 있다.
static method는 일반적으로 우리가 정의하는 메소드와는 다르다.
1. body가 있어야 한다.
2. implements 한 곳에서 override가 불가능하다.
인터페이스의 private 메소드, 자바 9
java8 에서는 default method와 static method가 추가 되었고,
java9 에서는 private method와 private static method가 추가 되었다.
java8의 default method와 static method는 여전히 불편하게 만든다.
단지 특정 기능을 처리하는 내부 method일 뿐인데도, 외부에 공개되는public method로 만들어야하기 때문이다.
interface를 구현하는 다른 interface 혹은 class가 해당 method에 엑세스 하거나 상속할 수 있는 것을 원하지 않아도,
그렇게 될 수 있는 것이다.
java9 에서는 위와 같은 사항으로 인해 private method와 private static method라는 새로운 기능을 제공해준다.
→코드의 중복을 피하고 interface에 대한 캡슐화를 유지 할 수 있게 되었다.
private [static] method 선언 방식
-
private 접근 지시자를 반드시 사용해야 함
-
private 과 abstract 키워드를 동시에 사용할 수 없다
-
동시에 사용 시 컴파일 에러
- private methods
- private method는 해당 클래스에서만 사용 가능하다, 결국 하위 클래스가 상속할 수 없고, 이 메소드를 재정의(오버라이딩) 할 수 없다는 의미이기 때문에 추상 메소드가 될 수없다
- 즉, 구현이 되어 있어야만 한다
- abstract methods
- 추상 메소드는 구현부가 없는 메소드라는 의미다
- 즉, 하위 클래스가 상속해서 이 추상 메소드를 구현해야한다
- private methods
-
private [static] 리턴타입 메소드명(매개변수, ...) { ... }
- private method는 interface 내에서만 사용할 수 있다
- private static method는 static & non-static 인터페이스 메소드 안에서 사용할 수 있다
- private non-static method는 private static method 내에서 사용할 수 없다
-
인터페이스 상속
인터페이스도 다른 인터페이스를 상속할 수 있다.
인터페이스는 클래스와는 달리 다중 상속을 허용한다.
다음과 같이 extends 키워드 뒤에 상속할 인터페이스들을 나열할 수 있다.
public interface 하위인터페이스 extends 상위인터페이스1, 하위인터페이스2{...}
하위 인터페이스를 구현하는 클래스는 하위 인터페이스의 메서드 뿐만 아니라 상위 인터페이스의 모든 추상 메서들에 대한 실체 메서드를 가지고 있어야 한다.
그렇게 때문에 구현 클래스로부터 객체를 생성하고 나서 다음과 같이 하위 및 상위 인터페이스 타입으로 변환이 가능하다.
하위인터페이스 변수 = new 구현클래스();
상위인터페이스1 변수 = new 구현클래스();
상위인터페이스2 변수 = new 구현클래스();
- 하위 인터페이스로 타입 변환이 되면
- 상, 하위 인터페이스에 선언된 모든 메소드를 사용할 수 있다
- 상위 인터페이스로 타입 변환되면
- 상위 인터페이스에 선언된 메소드만 사용 가능하다
- 하위 인터페이스에 선언된 메소드는 사용 불가하다
출처:
https://dev-coco.tistory.com/13 [슬기로운 개발생활😃]
github.com/yeGenieee/java-live-study/blob/main/%5B8%5DJava%20Live%20Study.md
www.notion.so/4b0cf3f6ff7549adb2951e27519fc0e6
도서 - 이것이 자바다
'스터디 > 백기선 라이브 스터디(자바)' 카테고리의 다른 글
10주차 과제: 멀티쓰레드 프로그래밍 (0) | 2021.02.25 |
---|---|
9주차 과제: 예외 처리 (0) | 2021.02.23 |
7주차 과제: 패키지 (0) | 2021.02.19 |
6주차 과제: 상속 (0) | 2021.02.17 |
5주차 과제: 클래스 (0) | 2021.02.08 |