5 min to read
자바 8 인 액션
1. java8을 사용하는 이유
- 2014년, 그 동안의 java의 발전 중 가장 큰 변화가 java 8에서 나옴.
- 멀티코어 cpu 대중화와 같은 HW적인 변화에 더불어 java 8에서는 병렬 실행을 새롭고 단순한 방식으로 접근 할 수 있는 방법을 제공하게 됐음.
- java 8이 제공하는 새로운 기술
- 스트림 API
- 기존 synchronized를 사용하지 않고도 스트림을 활용하여 멀티코어 CPU를 이용할 수 있음.
- 메서드에 코드를 전달하는 기법
- 람다
- 동작 파라미터화
- 함수형 프로그래밍
- interface의 default method
- 스트림 API
스트림 처리
- 스트림이란 한 번에 한 개씩 만들어지는 연속적인 데이터 항목들의 모임.
- 데이터베이스 질의처럼 일련의 스트림으로 만들어 처리
동작 파라미터화로 메서드에 코드 전달
- 메서드를 다른 메서드의 인수로 넘겨주는 기능 → 동작 파라미터화
- 스트림 API는 동작을 파라미터화 하여 코드를 다른 메서드로 넘겨줄 수 있는 기능을 제공함.
병렬성과 공유 가변 데이터
- 병렬성을 얻기 위해선 스트림 메서드로 전달하는 코드의 동작 방식을 변경해야 함.
- 다른 코드와 동시에 실행되더라도 안전하게 실행 하려면 공유된 가변 데이터에 접근하지 않아야 함.
- 함수형 프로그래밍의 핵심
- 공유되지 않은 가변 데이터
- 메서드와 함수 코드를 다른 메서드로 전달 가능
자바 함수
- 기본 값, 객체, 배열 등을 일급값(일급 시민)으로 부름.
- 일급 시민과 달리 메서드, 클래스처럼 값을 전달할 수 없는 구조체를 이급시민으로 부름.
- 이급시민 중 메서드도 전달할 수 있다면 프로그래밍에 매우 유용할 것이라는 생각에 이급 시민을 일급 시민으로 바꿀 수 있는 기능이 자바8에 추가됨.
- 메서드를 값으로 취급할 수 있음으로써 개발자들이 더 쉽게 프로그램을 구현할 수 있는 환경이 제공됨.
메서드 레퍼런스
File[] hiddenFiles = new File(".").listFiles(new FileFilter() {
public boolean accept(File file) {
return file.isHidden();
}
});
- 위와 같은 코드를 메서드 레퍼런스 기능을 통해 아래와 같이 더 간결한 코드 구현이 가능해짐.
File[] hiddenFiles = new File(".").listFiles(File::isHidden);
- :: (메서드를 값으로 활용한다는 의미)를 통해 메서드를 값 처럼 사용할 수 있게 됨.
람다: 익명 함수
- 위와 같은 코드를 람다를 포함하여 함수도 값으로 취급 할 수 있음.
public static boolean isGreenApple(Apple apple) { return "green".equals(apple.getColor()); } public static boolean isHeavyApple(Apple apple) { return apple.getWeight() > 150; } public interface Predicate<T>() { boolean test(T t); } static List<Apple> filterApples(List<Apple> inventory, Predicate<Apple> p) { List<Apple> result = new ArrayList<>(); for (Apple apple : inventory) { if (p.test(apple)) result.add(apple); } return result; } filterApples(inventory, Apple::isGreenApple); filterApples(inventory, Apple::isHeavyApple);
메서드 전달에서 람다로
- 위 isGreenApple, isHeavyApple 처럼 한 두번만 사용할 메서드라면 매번 정의하는 것이 불편할 것이다. 이러한 경우 람다를 활용하여 해결 할 수 있다.
filterApples(inventory, (Apple a) -> "green".equals(a.getColor())); filterApples(inventory, (Apple a) -> a.getWeight() > 150); filterApples(inventory, (Apple a) -> "green".equals(a.getColor()) || a.getWeight() > 150);
- 하지만, 람다가 몇 줄 이상 길어진다면 익명 람다보다는 명확하게 정의된 메서드를 구현하는 것이 바람직함.
- 코드의 명확성이 우선시되어야 되기 때문.
- 스트림 API 중 하나인 filter() 메서드를 활용한다면 위 코드에서 filterApples() 메서드도 일일이 구현할 필요가 없어짐.
스트림
Map<Currency, List<Transaction>> transactionsByCurrencies = new HashMap<>();
for (Transaction transaction : transactions) {
if (transaction.getPrice() > 1000) {
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>> transactionsByCurrencis = transactions.stream() .filter((Transaction t) -> t.getPrice() > 1000) .collect(groupingBy(Transaction::getCurrency());
- 기존 for-each 를 이용해서 반복적인 작업을 수행했으나(외부 반복) 스트림 API를 활용함으로써 루프를 신경쓰지 않게 되었음.
- 스트림 API를 사용함으로써 라이브러리 내부에서 로직이 돎으로써 내부 반복이 일어나게 됨.
- 스트림은 스트림 내의 요소를 쉽게 병렬로 처리할 수 있는 환경을 제공함.
- 컬렉션을 필터링 할 수 있는 가장 빠른 방법 → 컬렉션을 스트림으로 변형 후, 병렬로 처리한 다음, 리스트로 다시 복원
- 순차 처리 → .stream()
- 병렬 처리 → .parallelStream()
디폴트 메서드
- 라이브러리 설계자가 더 쉽게 변화할 수 있는 인터페이브를 만들 수 있도록 하는 것.
- default 키워드 사용.
함수형 프로그래밍의 핵심 아이디어
- 메서드와 람다를 일급값으로 사용
- 가변 공유 상태가 없는 병렬 실행을 이용한 함수 or 메서드 호출
- NullPointer 예외 회피
- Optional
클래스 제공 - 값을 갖거나 갖지 않을 수 있는 컨테이터 객체로, 변수에 값이 없을 때 처리를 명시 할 수 있음.
- Optional
Comments