"원소 시퀸스를 반환하는 메서드는 수없이 많다. 자바 7까지는 Collections, Set, List 같은 컬렉션 인터페이스, 혹은 Iterable이나 배열을 썼다. for-each 문에서만 쓰이거나 반환된 원소 시퀸스가 일부 Collection 메서드를 구현할 수 없을 때는 Iterable 인터페이스를 썼다. 반환 원소들이 기본타입이거나 성능에 민감한 상황이라면 배열을 썼다. 그런데 자바 8이 스트림이라는 개념을 들고 오면서 이 선택이 아주 복잡한 일이 되어버렸다."
-> 개발자는 원소 시퀀스를 반환할 때 어떤 메서드를 선택할지 고민하게 되었다.
- 스트림은 반복을 지원하지 않는다. 따라서 스트림과 반복을 알맞게 조합해야 좋은 코드가 나온다.
- 사실 Stream 인터페이스는 Iterable 인터페이스가 정의한 추상 메서드를 전부 포함할 뿐만 아니라, Iterable 인터페이스가 정의한 방식대로 동작한다. 그럼에도 for-each로 스트림을 반복할 수 없는 까닭은 Stream이 Iterable을 확장하지 않아서다.
- 이 문제를 해결해줄 방법은 딱히 없다. 있다해도, 실제로 쓰기에는 난잡하고 직관성이 떨어진다.
for (ProcessHandle ph : (Iterable<ProcessHandle>)
ProcessHandle.allProcess()::iterator) {
// 프로세스를 처리한다.
}
-
어댑터 패턴을 사용하면 stream도 for-each문으로 반복할 수 있다.
-
어댑터 패턴 : 클래스의 인터페이스를 사용자가 기대하는 인터페이스 형태로 적응(변환) 시킨다. 서로 일치하지 않는 인터페이스를 갖는 클래스들을 함께 동작시킨다.
-
그렇다면 어떤 근거로 메서드를 선택하고 사용할 수 있을까?
- Collection 인터페이스는 Iterable의 하위 타입이고 stream 메서드도 제공하니 반복과 스트림을 동시에 지원한다. 따라서 Collection이나 그 하위 타입을 쓰는 게 일반적으로 최선이다. Arrays 역시 Arrays.asList와 Stream.of 메서드로 손쉽게 반복과 스트림을 지원할 수 있다.
- 반환하는 시퀸스의 크기가 메모리에 올려도 안전할 만큼 작다면 ArrayList, HashSet 같은 표준 컬렉션 구현체를 반환하는 게 최선일 수 있다. 하지만 단지 컬렉션을 반환한다는 이유로 덩치 큰 시퀸스를 메모리에 올려서는 안 된다.
- 반환할 시퀸스가 크지만 표현을 간결하게 할 수 있다면 전용 컬렉션을 구현하는 방안을 검토해보자.
-> 아이템47에서는 원소 시퀀스를 반복적으로 뽑아내는 다양한 방법을 소개한다. 정답이 아닌 "~한 경우도 고려할 수 있다"라는 가정이므로 사용예시별 효과를 살펴보고 상황에 맞게 적용하는 것이 필요하다. 이 중 단언할 수 있는 것은 반환타입으로 스트림보다 컬렉션이 낫다는 것이다.
원소 시퀀스를 반환하는 메서드를 작성할 때는, 이를 스트림으로 처리하기를 원하는 사용자와 반복으로 처리하길 원하는 사용자가 모두 있을 수 있음을 떠올리고, 양쪽을 만족시키려 노력하자.
컬렉션을 반환할 수 있다면 그렇게 하라. 반환 전부터 이미 원소들을 컬렉션에 담아 관리하고 있거나 컬렉션을 하나 더 만들어도 될 정도로 원소 개수가 적다면 ArrayList 같은 표준 컬렉션에 담아 반환하라.
컬렉션을 반환하는게 불가능하면 스트림과 Iterable 중 더 자연스러운 것을 반환하라. 때로는 단순히 구현하기 쉬운 쪽을 선택하기도 한다.