언어는 하드웨어나 프로그래머 기대의 변화에 부응하는 방향으로 변화해야한다.
다음은 사과 목록을 무게순으로 정렬하는 고전적 코드다.
Collections.sort(inventory, new Comparator<Apple>() {
public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight);
}
});
이 코드는 자바 8을 이용하면 자연엉에 더 가깝게 간단한 방식으로 구현할 수 있다.
inventory.sort(comparing(Apple::getWeight));
멀티코어 CPU의 대중화와 같은 하드웨어의 발전은 자바 8에도 영향을 미쳤다. 지금까지의 자바 프로그램 대부분은 코어 중 하나만 사용했다. (즉, 나머지 코어는 유휴 idle 상태로 두거나, 운영체제, 바이러스검사 프로그램과 프로세스 파워를 나눠서 사용)
자바 8이 등장하기 전에는 나머지 코어를 활용하려면 스레드를 사용하는 것이 좋다고 조언했을 것이다. 하지만 스레드는 관리가 어렵고 많은 문제를 야기한다. 자바는 이러한 병렬 실행 환경을 쉽게 관리하고 에러가 덜 발생하는 방향으로 진화하려고 노력했다. 하지만 실무에서 이런 저수준 기능을 온전히 활용하기 어려웠다. 자바 5에서는 스레드 풀, 병렬실행 컬렉션 등 아주 강력한 도구를 도입했고, 자바 7에서는 포크 조인 프레임워크(병렬 실행에 도움을 줄 수 있는)를 제공했지만 여전히 개발자가 활용하기는 쉽지 않았다. 자바 8에서는 병렬 실행을 새롭고 단순한 방식으로 접근할 수 있는 방법을 제공한다.
자바 8은 멀티코어 프로세서의 쉬운 활용과 간결한 코드 이 두가지 요구사항을 기반으로 한다. chp1에서는 간단하게 훑어보는 식으로 구성되어 있다.
synchronized
같은 값 비싼 키워드를 사용하지 않아도 된다.스트림이란 ?
cat file1 file2 | tr "[A-Z]" "[a-z]" | sort | tail -3
일례로 유닉스의 cat으로 두 파일을 연결하여 스트림을 생성하며, tr은 스트림의 문자를 번역하고, sort는 스트림의 행을 정렬하며, tail -3은 스트림의 마지막 3개 행을 제공한다.
유닉스에서는 여러 명령을 병렬로 실행한다. 따라서 cat, tr이 완료되지 않은 시점에서 sort가 행을 처리하기 시작할 수 있다. 이를 기계적인 예로 자동차 생산 공장라인에 비유할 수 있다. 자동차 생산공장은 여러 자동차로 구성된 스트림을 처리하는데, 각 작업장에서는 자동차를 받아 수리한 뒤 다음 작업장에서 다른 작업을 처리할 수 있도록 넘겨준다. 조립라인은 자동차를 물리적 순서로 한 개씩 운반하지만 각각의 작업장에서는 동시에 작업을 처리한다.
//리스트에서 고가의 거래만 필터링을 한다음 통화로 결과를 그룹화하는 코드이다.
Map<Currency, List<Transaction>> transactionsByCurrencies = new HasMap<>();
for (Transaction transaction : transactions){
if (transaction.getPrice() > 1000){
Currency currency = transaction.getCurrenct();
List<Transaction> transactionsForCurrency = transactionsByCurrencies.get(currency);
if(transactionsForCurrenc == null){
transactionsForCurrency = new ArrayList<>();
transactionsByCurrencies.put(currency, transactionsForCurrency);
}
transactionsForCurrency.add(transaction);
}
}
위의 코드를 스트림을 활용하면 다음처럼 작성할 수 있다.
Map<Currency, List<Transaction>> transactionsByCurrencies = transactions.stream()
.filter((Transaction t) -> t.getPrice() > 1000)
.collect(groupingBy(Transaction::getCurrency));
우선은 스트림 API를 이용하면 컬렉션 API와 상당히 다른 방식으로 데이터를 처리할 수 있다는 사실을 기억하자.
컬렉션에서는 for-each loop으로 반복 과정을 직접 처리(외부 반복)했지만, 스트림 API를 이용하면 루프를 신경쓸 필요가 없다. 스트림 API에서는 라이브러리 내부에서 모든 데이터가 처리된다(내부반복).
컬렉션을 사용하면 거대한 리스트를 처리하는데 시간이 오래 걸리겠지만, 서로 다른 코어에 작업을 각각 할당하여 처리시간을 줄일 수 있다면, 병렬로 코어를 활용하여 단일 CPU에 비해 빠른 작업이 가능하다.
요즘은 외부에서 만들어진 컴포넌트를 이용해 시스템을 구축하는 경향이 있다. 이와 관련해 지금까지 자바에서는 특별한 구조가 아닌 평범한 자바 패키지 집합을 포함하는 JAR을 제공하는 것이 전부였다. 때문에 패키지의 인터페이스를 바꿔야 하는 과정에서는 인터페이스를 구현하는 모든 클래스의 구현을 바꿔야했다 -> 고통스럽다
자바 8에서는 인터페이스를 쉽게 바꿀 수 있도록 디폴트 메서드를 지원하다. 인터페이스에 새로운 메서드를 추가한다면 인터페이스를 구현하는 모든 클래스는 새로 추가된 메서드를 구현해야 한다. 이건 현실적으로 말이 안되는 상황이고, 기존의 구현을 고치지 않으면서 공개된 인터페이스를 변경할 수 있는 방법이 필요하다.
결정적으로 자바 8은 기존의 코드를 건드리지 않고, 원래의 인터페이스 설계를 자유롭게 확장할 수 있도록 구현하지 않아도 되는 메서드를 인터페이스에 추가할 수 있는 기능을 제공한다.
default void sort(Comparator<? super E> c){
Collections.sort(this, c);
}
예를 들어 자바 8에서는 sort메서드를 호출할 수 있다. 이는 자바 8의 List 인터페이스에 다음과 같은 디폴트 메서드 정의가 추가되었기 때문이다.
따라서 자바 8이전에는 List를 구현하는 모든 클래스가 sort를 구현해야 했지만 자바 8부터는 디폴트 sort를 구현하지 않아도 된다.
(1) 변수와 데이터에 할당 할 수 있어야 한다.
(2) 객체의 인자로 넘길 수 있어야 한다.
(3) 객체의 리턴값으로 리턴 할 수 있어야 한다.
만약 런타임 시점에 메서드를 전달할 수 있다면, 즉 메서드를 일급 객체로 만들면 프로그래밍에 유용하게 활용할 수 있다. 따라서 자바 8 설계자들은 이급 객체를 일급 객체로 바꿀수 있는 기능을 추가했다.
스칼라와 그루비 같은 언어에서 메서드를 일급값으로 사용하면 프로그래머가 활용할 수 있는 도구가 다양해지면서 프로그래밍이 수월해진다는 사실을 이미 실험을 통해 확인했다. 그래서 자바 8의 설계자들은 메서드를 값으로 취급할 수 있게, 그리하여 프로그래머들이 더 쉽게 프로그램을 구현할 수 있는 환경이 제공되도록 자바 8을 설계하였다. 더불어 자바 8에서 메서드를 값으로 취급할 수 있는 기능은 스트림 같은 다른 자바 8의 기능의 토대를 제공했다.
자바의 역사에 대해서 책을 통해 배울 수 있었고, 언제 어떤 요구로 자바가 업데이트 되어왔는지, 프로그래밍 언어에 대한 시대의 요구사항이 무엇인지에 대해서 간략하게나마 배울 수 있었다. 앞으로 세부 챕터들을 통해 더 자세히 학습하기로 한다. 나를 힘들게 했던 스트림을 정복해보자 라는 책을 구매하였는데 첫 장을 읽고나니 스트림 뿐아니라 다양한 지식들이 버무려져서 그때 그래서 이랬던거구나와 같은 생각을 들게끔 했다. 시작이 반이라고 다른 블로깅보다 힘들긴 하지만 꾸준히 블로깅을 이어나가야 겠다는 다짐도 해본다.