한정적 타입 토큰
<T extends Enum<T> & Operation>
자바 제네릭스 문법에서 사용되는 상한 경계(bound) 지정 구문이다.
이 구문은 제네릭 타입 T가 두 가지 조건을 모두 만족해야 함을 나타낸다:
- Enum<T>의 서브타입이어야 한다.
- Operation 인터페이스를 구현해야 한다.
이 구문을 사용하면 T는 반드시 열거형(enum)이면서 동시에 Operation 인터페이스를 구현한 타입이어야 한다.
이를 통해 열거형 타입에 대한 제네릭 클래스나 메서드를 정의하면서도, 해당 타입이 특정 인터페이스를 구현하도록 강제할 수 있다.
Operation 인터페이스 정의
public interface Operation {
double apply(double x, double y);
}
Operation을 구현하는 Enum 정의
public enum BasicOperation implements Operation {
PLUS("+") {
public double apply(double x, double y) { return x + y; }
},
MINUS("-") {
public double apply(double x, double y) { return x - y; }
};
private final String symbol;
BasicOperation(String symbol) {
this.symbol = symbol;
}
@Override
public String toString() {
return symbol;
}
}
제네릭 메서드 정의 및 사용
public class Calculator {
public static <T extends Enum<T> & Operation> void printAllOperations(Class<T> opSet, double x, double y) {
for (Operation op : opSet.getEnumConstants()) {
System.out.printf("%f %s %f = %f%n", x, op, y, op.apply(x, y));
}
}
public static void main(String[] args) {
printAllOperations(BasicOperation.class, 3, 4);
}
}
한정적 와일드카드 타입
Collection<? extends Operation>
한정적 타입 토큰과의 차이점
- 타입 제한 및 유연성:
- Class<T> 타입의 파라미터는 특정 열거형 타입만 받아들일 수 있어서 제네릭 타입 매개변수 T는 Enum<T>의 서브타입이면서 Operation 인터페이스를 구현해야 한다.
- Collection<? extends Operation> 타입의 파라미터는 더 유연하여, Operation 인터페이스를 구현하는 객체들의 컬렉션을 받아들일 수 있다. 이는 열거형뿐만 아니라 다른 클래스의 인스턴스도 포함될 수 있다.
- 사용 방식:
- Class<T>를 사용하면 해당 클래스의 열거형 상수를 얻기 위해 getEnumConstants() 메서드를 호출할 수 있다.
- Collection<? extends Operation>를 사용하면 단순히 컬렉션에 포함된 객체들을 순회하면 된다.
import java.util.Collection;
import java.util.List;
public class Calculator {
public static void printAllOperations(Collection<? extends Operation> opSet, double x, double y) {
for (Operation op : opSet) {
System.out.printf("%f %s %f = %f%n", x, op, y, op.apply(x, y));
}
}
public static void main(String[] args) {
List<Operation> operations = List.of(
BasicOperation.PLUS,
BasicOperation.MINUS,
new AdvancedOperation("MULTIPLY", (a, b) -> a * b),
new AdvancedOperation("DIVIDE", (a, b) -> a / b)
);
printAllOperations(operations, 3, 4);
}
}
Collection<? extends Operation>를 사용하면 더 유연한 구조를 가지며, 다양한 Operation 구현체를 처리할 수 있음을 보여준다.
열거형 뿐만 아니라 일반 클래스도 함께 사용할 수 있어 코드 재사용성과 확장성이 높아진다.
Operation은 interface인데, extends ?
Java 제네릭에서 사용되는 extends 키워드는 제네릭 타입 파라미터에 대한 상한 경계를 지정하는 데 사용된다. 여기서 extends는 클래스뿐만 아니라 인터페이스에도 적용된다. 이는 제네릭 타입이 특정 클래스의 서브타입이거나 특정 인터페이스를 구현해야 한다는 의미이다. 따라서 Collection<? extends Operation>은 Operation 인터페이스를 구현하는 모든 타입을 포함할 수 있습니다.
제네릭 상한 경계에서 extends의 의미
제네릭 타입 파라미터에서 extends는 특정 클래스의 서브타입이거나 특정 인터페이스를 구현하는 타입으로 제한하는 역할을 한다. 예를 들어, T extends Number는 T가 Number의 서브타입이어야 한다는 의미이며, T extends Runnable은 T가 Runnable 인터페이스를 구현해야 한다는 의미이다.