* 이 글은 제가 공부하기 위해 최대한 사실에 입각해 내용을 적으려고 하지만 일부 내용들이 정확하지 않을 수 있습니다.
혹시나 잘못된 부분이 있으면 너그럽게 이해해주시고 피드백 부탁드려요!
9장 인터페이스
9.1 인터페이스의 개념
프로그램과 프로그램 사이에 표면을 연결해주는 인터페이스라는 것을 두면 프로그램 A는 이 인터페이스를 실행하고 인터페이스에서 프로그램 B를 실행하게 된다. 실행이 끝나면 프로그램 B는 인터페이스로 실행결과를 리턴하고, 인터페이스는 프로그램 A로 다시 리턴하게 된다. 이것이 인터페이스의 역할이다.
대표적인 인터페이스가 활용되는 예가 있다. 여러분들 웹사이트를 돌아다니다 보면 네이버지도나 구글지도가 들어가 있는 웹사이트를 봤을 것이다. 이 웹사이트에서 직접 네이버나 구글 지도 프로그램을 실행하지 않는다. 정확히 얘기하면 실행할 수 없다. 웹사이트에서는 지도 API를 사용해서 필요한 기능을 웹사이트에서 사용하게 된다. 이 API가 Application Programming Interface의 약자이다. 또 여러분이 컴퓨터와 상호작용하기 위해 모니터에 출력되는 화면을 UI라고 한다. 이 UI를 통해 사용자는 버튼을 클릭하고, 컴퓨터는 결과를 화면에 출력한다. 이 UI가 User Interface이다.
프로그램이 실행 중 객체 A를 실행하고자 할 때 직접 실행(호출)하는 것이 아니라 인터페이스를 실행(호출)하게 된다. 그러면 이제 도대체 왜 실행 코드에서 객체 A를 직접 실행하지 않고 힘들게 인터페이스를 하나 더 만들어서 불편하게 만들었을까?
다시 객체지향 프로그래밍 관점으로 생각해보면 객체 A, 객체 B, 객체 C는 부품이다. 프로그램이 수정되거나, 새로운 기능이 추가되어도, 실행코드를 직접 수정하지 않고, 부품만 추가하거나 교체하면 되는 것이다.
9.2 인터페이스의 선언
인터페이스를 정의할 때는 class 키워드 대신 interface 키워드를 사용해서 선언한다. 인터페이스는 상수와 메서드들로 구성되는데, 인터페이스 파일을 생성할 때 이클립스에서 File > New > Interface로 선택하면 된다. 인터페이스의 메서드는 이전 자바 7버전까지는 추상 메서드만 선언이 가능했는데, 자바 8버전 이후부터는 default 메서드와 static 메서드도 선언이 가능하다. 그리고 인터페이스는 객체로 생성되어 질 수 없다.
인터페이스의 구조
[public] interface 인터페이스명 { [public static final] 자료형 상수명 = 값; [public abstract] 리턴타입 추상메서드명(); [public] default 리턴타입 메서드명(매개변수...) {...} [public] static 리턴타입 메서드명(매개변수...) {...} } |
상수
인터페이스의 필드는 상수만 선언할 수 있다. 상수는 public static final 키워드가 적용되는데 생략하면 컴파일 시 자동으로 포함해준다. 그리고 선언시 반드시 초기값을 대입해줘야 한다.
추상메서드
인터페이스의 메서드는 기본적으로 추상메서드로 선언되는데, 모든 추상 메서드는 public abstract 키워드가 붙는다. 역시 생략해도 컴파일 과정에서 자동으로 넣어준다.
default 메서드
default 메서드는 자바 8번전에서 추가되었으며, 구현부(실행 블록)이 있는 메서드이다. default 키워드를 반드시 붙여야 하며, 접근제한자는 생력하는 경우 public으로 컴파일 과정에서 추가된다.
static 메서드
static 메서드도 자바 8버전에서 추가되었으며, default 메서드와 마찬가지로 구현부를 가지고 있는 메서드이다. static 메서든는 객체없이 인터페이스만으로 호출이 가능한 메서드이다.
package chapter09;
public interface InterfaceEx {
// 상수
public int MIN_PRICE = 0;
public int MAX_PRICE = 100000;
// 추상 메서드
public double meanPrice();
public double totalPrice();
// default 메서드 // 가급적 안쓰는 게 좋음
default double getSalePrice(double price) {
return price - (price * 0.05);
}
// static 메서드
static void printPrice(double price) {
System.out.println(price);
}
}
// 디폴트 동일 패키지 안에서만 사용 가능
// 프로텍트 : 상속까지만 사용가능
인터페이스의 특징
1. 클래스 간 -> 상속, 인터페이스와 클래스 간 -> 구현
2. 객체생성 불가, 구현 클래스로 객체 생성
3. 다중 구현 가능(하나의 클래스에서 여러 개의 인터페이스를 구현 가능)
4. 인터페이스 간의 상속 가능(인터페이스 간은 다중 상속도 가능)
5. 인터페이스의 모든 멤버의 접근 제한자는 public
6. 인터페이스의 모든 멤버 변수는 final이 붙은 상수
9.3 인터페이스 구현
인터페이스의 추상 메서드들을 일반 메서드에서 구현부를 정의할 수 있는데 이를 구현(implements)한다라고 표현한다. 구현하는 클래스를 구현 클래스라고 하고, 인터페이스와 구현 클랫와의 관계도 상속관계 상위, 하위 관계가 성립된다. 또한 인터페이스에서 정의한 모든 추상 메서드들은 반드시 구현 클래스에서 구현해줘야 한다.
구현 클래스에서는 implements 키워드를 사용하고 인터페이스 이름을 명시한다.
public class 구현클래스명 implements 인터페이스명 { // 인터페이스의 모든 |
package chapter09;
public interface Printer {
int INK = 100;
void print();
}
package chapter09;
public interface Scanner {
void scan();
}
package chapter09;
public interface Fax {
String FAX_NUMBER = "02-1234-5678";
void send(String tel);
void receive(String tel);
}
package chapter09;
public class Complexer implements Printer, Scanner, Fax{
@Override
public void send(String tel) {
// TODO Auto-generated method stub
System.out.println(FAX_NUMBER + "에서 " + tel + "로 FAX 전송");
}
@Override
public void receive(String tel) {
// TODO Auto-generated method stub
System.out.println(tel + "에서 " + FAX_NUMBER + "로 FAX 수신");
}
@Override
public void scan() {
// TODO Auto-generated method stub
System.out.println("스캔 실행");
}
@Override
public void print() {
// TODO Auto-generated method stub
System.out.println("출력 실행");
}
}
package chapter09;
public class ComplexerMain {
public static void main(String[] args) {
Complexer com = new Complexer();
System.out.println(Complexer.INK);
System.out.println(Complexer.FAX_NUMBER);
com.print();
com.scan();
com.send("02-8765-4321");
com.receive("02-8765-4321");
}
}
익명 구현 개체
보통 위 예제처럼 구현 클래스를 따로 만들어서 사용하는 경우가 많지만, 한번만 사용하는 경우 굳이 파일을 새로 생성하는 것이 더 불편할 수 있다. 그래서 실행 클래스에서 이름이 없는 익명 구현 객체로 객체를 생성할 수 있는 방법이 있다. 보통 안드로이드와 같은 GUI 프로그래밍에서 이벤트 처리 시, 또는 스레드를 사용하는 프로그래밍을 할 때 자주 사용한다. 익명 구현 객체로 객체를 생성할 때는, 이름이 없기 때문에 인터페이스명으로 객체를 생성해야한다.
인터페이스 객체명 = new 인터페이스() { // 인터페이스의 모든 추상 메서드 구현 } |
익명 구현 객체를 생성하는 코드에서는 맨 마지막에 항상 세미클론을 붙여줘야 한다. 객체를 생성하는 하나의 실행문이기 때문이다.
9.4 인터페이스의 다형성
상속에서와 마찬가지로 상위 클래스를 타입으로 지정해서 여러 하위 클래스를 객체로 생성해 같은 메서드를 실행해도 결과가 다르게 나오도록 구현하는 개념은 동일하다. 인터페이스 타입으로 지정한 객체를 구현 클래스로 객체를 생성하면 아주 쉽고, 편하게 구현 객체를 교체할 수 있다. 상속보다 인터페이스를 이용해서 다형성을 적용하는 경우가 많은 이유는 선임 개발자나 클래스 설계를 담당하는 개발자가 클래스를 먼저 인터페이스로 구현해 놓으면, 이 인터페이스의 메서드를 반드시 구현하도록 강제할 수 있기 때문에, 클래스 설계용도로도 많이 사용한다.
매개 변수의 다형성
매개 변수 역시 상속과 마찬가지로 다형성 개념을 적용해 여러 구현 클래스들을 인터페이스 자료형으로 선언해 매개 변수 값을 다양화 할 수 있다.
강제 형변환
구현 클래스의 객체가 상위 클래스인 인터페이스로 자동 형변환이 일어나면, 구현 클래스에서 추가한 메서든느 사용할 수가 없게 된다. 이럴 땐 다시 구현 클래스 타입으로 강제 형변환을 해주면 추가한 메서드를 사용할 수 있다.
instanceof
상속관계나 구현관계에 있는 클래스의 객체들은 자동 형변환이나 강제 형변환을 이용해 자유롭게 자료형을 변경할 수 있는데 전제 조건은 상속에서는 클래스 간에, 구현에서는 인터페이스와 클래스 간에 상위 또는 하위 관계여야 한다.
객체 instanceof 클래스(인터페이스) |
instanceof는 앞에 객체가 뒤쪽 클래스(인터페이스)의 객체인가? 라는 질문의 대답이 결과로 나온다. 예(true), 아니오(false) 둘 중의 하나이다. 그래서 instanceof는 형변환을 할 수 있는지 없는지를 먼저 확인할 때 사용한다. 상속과 구현 모두 같은 개념이기 때문에 모두 사용할 수 있다.
9.5 default 메서드와 static 메서드(생략)
자바 7버전까지는 상수와 추상 메서드로만 인터페이스를 정의할 수 있었는데, 자바 8버전 이후 부터는 default 메서드와 static 메서드도 같이 정의할 수 있도록 개선되었다.
9.6 어노테이션(생략)
어노테이션(Annotation)은 프로그램에게 추가적인 정보를 제공해주는 메타데이터(metadata)이다. 컴파일러에게 코드를 작성할 때 문법 에러를 체크하거나 정보를 제공하거나 빌드, 배치 시 코드를 자동으로 생성해주는 정보를 제공한다. 아래 표는 자바에서 제공하는 표준 어노테이션이다.
어노테이션 명 | 특징 |
@Override | 오버라이딩 검사, 오버라이딩 되지 않으면 에러 |
@Deprecated | Deprecated된 메서드 사용하지 않도록 검사, 사용시 컴파일 에러 |
@SuppressWarnings | 경고 메세지 표시 안되도록 설정 |
@SafeVarargs | 제너릭 타입의 가변 인자 사용 시 경고 무시(java 7버전 이상) |
@FunctionalInterface | 함수형 인터페이스로 추상 메서드가 한 개만 가능하도록 설정(java 7 버전 이상) |
@Native | Native 메서드에서 참조되는 상수 |
앞에서 배웠던 메서드 재정의(오버라이딩)에서 이클립스를 통해 자동 생성시키면 @Override 어노테이션이 자동으로 생성된 것을 보았는데, 이 어노테이션이 붙어 있으면 정확히 오버라이딩하지 않으면 컴파일러가 에러를 발생하게 된다. 이렇게 사용하지 않아도 코딩 시 제약 사항은 없지만, 좀 더 편하게 코딩할 수 있다. 나중에 서블릿이나 스프링 프레임워크를 배우게 되면 어노테이션을 사용해서 개발하는 경우가 많으니, 개념적으로 익혀두길 바란다.
메타 어노테이션이란 사용자 어노테이션을 만들 수 있는 어노테이션을 말한다. 메타 어노테이션의 종류는 아래와 같다.
메타 어노테이션 | 특징 |
@Retention | 어노테이션의 범위 설정, 어느 범위까지 영향을 미치는 설정 |
@Documented | 문서 어노테이션의 정보가 표현되도록 설정 |
@Target | 어노테이션이 적용할 위치 설정 |
@Inherited | 자식클래스가 부모클래스의 어노테이션을 상속 |
@Repeatable | 반복적으로 어노테이션을 선언 가능 |
어노테이션을 적용할 수 있는 대상은 java.lang.annotation.ElementType 열거 상수로 정의되어 있다.
ElementType 열거 상수 | 적용 대상 |
TYPE | 클래스, 인터페이스, enum 등의 타입 선언 시 |
ANNOTATION_TYPE | 어노테이션 타입 선언 시 |
FIELD | 멤버 변수 선언 시 |
CONSTRUCTOR | 생성자 선언 시 |
METHOD | 메서드 선언 시 |
LOCAL_VARIABLE | 지역변수 선언 시 |
PACKAGE | 패키지 선언 시 |
PARAMETER | 매개변수 선언 시 |
어노테이션을 정의하는 방법은 인터페이스와 비슷하다. 아래와 같이 @interface를 사용해서 정의한다.
public @interface 어노테이션명 { 자료형 요소명() [default 기본값]; } |
'Full Stack > JAVA' 카테고리의 다른 글
[풀스택과정] JAVA 10장 내부클래스(중첩클래스) (1) | 2023.01.24 |
---|---|
[풀스택과정] JAVA 9장 연습문제 (1) | 2023.01.22 |
[풀스택과정] JAVA 8장 연습문제 (1) | 2023.01.20 |
[풀스택과정] JAVA 8장 상속 (1) | 2023.01.19 |
[풀스택과정] JAVA 7장 연습문제 (1) | 2023.01.18 |