Lists.map(List.of("a", "bb"), String::length); // T = String, R = IntegerLists.map(List.of(1, 2), n -> "#" + n); // T = Integer, R = String
해본 것
나온 에러
List<String> r = Lists.map(List.of(1, 2), n -> n * 2);
inference variable R has incompatible bounds
Lists.reduce(List.of(1, 2), "", (acc, n) -> n);
cannot infer type-variable(s) T,R,E
incompatible bounds라는 표현이 그 증거다. 컴파일러는 R의 값을 모른 채, R에 걸린 제약들이 동시에 만족 가능한지만 본다.
두 축이 갈리는 지점 — 타입 파라미터의 수명
언제 정해지나
언제 끝나나
제네릭 함수
호출 지점마다
그 호출이 끝나면
제네릭 타입
타입을 적는 순간
그 값이 사는 내내 따라다닌다
Stack<String> stack = newStack<>(); // 여기서 E = String이 확정되고stack.push(42); // 그 값이 사는 내내 따라다닌다// error: incompatible types: int cannot be converted to String
제네릭 함수는 다음 줄에서 완전히 다른 타입으로 다시 쓸 수 있다.
제네릭 타입은 그럴 수 없다. 그게 제약이 아니라 그 값에 대한 약속을 붙들어두는 방식이다.
타입이 코드를 따라 흐른다
Stack이 "타입 하나를 받는 틀"이라면, Pair는 타입이 이동하는 것을 보여준다.
publicPair<B, A> swap()
값만 자리를 바꾸는 게 아니라 타입도 함께 바꾼다.
그리고 그 사실이 시그니처에 적혀 있어서, 호출한 쪽에서 컴파일러가 이어서 검증한다.
Pair<String, Integer> p = Pair.of("a", 1).swap();
// error: Pair<Integer,String> cannot be converted to Pair<String,Integer>