Interface 추상화
가장 필요한 요소를 제회하고 모두 다 감춰버리는 것
- 자바에서는 "인터페이스" 혹은 "추상 클래스" 를 통해 이뤄낼 수 있다.
- 인터페이스: 구현체는 감추고, 필요한 메서드만 노출 -> 메서드 가져다 호출만 하면 된다.
- 추상 클래스: 구현체는 감추고, 필요한 필드와 메서드만 노출 -> 메서드, 필드 가져다 호출만 하면 된다.
Generic
제네릭스는 다양한 타입의 객체들을 다루는 메서드나 컬렉션 클래스에 컴파일 시의 타입 체크(compile-time type check)를 해주는 기능이다.
객체의 타입을 컴파일 시에 체크하기 때문에 객체의 타입 안전성을 높이고 형변환의 번거로움이 줄어든다.
💡타입의 안전성?
타입의 안전성을 높인가는 것은 의도하지 않은 타입의 객체를 저장하는 것을 막고, 저장된 객체를 꺼내올 때 원래의 타입과 다른 타입으로 형 변환되어 발생할 수 있는 오류를 줄여준다는 뜻이다.
제네릭스를 적용한 ArrayList
ArrayList<Member> memberList = new ArrayList<Member>();
memberList.add(new Member());
Member m = memberList.get(0);
제네직스를 적용하지 않은 ArrayList
ArrayList memberList1 = new ArrayList();
memberList1.add(new Member());
Member member1 = (Member) memberList.get(0);
두 코드의 차이점을 살펴보면 ArrayList에 저장된 객체를 꺼낼 때 형변환할 필요가 없어서 편리하다. 이미 어떤 타입의 객체들이 저장되어 있는지 알고 있기 때문이다.
Interface Class
현업에서 사용하는 인터페이스 예시
public interface KafkaListener<T> {
KakfaListnerType getType();
void consume(T consumed);
}
Kafka?
Apache Kafka는 실시간으로 스트리밍 데이터를 수집하고 처리하는 데 최적화된 분산 데이터 스토어입니다.
스트리밍 데이터는 수천 개의 데이터 원본에서 연속적으로 생성되는 데이터로, 보통 데이터 레코드를 동시에 전송합니다.
Kafka는 대기열과 게시-구독이라는 두 가지 메시징 모델을 결합하여 소비자에게 각 모델의 주요 이점을 제공합니다. 대기열을 사용하면 데이터 처리를 여러 소비자 인스턴스에 분산할 수 있어 확장성이 뛰어납니다.
Kafka의 경우 위와 같이 인터페이스를 사용한다.
producer(메시지 보내는 사람) - 큐 - consumer(메시지 받는 사람) 으로 작동한다.
consumer의 경우 본인에게 온 메시지만 받아야하므로 제네릭스를 사용해서 `T`로 받은 타입을 명시해준다.
💡kafka는 nginx처럼 로드밸런싱 해준다고 보면 되나?
실시간 로드 밸런싱은 nginx를 사용해서 해준다고 보면 되고, kafka는 큐를 이용해서 네트워크나 리소스가 여유있을 때 처리해준다고 생각
public interface KafkaListener<T> {
KakfaListnerType getType();
/*(public abstract)*/ void consume(T consumed);
}
- 인터페이스는 아래와 같은 제약사항이 있음
- 모든 멤버 변수는 `public static final` 이어야하며, 이를 생략할 수 있다.
- 모든 메서드는 `public abstract`이어야하며, 이를 생략할 수 있다.
- 단, static메서드와 디폴트 메서드는 예외(Java 8이후부터)
- 다중 "상속(extends)" 가능 + 다중 "구현(implements)" 가능
- 다중 "상속"가능: Java에서 클래스는 다중 상속이 되지 않는다. 단 인터페이스는 다중 상속이 가능
아래 코드는 인터페이스에 다중 상속이 가능함을 보여준다.
interface MakingRamen extends MakingRamenWater, MakingRamenSoup {
public interface Vehicle {
public default void doSomething(int n) {
System.out.println("doSomething(Vehicle)");
}
}
public interface VehicleChild extends Vehicle {
}
다중 구현 가능 코드로 보기
public interface Vehicle {
public default void doSomething(int n) {
System.out.println("doSomething(Vehicle)");
}
}
public interface Movable {
public default void doSomething(int n) {
System.out.println("doSomething(Movable)");
}
}
public static class Car implements Vehicle, Movable {
@Override
public void doSomething(int n) {
Vehicle.super.doSomething(n);
Movable.super.doSomething(n);
System.out.println("doSomething(Car)");
}
}
public static void main(String args[]) {
Car car = new Car();
car.doSomething(10);
}
인터페이스에 디폴트 메서드와 정적 필드 / 메서드 정의 가능
Private 메서드는 Abstract가 아닌 구현체로 인터페이스 내 존재 가능
-> 인터페이스 디폴트 메서드가 등장함과 동시에 인터페이스 내 메서드 호출 가능
-> fail over 같은 역할을 해준다고 생각, 해당 메서드가 구현되지 않으면 실행될 fail over메서드
-> 추상화 클래스에서 abstract함수를 사용하면 무조건 구현해야하는 강제성이 있었음. 디폴트 메서드는 구현하지 않아도 된다.
인터페이스 내 정적 필드, 정적 메서드 정의 가능
정리
interface A extends A, B, D //가능
class A extends B,C,D,E //불가능
class A implements B,C,D,E //가능
Abstract Class
미완성 설계도에 비유할 수 있다.
인스턴스 생성 불가, 추상 메서드를 포함하고 있는 클래스
현업에서 사용하는 추상클래스 예시
@Slf4j
public abstract class KafkaAbstractListener<T> implements KafkaListener<T> {
@PostConstruct
public void instantiated() {
log.info("[Abstract Class] {} is created", this.getType().name());
}
@PreDestroy
abstract void destroy();//메서드는 구현하지 않고
}
- 형태와 구현을 혼합 정의 = 데이터: 필드Field) 멤버변수 정의 + 행위: 메서드 (Method) 정의
- 형태: 어떤 메소드를 가져야하는지 명시
- 구현: 구현 메소드를 정의
- 추상 메서드는 상속받은 자손 클래스는 오버라이딩을 통해 조상인 추상 클래스의 추상 메서드를 모두 구현해주어야한다.
인터페이스와 추상화 클래스에서 접근 제어자 설정
인터페이스는 무조건 public, 추상 클래스는 protected로 가능하다.
💡추상 클래스를 사용할 때 protected를 지향해야하는가?
그냥 적재적소에 사용하면 된다. 다 오픈하고 싶다면 public으로 해도 된다.
'ASAC 웹 풀스택 > Spring Boot' 카테고리의 다른 글
Java 기본 문법 및 JVM 구성(13) - Enum 활용 (0) | 2024.09.30 |
---|---|
Java 기본 문법 및 JVM 구성(12) - 다형성 (0) | 2024.09.29 |
Java 기본 문법 및 JVM 구성(10) - final, static (0) | 2024.09.29 |
Java 기본 문법 및 JVM 구성(9) - DTO, VO (0) | 2024.09.28 |
Java 기본 문법 및 JVM 구성(8) - 정적 팩토리 메서드 (1) | 2024.09.28 |