1.1 역사의 흐름은 무엇인가?
자바 역사를 통틀어 가장 큰 변화가 자바 8에서 일어났다.
-
간단한 방식으로 코드 구현 가능
//Java 7 Collections.sort(lowCaloricDishes, new Comparator<Dish>() { @Override public int compare(Dish d1, Dish d2) { return Integer.compare(d1.getCalories(), d2.getCalories()); } }); //Java 8 lowCaloricDishes.sort(Comparator.comparingInt(Dish::getCalories));
-
멀티코어 CPU 대중화와 같은 하드웨어적인 변화도 영향
-
JAVA 8 이전
-
코어 중 하나만 사용
-
멀티 코어를 위해 스레드를 사용->관리가 어렵다는 단점
-
-
JAVA 8 이후
-
스트림을 이용한 쉬운 병렬 실행 기법 제공
-
-
자바 8은 두 가지 요구 사항을 기반으로 한다.
-
간결한 코드
-
멀티코어 프로세서의 쉬운 활용
자바 8에서 크게 세가지의 새로운 기술을 제공한다.
-
스트림 API
-
메서드에 코드를 전달하는 기법
-
인터페이스의 디폴트 메서드
1.2 왜 아직도 자바는 변화하는가?
자바는 코드를 JVM 바이트 코드로 컴파일하는 특징, 객체지향적인 특징 때문에 주요 언어가 되었다.
하지만 빅데이터라는 도전에 직면하면서 멀티코어 컴퓨팅을 통해 빅데이터를 효과적으로 처리할 필요성이 커졌다.
즉, 병렬 프로세싱을 활용해야 하는 데 지금까지의 자바로는 충분히 대응할 수 없었다.
자바 8에서는 이러한 문제들을 해결할 수 있도록 도와준다.
다음은 자바 8 설계의 밑바탕을 이루는 세 가지 프로그래밍 개념이다.
1) 스트림 처리
-
파이프라인을 만드는데 필요한 메서드들 제공
-
유닉스 명령어와 비슷
cat file1 file2 | sort | tail -3
-
스트림 파이프라인을 이용해 입력 부분을 여러 CPU 코어에 할당
-
스레드 사용하지 않고 쉽게
병렬성
제공
-
2) 동작 파라미터화로 메서드에 코드 전달하기
//Java 7
Collections.sort(lowCaloricDishes, new Comparator<Dish>() {
@Override
public int compare(Dish d1, Dish d2) {
return Integer.compare(d1.getCalories(), d2.getCalories());
}
});
//Java 8
lowCaloricDishes.sort(Comparator.comparingInt(Dish::getCalories));
위의 코드와 같이 자바8은 메서드를 다른 메서드의 인수로 넘겨주는 기능을 제공한다.
이를 동작 파라미터화라고 부른다.
스트림 API는 연산의 동작을 파라미터 화할 수 있는 코드를 전달한다는 사상에 기초하기 때문에 중요하다.
3) 병렬성과 공유 가변 데이터
-
스트림 사용 시 공유된 가변 데이터에 접근하지 않아야 한다
-
JAVA 8 이전
-
스레드 synchronized을 이용한 Lock
-
Lock을 걸면 순차적으로 실행된다 -> 병렬이라는 목적 무력화
-
-
JAVA 8 이후
-
쉽게 병렬성 활용
-
1.3 자바 함수
-
메서드 vs 함수의 개념
-
함수는 독립적인 개념
-
메서드는 클래스에 종속되어 존재
-
-
일급 시민
-
런타임 때 자유롭게 값을 전달할 수 있는 것
-
자바 8 이전의 메서드는 일급 시민이 아니다
-
자바 8부터는 메서드를 일급 시민으로 취급한다
-
-
메서드 참조
- 이 메서드를 값으로 사용하라는 의미
- '::'를 이용
File[] hiddenFiles = new File(".").listFiles(File::isHidden);
-
람다를 이용하여 함수를 값 취급
-
직접 메서드를 정의할 수도 있지만, 이용할 수 있는 편리한 클래스나 메서드가 없을 때 사용
-
간결한 코드 구현이 가능하다
-
//x라는 인수를 호출하면 x + 1을 반환
(int x) -> x + 1
1.4 스트림
모든 자바 애플리케이션은 컬렉션을 만들고 활용한다.
기존의 컬렉션 방식은 반복 과정을 직접 처리해야 한다(외부 반복)
Map<Currency, List<Transaction>> transactionsByCurrencies = new HashMap<>();
for (Transaction transaction : transactions) {//트랜잭션 리스트 반복
Currency currency = transaction.getCurrency();//트랜잭션 통화 추출
List<Transaction> transactionsForCurrency = transactionsByCurrencies.get(currency);
if (transactionsForCurrency == null) {
transactionsForCurrency = new ArrayList<>();
transactionsByCurrencies.put(currency, transactionsForCurrency);
}
transactionsForCurrency.add(transaction);
}
스트림 API를 이용하면 라이브러리 내부에서 모든 데이터가 처리되어 코드가 간결해진다(내부 반복)
Map<Currency, List<Transaction>> transactionsByCurrencies = transactions.stream()
.collect(groupingBy(Transaction::getCurrency));
1.5 디폴트 메서드와 자바 모듈
-
디폴트 메서드
-
인터페이스에서 구현체 작성 가능
- 디폴트 메서드가 없다면, 인터페이스에 새로운 메서드 추가했을 때 인터페이스를 구현하는 모든 클래스에 새로 추가된 메서드를 구현해야 한다.
-
아래는 List 인터페이스에 구현되어 있는 디폴트 메서드인 sort() 메서드이다.
자바 8 이전에는 List를 구현하는 모든 클래스가(ArrayList 같은..) sort를 구현해야 했지만 자바 8부터는 구현하지 않아도 된다.
-
디폴트 메서드의 다중 상속 문제 해결
1.6 함수형 프로그래밍에서 가져온 다른 유용한 아이디어
-
NullPointer 예외를 피할 수 있도록 도와주는 Optional <T> 클래스 제공
-
패턴 매칭 기법