모든 기능을 추상화로 정의만 하고 구현은 하지 않은것을 의미한다.
객체지향 solid 개발을 구현하게 도와줍니다.
인터페이스(interface)란?
자식 클래스가 여러 부모 클래스를 상속받을 수 있다면, 다양한 동작을 수행할 수 있다는 장점을 가지게 될 것입니다.
하지만 클래스를 이용하여 다중 상속을 할 경우 메소드 출처의 모호성 등 여러 가지 문제가 발생할 수 있어 자바에서는 클래스를 통한 다중 상속은 지원하지 않습니다.
하지만 다중 상속의 이점을 버릴 수는 없기에 자바에서는 인터페이스라는 것을 통해 다중 상속을 지원하고 있습니다.
인터페이스(interface)란 다른 클래스를 작성할 때 기본이 되는 틀을 제공하면서, 다른 클래스 사이의 중간 매개 역할까지 담당하는 일종의 추상 클래스를 의미합니다.
인터페이스는 객체를 어떻게 구성해야 하는지 정리해준 설계도 입니다.
구현 객체를 직접 몰라도 인터페이스 메서드만 알면 객체 호출이 가능합니다.
자바에서 추상 클래스는 추상 메소드뿐만 아니라 생성자, 필드, 일반 메소드도 포함할 수 있습니다.
하지만 인터페이스(interface)는 JAVA8 이전에는 오직 추상 메서드와 상수만을 포함 했으나 JAVA8 이후 부터 디폴트메서드, 정적메서드도 포함하게 되었습니다.
1. 인터페이스(Interface)의 선언
접근제어자 interface 인터페이스이름 {
//상수 (변하지 않는값)
[public static final] 필드타입 상수이름 = 값;
...
//추상 메소드 (오버라이팅 후 재구현 필수 : 선언부만 있고 구현부가 없는 메소드)
[public abstract] 리턴타입 메서드이름(매개변수목록);
...
//디폴트 메소드 (오버라이팅 후 재구현 선택 : 구현부 제공)
[public] default 리턴타입 메서드이름(매개변수목록);
...
//정적 메소드 (오버라이딩 불가, 그대로 사용)
[public] static 리턴타입 메서드이름(매개변수목록);
...
}
- "인터페이스이름"은 Upper CamelCase로 작성되어야 합니다.
- interface의 접근 지정자는 public만 가능합니다. 이유는 interface는 class 설계도 이기 때문에 애초에 존재 목적이 공개이기 때문입니다.
2. 인터페이스(Interface)의 특징
- 다중상속 가능
- 상수, 추상메서드, 디폴트메서드, 정적메서드 만 사용가능
- interface는 객체로 생성할 수 없기 때문에, 생성자 사용불가
3. 인터페이스(Interface) 구성요소
인터페이스 구성요소는 세 가지가 있습니다.
상수필드, 추상 메서드, 디폴트 메서드, 정적 메서드
3-1. 상수 필드(Constant Field)
public interface User {
// 상수 필드(Constant Field)
[public static final] 필드타입 상수명 = 값;
// 예시
String FIRST_NAME = "Ryan"; // 또는
public static final String FIRST_NAME = "Ryan"; // 는 같다.
}
- 인터페이스는 객체가 될 수 없기 때문에, 런타임에 필드 데이터를 저장할 수 없습니다. 그래서 인스턴스 필드/ 정적 필드는 선언이 불가능합니다.
- 상수 필드는 Compile Time에 선언되고 Run Time에 변경되지 않으므로 인터페이스에 선언이 가능합니다.
- [public static final]는 명시적으로 사용하지 않아도, Compile Time에 자동으로 선언되어 상수로 만듭니다.
- 네이밍은 모두 대문자로 구성되고 구분자는 "_"(언더바)로 표현됩니다. (Java Convention)
3-2. 추상 메서드(Abstract Method)
public interface User {
// 추상 메서드(Abstract Method)
[public abstract] 리턴타입 메서드이름(매개변수, ...);
// 예시
String sendMoney(Money money); // 와
public abstract String sendMoney(Money money); // 는 같다.
}
- 인터페이스 변수로 호출된 메서드는 최종적으로 구현 객체에서 실행됩니다. 그래서 실체는 인터페이스에 없고, 구현 클래스에 있습니다.
- 추상 메서드는 리턴 타입 / 메서드 이름 / 매개변수 가 기술되는 클래스 설계 메서드입니다.
- [public abstract]은 명시적으로 선언하지 않아도, Compile Time에 자동으로 선언됩니다.
3-3. 디폴트 메서드(Default Method)
public interface User {
// 디폴트 메서드(Default Method)
[public] default 리턴타입 메서드이름(매개변수, ...) { ... }
// 예시
public default void setStatus(Status status) {
if(status == Status.ACTIVE) {
System.out.println("사용자가 활성화 되었습니다");
return;
}
System.out.println("사용자가 비활성화 되었습니다");
}
}
- Java 8에서 추가된 인터페이스의 멤버입니다.
- 클래스의 인스턴스 메서드와 동일합니다. 즉, 인스턴스 메서드입니다. 다만 인터페이스에서 선언할 때, 리턴 타입 앞에 default 키워드가 붙습니다.
- [public]은 명시적으로 사용하지 않아도, Compile Time에 자동 선언됩니다.
- 디폴트 메서드는 나중에 인터페이스를 구현한 구현 클래스에 인스턴스 메서드로 추가됩니다.
- 재정의(Override)를 통해서 구현 클래스에서 재정의된 인스턴스 메서드로 사용할 수 있습니다.
3-4. 정적 메서드(Static Method)
public interface User {
// 정적 메서드(Static Method)
[public] static 리턴타입 메서드이름(매개변수, ...) { ... }
// 예시
public static void printFirstName() {
System.out.println("나의 이름은 " + firstName + "입니다.");
}
}
- Java 8에서 추가된 인터페이스의 멤버입니다.
- 선언 형식은 클래스 정적 메서드와 완전 동일합니다.
- [public]은 명시적으로 사용하지 않아도, Compile Time에 자동으로 선언됩니다.
- 인터페이스의 정적 메서드도 클래스의 정적 메서드와 똑같은 방식으로 사용이 가능합니다
4. 인터페이스(Interface)의 구현
class 클래스이름 implements 인터페이스이름 { ... }
인터페이스를 구현한 객체를 구현 객체(구현체)라고 합니다. 구현 객체를 생성하는 클래스를 구현 클래스라고 합니다.
인터페이스 구현 방식은 세 가지 방식이 있습니다.
단일 인터페이스 구현 클래스, 다중 인터페이스 구현 클래스, 익명 구현 객체
4-1. 단일 인터페이스 구현 클래스(Single Interface Implement Class)
public class 구현클래스이름 implements 인터페이스이름 {
// 인터페이스의 추상 메서드를 구현한 실체 메서드를 선언하는 부분
}
// 인터페이스 예시
public interface User {
//상수 필드
public static final String FIRST_NAME = "Ryan";
//추상 메서드
public abstract String sendMoney(Money money);
//디폴트 메서드
public default void setStatus(Status status) {
if(status == Status.ACTIVE) {
System.out.println("사용자가 활성화 되었습니다");
return;
}
System.out.println("사용자가 비활성화 되었습니다");
}
//정적 메서드
public static void printFirstName() {
System.out.println("나의 이름은 " + firstName + "입니다.");
}
}
//구현 클래스
public class Recipient implements User {
// 추상 메서드는 다음처럼 실체 메서드를 정의해야합니다.
public String sendMoney(Money money) {
thirdpartyApi.send(money.getType(), money.getAmount());
return Status.SUCCESS.name();
}
// 디폴트 메서드는 재정의가 가능합니다.
// 재정의 하지 않으면, 인터페이스에 정의된 내용 그대로 사용됩니다.
@Override
public default void setStatus(Status status) {
if(status == Status.ACTIVE) {
System.out.println("수취인이 활성화 되었습니다");
return;
}
System.out.println("수취인이 비활성화 되었습니다");
}
}
//만약 인터페이스를 구현한다고 하고,
//추상 메서드를 구현 클래스에서 실체 메서드를 모두 작성하지 않으면 해당 구현 클래스는 추상 클래스로 선언되어야 합니다.
public abstract class Recipient implements User {
}
//인터페이스 변수에 구현 객체 대입 예시
// User 인터페이스를 구현한 구현 클래스 Recipient
public class Recipient implements User { ... }
// User 인터페이스를 구현한 구현 클래스 Sender
public class Sender implements User { ... }
User user = new Recipient();
user = new Sender();
4-2. 다중 인터페이스 구현 클래스(Multiple Interface Implement Class)
public class 구현클래스이름 implements 인터페이스이름1, 인터페이스이름2 {
// 인터페이스의 추상 메서드를 구현한 실체 메서드를 선언하는 부분
}
- 인터페이스를 구현한 구현 클래스는 다중 인터페이스를 구현 가능합니다.
- 다중 인터페이스를 구현한 구현 클래스는 반드시 모든 인터페이스의 추상 메서드를 실체 메서드로 구현해야 합니다.
- 하나라도 추상 메서드가 구현되지 않으면, 구현 클래스는 추상 클래스로 선언되어야 합니다.
4-3. 익명 구현 객체(Anonymous Implement Object)
// 인터페이스에 선언된 추상 메서드의 실체 메서드 선언
User user = new User() {
public String sendMoney(Money money) {
thirdpartyApi.send(money.getType(), money.getAmount());
return Status.SUCCESS.name();
}
@Override
public default void setStatus(Status status) {
if(status == Status.ACTIVE) {
System.out.println("수취인이 활성화 되었습니다");
return;
}
System.out.println("수취인이 비활성화 되었습니다");
}
};
- 구현 클래스를 만들어서 사용하는 것이 일반적이고, 재사용이 가능하기에 편리합니다. 하지만 일회성으로 사용하는 구현 클래스는 클래스로 만들어서 선언해서 쓰는 것이 비효율적입니다.
- 비효율을 개선하기 위해서 만들어진 것이 익명 구현 객체입니다. 익명 구현 객체는 임시 작업 스레드를 만들기 위해 많이 활용됩니다.
- 특징 중에 하나는 new 키워드 뒤에 원래는 인터페이스 구현 클래스 이름이 와야 하는데, 익명 구현 객체의 경우에는 참조할 구현 클래스가 없기 때문에 User 인터페이스 이름을 그대로 사용합니다.
- 다만, 익명 구현 객체의 구현 부에는 인터페이스의 추상 메서드가 아닌 실체 메서드를 선언해야 합니다.
그렇다고 익명 구현 객체를 사용한다고 해서, 클래스가 생성되지 않는 것은 아닙니다.
5. 인터페이스(Interface)의 사용
인터페이스 변수는 참조 타입이기 때문에 구현 객체가 대입될 경우 구현 객체의 번지가 저장됩니다.
User sender = new Sender(); // User 인터페이스 참조변수 sender에 Sender 객체의 번지 저장
User recipient = new Recipient(); // User 인터페이스 참조변수 recipient에 Recipient 객체의 번지 저장
인터페이스 변수는 다음 5개의 부분에서 구현 객체의 참조 용도로 사용될 수 있습니다.
- 클래스의 필드
- 생성자의 파라미터
- 생성자의 로컬 변수
- 메서드의 파라미터
- 메서드의 로컬 변수
public class TestClass {
// 1.클래스의 필드
User user = new Recipient();
// 2.생성자의 파라미터
TestClass(User user) {
this.user = user;
// 3.생성자의 로컬변수
User recipient = new Recipient();
}
// 4.메서드의 파라미터
void methodA(User user) {
...
}
void methodB() {
// 5.메서드의 로컬변수
User user = new Recipient();
}
}
인터페이스의 구성요소 사용 방법
5-1. 상수 필드(Constant Field) 사용
5-2. 추상 메서드(Abstract Method) 사용
5-3. 디폴트 메서드(Default Method) 사용
5-4. 정적 메서드(Static Method) 사용
설명을 위해서 User 인터페이스를 예시 용도로 가져왔습니다.
public interface User {
public static final String FIRST_NAME = "Ryan";
public abstract String sendMoney(Money money);
public default void setStatus(Status status) {
if(status == Status.ACTIVE) {
System.out.println("사용자가 활성화 되었습니다.");
return;
}
System.out.println("사용자가 비활성화 되었습니다.");
}
public static void printFirstName() {
System.out.println("나의 이름은 " + firstName + "입니다.");
}
}
// User 인터페이스를 구현한 구현 클래스 Recipient
public class Recipient implements User { ... }
// User 인터페이스를 구현한 구현 클래스 Sender
public class Sender implements User { ... }
5-1. 상수 필드(Constant Field) 사용
User.FIRST_NAME
위 방식으로 상수는 클래스의 상수와 같은 방식으로 사용할 수 있습니다.
5-2. 추상 메서드(Abstract Method) 사용
public class Example {
public static void main(String[] args) {
User user = null;
user = new Recipient();
user.sendMoney(new Money(1l));// Recipient가 1원을 보냈다.
user = new Sender();
user.sendMoney(new Money(2l));// Sender가 2원을 보냈다.
}
}
user 인터페이스의 추상 메서드 sendMoney를 호출하면, 인터페이스 변수에 대입되었던 구현 객체의 주소를 판단해서 해당하는 실체 메서드를 호출합니다.
5-3. 디폴트 메서드(Default Method) 사용
public class Example {
public static void main(String[] args) {
User user = null;
user = new Recipient();
user.setStatus(Status.ACTIVE); // 사용자가 활성화 되었습니다.
user = new Sender();
user.setStatus(Status.INACTIVE); // 사용자가 비활성화 되었습니다.
}
}
구현 클래스에 실체 메서드를 작성하지 않아도 구현 객체에서 호출이 가능합니다.
물론, 구현 클래스에서 디폴트 메서드가 변경이 필요한 경우, 재정의(Override)가 가능합니다.
추상 메서드가 아니고 인스턴스 메서드이기 때문에 생성한 구현 객체가 있어야 사용할 수 있습니다.
디폴트 메서드를 재정의한 예시를 보겠습니다.
public class Recipient implements User {
@Override
public default void setStatus(Status status) {
if(status == Status.ACTIVE) {
System.out.println("수취인이 활성화 되었습니다.");
return;
}
System.out.println("수취인이 비활성화 되었습니다.");
}
}
public class Sender implements User {
@Override
public default void setStatus(Status status) {
if(status == Status.ACTIVE) {
System.out.println("송신자가 활성화 되었습니다.");
return;
}
System.out.println("송신자가 비활성화 되었습니다.");
}
}
public class Example {
public static void main(String[] args) {
User user = null;
user = new Recipient();
user.setStatus(Status.ACTIVE); // 수취인이 활성화 되었습니다.
user = new Sender();
user.setStatus(Status.INACTIVE); // 송신자가 비활성화 되었습니다.
}
}
구현 클래스에서 재정의된 디폴트 메서드가 사용되어 호출됩니다.
5-4. 정적 메서드(Static Method) 사용
public class Example {
public static void main(String[] args) {
User.printFirstName(); // 나의 이름은 Ryan입니다.
}
}
인터페이스의 정적 메서드도 클래스의 정적 메서드와 같은 방식으로 사용합니다.
정적 메서드이기 때문에 재정의(Override)는 불가능합니다.
연관된 글:
[CS] 좋은 객체 지향 설계의 5대 원칙: SOLID
[Java] 접근 제어자 (Access Modifier)와 캡슐화(encapsulation)
참고 :
자바 인터페이스(Java Interface)는 무엇인가?
'개발 > Java' 카테고리의 다른 글
[JAVA] DTO, VO, DAO, Entity 의 차이 (0) | 2023.05.25 |
---|---|
[JAVA] Lombok 어노테이션 (0) | 2023.04.29 |
[Java] 멀티 스레드(multi thread) (0) | 2023.03.29 |
[Java] 메소드 참조(method reference) (0) | 2023.03.20 |
[JAVA] Lambda (0) | 2023.03.20 |