자바 8
위 두가지 요구사항을 기반으로 한다.
자바8 추가된 기능
1. 스트림 API
2. 메서드에 코드를 전달하는 기법
3. 인터페이스의 디폴트 메서드
스트림 API 덕분에
➡️ 메서드에 코드를 전달하는 간결 기법(메서드 참조와 람다)과 인터페이스의 디폴트 메서드가 존재
할 수 있도록 3 가지 프로그래밍 개념을 다룬다.
스트림이란?
자바 8에서 java.util.stream 패키지에 스트림 API 추가
Stream<T>
: T
형식으로 구성된 일련의 항목을 의미스트림 API의 핵심
자바 8에서는 우리가 하려는 작업을 (DB 쿼리처럼) 고수준으로 추상화해서 일련의 스트림으로 만들어 처리할 수 있음
Comparator
객체를 만들어서 전달synchronized
를 이용해서 공유된 가변 데이터 보호 규칙을 만들 수도 있다.int
, double
"abc"
, new Integer(111)
, new HashMap<Integer, String> (100)
일급 값(시민)
int
, double
, 객체 ...이급 시민
➡️ 런 타임에 메서드를 전달할 수 있다면, 즉 메서드를 일급 시민으로 만들면 프로그래밍에 유용하게 활용될 수 있다.
<자바 8 이전>
File[] hiddenFiled = new File(".").listFiles(new Filter() {
public boolean accept(File file) {
return file.isHidden();
}
});
File 클래스에는 이미
isHidden
이라는 메서드가 있는데 굳이FileFilter
로isHidden
을 복잡하게 감싼 다음에FileFilter
를 인스턴스화해야 할까?
<자바 8 이후>
File[] hiddenFiles = new File.(".").listFiles(File::isHidden);
자바 8의 메서드 참조 ::를 이용해서
listFiles
에 직접 전달할 수 있다.
File::isHiden
을 이용해서 메서드 참조를 만들어 전달할 수 있다.(int x) -> x + 1
x
라는 인수로 호출하면 x + 1
을 반환 public static boolean isHeavyApple(Apple apple) {
return apple.getWeight() > 150;
}
public static boolean isGreenApple(Apple apple) {
return GREEN.equals(apple.getColor());
}
// java.util.function 에서 import 함
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;
}
다음처럼 메서드 호출할 수 있다.
filterApple(inventory, Apple::isGreenApple);
filterApple(inventory, Apple::isHeavyApple);
프레디케이트(predicate)란 무엇인가?
인수를 받아 true나 false 값을 반환하는 함수를 프레디케이트라고 한다.
자바 8에서도Function<Apple, Boolean>
같이 코드를 구현할 수 있지만Predicate<Apple>
을 사용하는 것이 더 표준적인 방식이다.
isHeavyApple
, isGreenApple
처럼 한두 번만 사용할 메서드를 매번 정의하는 것은 귀찮다.filterApples(inventory, (Apple a) -> GREEN.equals(a.getColor()));
filterApples(inventory, (Apple a) -> a.getWeight() > 150);
filterApples(inventory, (Apple a) -> a.getWeight() < 80 || RED.equals(a.getColor()) );
<컬렉션 사용>
Map<Currency, List<Tranaction>> transactionByCurrencies = 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 ArraysList<>();
transactionsByCurrencies.put(currency, transactionsForCurrency);
}
transactionsForCurrency.add(transaction); // 현재 탐색된 트랜잭션을 같은 통화의 트랜잭션 리스트에 추가
}
}
<스트림 사용>
import static java.util.stream.Collectors.groupingBy;
Map<Currency, List<Transaction>> transactionsByCurrencies
= transactions.stream()
.filter((Transaction t) -> t.getPrice() > 1000) // 고가의 트랜잭션 필터링
.collect(groupingBy(Transaction::getCurrency)); // 통화로 그룹화함
멀티스레딩 모델은 순차적인 모델보다 다루기가 어렵다.
자바 8은 스트림 API(java.util.stream)로 아래 2가지를 해결했다.
컬렉션
스트림
컬렉션을 필터링할 수 있는 가장 빠른 방법
1. 컬렉션을 스트림으로 바꾸고
2. 병렬로 처리한 다음
3. 리스트로 다시 복원
스트림과 람다 표현식을 이용하면 병렬성을 공짜로 얻을 수 있으며 리스트에서 무거운 사과를 순차적으로 또는 병렬로 필터링할 수 있다.
<순차 처리 방식의 코드>
import static java.util.stream.Collectors.toList;
List<Apple> heavyApples = inventory.stream().filter((Apple a) -> a.getWeight() > 150)
.collect(toList());
<병렬 처리 방식의 코드>
import static java.util.stream.Collectors.toList;
List<Apple> heavyApples = inventory.parallel.Stream().filter((Apple a) -> a.getWeight() > 150)
.collect(toList());
자바 8에서는 인터페이스를 쉽게 바꿀 수 있도록 디폴트 메서드를 지원한다.
인터페이스에 새로운 메서드를 추가하면 → 인터페이스를 구현하는 모든 클래스는 새로 추가된 메서드를 구현해야 한다....
default
라는 새로운 키워드를 지원자바 8에서는 List
에 직접 sort
메서드를 호출할 수 있다.
default void sort(Comparator<? super E> c) {
Collections.sort(this, c);
}
하나의 클래스에서 여러 인터페이스를 구현할 수 있음
➡️ 여러 인터페이스에 다중 디폴트 메서드가 존재할 수 있다는 것은 결국 다중 상속이 혀용되는 의미일까??
➡️ 어느 정도는 그렇다라고 말할 수 있다.
자바에 포함된 함수형 프로그래밍의 핵심적인 두 아이디어
1. 메서드와 람다를 일급값으로 사용하는 것
2. 가변 공유 상태가 없는 병렬 실행을 이용해서 효율적이고 안전하게 함수나 메서드를 호출할 수 있다는 것
NullPointer
예외를 피할 수 있도록 도와주는 Optional<T>
클래스를 제공한다.Optional<T>
는 값을 갖거나 갖지 않을 수 있는 컨테이너 객체