좌측에는 넘겨지는 매개 변수들의 타입이 선언되고, 중간에는 화살표 연산자, 가장 우측에는 리턴되는 값을 표시한다.
// 람다 표현식 사용을 위한 메소드 1개인 인터페이스 생성
interface Calculate {
int operation(int a, int b);
}
// 익명 클래스 사용
private void calcuateClassic() {
Calculate calculateAdd = new Calculate() {
public int operation(int a, int b) {
return a + b;
}
}
}
// 람다 표현식 사용
private void calculateLambda(){
Calculate calculateAdd = (a, b) -> a + b;
System.out.println(calculateAdd.operation(1, 2)); // 3 출력
Calculate calculateSubtract = (a, b) -> a - b;
System.out.println(calculateSubtract.operation(1, 2)); // -1 출력
}
람다 표현식 예제 소스에서 Calculate는 메소드가 하나만 선언되어 있기 때문에 (a, b)라고 되어 있는 부분은 operation() 메소드의 int a와 int b를 매개 변수로 받는다는 의미. -> 옆에 a + b는 결과로 a + b의 합을 리턴한다는 의미.
Calculate 인터페이스와 같이 하나의 메소드만 선언되어 있는 인터페이스를 Functional(기능적) 인터페이스라고 한다.
람다 표현식을 사용하기 위한 하나의 장치와 같은 역할을 해준다.
Functional interface라는 것을 확실히 하기 위해 @FunctionalInterface 어노테이션을 사용해 하나의 메소드만 선언할 수 있도록 할 수 있다.
// Runnable 인터페이스를 람다 표현식으로 사용
private void runThread() {
new Thread(() -> {
System.out.println(Thread.currentThread().getName());
}).start();
}
쓰레드를 처리하는 Runnable 인터페이스도 run() 메소드 하나만 선언된 인터페이스이기 때문에 람다 표현식으로 표현이 가능하다.
java.util.function 패키지
Java 8에서 제공하는 주요 Functional 인터페이스
Predicate
두 개의 객체를 비교할 때 사용해 boolean을 리턴하는 test() 메소드가 있다.
default metod로는 and(), negate(), or() 이 있다.
negate()는 데이터가 조건과 다른지 확인한다.
and()는 데이터가 두 개의 조건과 모두 맞는지 확인한다.
or()은 데이터가 두 개의 조건 중 하나라도 맞는지 확인한다.
isEqual() static 메소드도 존재한다.
Supplier
generic 타입을 리턴하는 get() 메소드가 있다.
get() 메소드 외 다른 추가적인 메소드는 없다.
Consumer
리턴 값이 없고 매개 변수를 하나 갖는 accept() 메소드가 있다.
출력할 때처럼 작업 수행 후 결과를 받을 일이 없을 때 사용한다.
default method로 순차적인 작업을 할 때 사용하는 andThen() 메소드가 있다.
Function
하나의 매개 변수와 리턴 값을 갖는 apply() 메소드가 있다.
Function<T, R>로 인터페이스가 선언되어 있으며 Generic 타입을 두 개 가지고 있다.
T는 입력 타입, R은 리턴 타입을 의미하며 변환을 할 필요가 있을 때 Function 인터페이스를 사용
UnaryOperator T -> T
하나의 매개 변수와 동일한 타입을 리턴하는 apply() 메소드가 있다.
한 가지 타입에 대해서 결과도 동일한 타입일 경우 사용한다.
BinaryOperator (T, T) -> T
두 개의 매개 변수와 동일한 타입을 리턴하는 apply() 메소드가 있다.
한 가지 타입에 대해서 결과도 동일한 타입일 경우 사용한다.
// Predicate 인터페이스
Predicate<String> preLen = (a) -> a.length > 5; // 문자열 a의 길이가 5보다 큰지 여부를 리턴한다.
Stream
stream은 사전적 의미로 시내, 줄기, 줄 등 줄줄이 이어져 있는 것을 나타낸다.
자바 stream은 "뭔가 연속된 정보"를 처리하는 데 사용한다.
이전에 자바에서 연속된 정보를 처리하는 방식은 배열과 컬렉션이 있다.
배열은 스트림을 사용할 수 없고 컬렉션은 스트림을 사용할 수 있다.
배열을 컬렉션 List로 변환하는 방법이 여러 가지 존재한다.
Integer[] values = {1, 3, 5};
// 배열을 List로 변환
// 1번 방법 Arrays.asList() 사용
List<Intger> list = new ArrayList<Integer>(Arrays.asList(values));
// 2번 방법 stream() 사용
Arrays.stream().collect(Collectors.toList());
Stream의 구조
list.stream().filter(x -> x>10).count();
스트림 생성 : 컬렉션의 목록을 스트림 객체로 변환한다. ex) stream()
중개 연산 : 생성된 스트림 객체를 사용해 중개 연산을 처리하지만 Stream 객체를 리턴한다. ex) filter(x -> x>10)
종단 연산 : 중개 연산에서 작업된 내용을 바탕으로 결과를 리턴한다. ex) count()
중개 연산은 반드시 있어야 하는 것이 아닌 0개 이상의 중개 연산이 존재할 수 있다.
stream()은 순차적으로 데이터를 처리한다.
10개의 데이터가 있다면 인덱스 0~9번째 인덱스를 처음부터 하나씩 처리한다.
stream() 보다 빠른 처리를 위해 parallelStream()을 사용할 수 있다.
CPU도 많이 사용하고 몇 개의 쓰레드로 처리할지 보장되지 않기 때문에 parallelStream()은일반적인 웹 프로그램에서는 거의 사용하지 않는다.