질문 내용 자체를 오해했어서 다시 설명해주려고 하는데
저도 좀 헷갈려서 정리하는데 시간이 걸렸어요
정리해보자면 질문이 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]에 대입이 가능합니다. 즉 문법적으로는 호환이 됩니다.
그러므로 컴파일 에러는 나지 않고, 런타임 에러가 발생합니다.
아래는 실제 코드 예시입니다.
반면에 Stream.toList는 외부로부터 타입 힌트를 받지 않습니다.
그러므로 호환이고 나발이고 따질 필요 없이 생성 시점에 정해진 ArrayList라는 타입으로 List를 생성합니다.
List[ArrayList]는 List[List[Integer]]가 될 수 없으므로 당연히 컴파일 에러가 발생합니다
2. List[List]랑 List[? extends List]랑 똑같아 보이는데 무슨 차이냐?
중첩 리스트는 복잡하니까, 이해를 돕기 위해 List가 아니라 다른걸 준비했습니다.
귀찮아서 스샷에 있는 설명문으로 텍스트를 대체합니다.
와 뭐야 저 날 6시까지 구글링하고 제네릭공부하고 메소드뜯어보고 했는데 글올려줬구나... 암튼 제네릭 쪽으로 방향 잡아줘서 잘 해결했고 이거 보고 한번더 정리됐음 고마워 복많이받아