질문 내용 자체를 오해했어서 다시 설명해주려고 하는데
저도 좀 헷갈려서 정리하는데 시간이 걸렸어요


정리해보자면 질문이 2가지였는데, 차례대로 설명해줄게요

1. Stream.toList와 Stream.collect(Collectors.toList)의 결과가 왜 다르냐?

2. List[List]랑 List[? extends List]랑 똑같아 보이는데 무슨 차이냐?




1. Stream.toList와 Stream.collect(Collectors.toList)의 결과가 왜 다르냐?

먼저 Stream.collect(Collectors.toList)의 명세를 봐야해요

collect의 결과인 R은 외부로부터 타입이 추론됩니다.

즉, List[List[Integer]]가 R입니다. 그런데 이 R이 Collectors.toList에서는 List[T]와 매치됩니다.

Collectors.toList의 T는 Stream.collect로부터 전달받은 ? super T입니다. 

Stream.collect의 T는 외부로부터 추론된 것이 아닌, 생성 시점에 정해진 타입인 ArrayList입니다.

즉 ? super ArrayList입니다.

List[List[Integer]]와 List[? super ArrayList] 이 두개가 호환되는 지점을 찾아야하는데, ArrayList의 부모는 List입니다.

List는 구버전 호환을 위해 List[Integer]에 대입이 가능합니다. 즉 문법적으로는 호환이 됩니다.

그러므로 컴파일 에러는 나지 않고, 런타임 에러가 발생합니다.


a15714ab041eb360be3335622983746f11c3866e67e5daee46df98e12f6ce6d5460eccc7de83477b6dd8

a15714ab041eb360be3335622983746f11c3866e67e5daee46dc9de22f6ce6d58b80b8f399a2d61f96f515


아래는 실제 코드 예시입니다.


a15714ab041eb360be3335622983746f11c3866e67e5daee47d998e72f6ce6d5ac08131ecf43e61957659160


반면에 Stream.toList는 외부로부터 타입 힌트를 받지 않습니다.

그러므로 호환이고 나발이고 따질 필요 없이 생성 시점에 정해진 ArrayList라는 타입으로 List를 생성합니다.

List[ArrayList]는 List[List[Integer]]가 될 수 없으므로 당연히 컴파일 에러가 발생합니다


a15714ab041eb360be3335622983746f11c3866e67e5daee47df9de42f6ce6d55a8a76be38e94c9044f829

a15714ab041eb360be3335622983746f11c3866e67e5daee47dc98e52f6ce6d55704ad250552560a4eb648




2. List[List]랑 List[? extends List]랑 똑같아 보이는데 무슨 차이냐?


중첩 리스트는 복잡하니까, 이해를 돕기 위해 List가 아니라 다른걸 준비했습니다.

a15714ab041eb360be3335622983746f11c3866e67e5daee47dd98e12f6ce6d59f9093fee94a64bc986532


귀찮아서 스샷에 있는 설명문으로 텍스트를 대체합니다.

a15714ab041eb360be3335622983746f11c3866e67e5daee47d299e32f6ce6d53db9b02659de93481b048674