메소드를 참조해서 매개 변수의 정보 및 리턴 타입을 알아내어 람다식에서 불필요한 매개 변수를 제거하는 것이 목적이다. 인터페이스의 익명 구현 객체로 생성되며 타겟 타입인 인터페이스의 추상 메소드가 어떤 매개 변수를 가지고, 리턴 타입이 무엇인가에 따라 달라진다.
정적 메소드또는 인스턴스 메소드를 참조할 수 있고, 생성자 참조도 가능하다.
| 종류 | 람다 | 메소드 참조 |
|---|---|---|
| 정적(static) 메소드 참조 | (x) → ClassName.method(x) | ClassName::method |
| 인스턴스 메소드 참조 | (obj, x) → obj.method(x) | ClassName::method |
// 두 개의 값을 받아 큰 수를 리턴하는 Math 클래스의 max() 정적 메소드를 호출하는 람다식
(left, right) -> Math.max(left, right);
// 메소드 참조
Math::max;
실습
@FunctionalInterface
public interface IntBinaryOperator {
int applyAsInt(int left, int right);
}
// Math.max 메소드
public static int max(int a, int b) {
return Math.max(a, b);
}
// 1단계
IntBinaryOperator operator = (a, b) -> Math.max(a, b);
// 2단계
IntBinaryOperator operator = Math::max; // 메소드 참조
// 정적 메소드
클래스::메소드
// 인스턴스 메소드
참조변수::메소드
실습
package chap12;
public class Calculator {
// 정적 메소드
static int staticMethod(int left, int right){
return left + right;
}
// 인스턴스 메소드
public int method(int left, int right){
return left + right;
}
}
package chap12;
import java.util.function.IntBinaryOperator;
public class MethodReferenceExample {
public static void main(String[] args) {
IntBinaryOperator operator;
// 정적 메소드 호출
operator = (left, right) -> Calculator.staticMethod(left, right);
System.out.println(operator.applyAsInt(1, 2));
operator = Calculator::staticMethod; // 메소드 참조
// 인스턴스 메소드 호출
Calculator calculator = new Calculator();
operator = (left, right) -> calculator.method(left, right);
operator = calculator::method;
System.out.println(operator.applyAsInt(1,2));
}
}
// a 매개변수의 메소드를 호출해서 b 매개변수를 매개값으로 사용하는 경우
(a, b) -> { a.instanceMethod(b); }
// 메소드 참조
클래스::instanceMethod
실습
package chap12;
import java.util.function.ToIntBiFunction;
public class MethodParameterReferencesExample {
public static void main(String[] args) {
ToIntBiFunction<String, String> function;
function = String::compareToIgnoreCase;
print(function.applyAsInt("a","A"));
}
static void print(int order){
if(order < 0){
System.out.println("사전순으로 먼저 옵니다.");
} else if(order == 0){
System.out.println("동일한 문자열입니다.");
} else{
System.out.println("사전순으로 나중에 옵니다.");
}
}
}
동일한 문자열입니다.
작성 방법은 정적 메소드 참조와 동일하지만, a의 인스턴스 메소드가 참조되므로 전혀 다른 코드가 실행된다.
생성자를 참조한다는 것은 객체를 생성하는 것을 의미, 단순히 객체를 생성하고 리턴하는 람다식은 생성자 참조로 대치할 수 있다.
(a, b) -> { return new 클래스(a, b); }
// 생성자 참조
클래스 :: new
실습
package chap12;
public class Member {
private String name;
private String id;
public Member(String name) {
System.out.println("매개변수 하나인 생성자 호출");
this.name = name;
}
public Member(String name, String id) {
System.out.println("매개변수 두개인 생성자 호출");
this.name = name;
this.id = id;
}
}
package chap12;
import java.util.function.BiFunction;
import java.util.function.Function;
public class ConstructorReferencesExample {
public static void main(String[] args) {
Function<String, Member> function = Member::new;
function.apply("string");
BiFunction<String, String, Member> function2 = Member::new;
function2.apply("string", "string2");
}
}
매개변수 하나인 생성자 호출
매개변수 두개인 생성자 호출
데이터 흐름(생성 → 가공 → 결과), 자바 8버전 부터 사용할 수 있는 기능, 배열이나 컬렉션을 가공하여 원하는 결과를 얻을 수 있다. 람다식을 가공할 때 많이 사용한다. 자바에서 제공해주는 Collection 인터페이스의 메서드로 스트림을 제공해준다. → Stream<E> stream()
스트림은 배열과 컬렉션을 처리할 때 for, while 문을 사용하지 않고 함수형으로 처리하며 데이터 처리 과정을 filter, forEach 와 같은 단어로 사용하여 가독성을 높여주고, 병렬 처리(하나의 작업을 둘 이상으로 나누어 동시에 진행)가 가능하다는 장점이 있다.
distinct, filter, map, flatMap, sorted, peekforEach, collect()“java.util.stream” 패키지에는 스트림 인터페이스가 정의되어 있고, BaseStream을 부모로 해서 자식 인터페이스들이 상속 구조를 이루고 있다.
BaseStream
| 이름 | 설명 |
|---|---|
| BaseStream | 모든 스트림에서 사용할 수 있는 공통 메소드들이 정의되어 있습니다. 코드에서 직접 사용하지는 않습니다. |
| Stream | 객체 요소를 처리하는 스트림입니다. |
| IntStream | int 형을 처리하는 스트림입니다. |
| LongStream | long 형을 처리하는 스트림입니다. |
| DoubleStream | double 형을 처리하는 스트림입니다. |
실습
package chap13;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;
public class StreamExample {
public static void main(String[] args) throws IOException {
// 1. 컬렉션으로 스트림 생성
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream(); // 흐름 생성
// 2. 배열로 스트림 생성
String[] array = {"a", "b", "c"};
Stream<String> stream2 = Arrays.stream(array);
// 3. 숫자 범위로 스트림 생성
IntStream intStream = IntStream.rangeClosed(1, 5); // [1,2,3,4,5]
LongStream longStream = LongStream.range(1, 5); // [1,2,3,4]
DoubleStream doubleStream = DoubleStream.of(1, 2, 3); // of : 특정 값을 입력해서 만듦 [1.0,2.0.3.0]
// 4. 파일을 통한 스트림 생성
Stream<String> fileStream = Files.lines(Paths.get("example.txt"), Charset.forName("UTF-8"));
fileStream.forEach(System.out::println); // stream 안에서 제공해주는 루프 메소드 사용, 요소 각각 출력
// 5. 스트림 연결해서 하나의 스트림 생성
Stream<Integer> intStream1 = Stream.of(1, 2, 3);
Stream<Integer> intStream2 = Stream.of(4, 5, 6);
Stream<Integer> concated = Stream.concat(intStream2, intStream1);
concated.forEach(System.out::println); // 요소 각각 출력, 4 5 6 1 2 3
}
}
NPE를 방지할 수 있도록 도입된 null을 포함한 모든 데이터를 저장할 수 있는 Wrapper 클래스이며, 실제 값이 있는지 여부를 체크하거나, 만약 값이 없을 경우 디폴트 값을 지정할 수 있는 기능 등을 제공한다. → 객체(Object)가 null인지 아닌지는 알 수 없으나 일단 Optional로 감싼다.
List<Integer> list = null;
list.stream().forEach(System.out::println); // NPE
// Optional로 컬렉션의 null 여부 체크
List<Integer> list = null;
Optional.ofNullable(list) // Optional.ofNullable 사
.orElseGet(Collections::emptyList)
.forEach(System.out::println);
Optional 클래스는 of와 ofNullable메소드를 지원한다. of() 메소드나 ofNullable() 메소드를 사용하여 Optional 객체를 생성할 수 있다.of는 null값을 허용하지 않고 ofNullable은 null값을 허용한다.
| 메소드 | 설명 |
|---|---|
| static Optional empty() | 아무런 값도 가지지 않는 비어있는 Optional 객체를 반환함. |
| static Optional of(T value) | null이 아닌 명시된 값을 가지는 Optional 객체를 반환함. |
| static Optional ofNullable(T value) | 명시된 값이 null이 아니면 명시된 값을 가지는 Optional 객체를 반환하며, 명시된 값이 null이면 비어있는 Optional 객체를 반환함. |
Optional 클래스의 get()메서드를 사용하여 Optional 객체에 저장된 값을 반환하지만 안전하게 객체를 꺼내오기 위해 null일 경우 사용할 default 값을 정할 수 있는데, 이때 orElse(), orElseGet() 메소드를 사용한다. → orElseGet() 메소드는 인수로 람다 표현식을 사용할 수 있다.
| 메소드 | 설명 |
|---|---|
| T get() | Optional 객체에 저장된 값을 반환함. |
| T orElse(T other) | 저장된 값이 존재하면 그 값을 반환하고, 값이 존재하지 않으면 인수로 전달된 값을 반환함. |
| T orElseGet(Supplier<? extends T> other) | 저장된 값이 존재하면 그 값을 반환하고, 값이 존재하지 않으면 인수로 전달된 람다 표현식의 결과값을 반환. |
| T orElseThrow(Supplier<? extends X> exceptionSupplier) | 저장된 값이 존재하면 그 값을 반환하고, 값이 존재하지 않으면 인수로 전달된 예외를 발생시킴. |
| boolean isPresent() | 저장된 값이 존재하면 true를 반환하고, 값이 존재하지 않으면 false를 반환함. |
실습
package chap13.optional;
import java.util.*;
public class OptionalExample {
public static void main(String[] args) {
List<String> list = null;
// list.stream().forEach(System.out::println); // NullPointerException
// Optional
Optional<List<String>> optional = Optional.ofNullable(list);
// optional.orElseGet(() -> Collections.emptyList()); // 값 꺼내오기
optional.orElseGet(Collections::emptyList)
.forEach(System.out::println);
// Optional 객체 생성 empty(), of(), ofNullable()
Optional<Integer> empty = Optional.empty();
OptionalInt empty2 = OptionalInt.empty();
OptionalDouble empty3 = OptionalDouble.empty();
Optional<Integer> optional2 = Optional.of(10);
Optional<Integer> optional3 = Optional.ofNullable(3456);
// Optional 객체 꺼내기 get(), orElse(), orElseGet(), orElseThrow()
Integer value = empty.orElse(123); // value 123
// empty2.orElseGet(() -> 12);
Integer i = optional2.orElseThrow(NoSuchElementException::new);
System.out.println(i);
// isPresent()
if (optional.isPresent()) {
Integer integer = optional3.get();
System.out.println(integer);
}
}
}
컬렉션이 null은 아니지만 아무런 값도 가지고 있지 않은 경우
실습
package chap13.optional;
import java.util.ArrayList;
import java.util.List;
import java.util.OptionalDouble;
public class OptionalExample2 {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
// NoSuchElementException 예외 발생
OptionalDouble optionalDouble = list.stream()
.mapToInt(Integer::intValue)
.average()
;
optionalDouble.ifPresent(System.out::println); // 방법 3
/* 빙법 2
double result = optionalDouble.orElse(0.0);
System.out.println(result);
*/
/* 방법 1
if(optionalDouble.isPresent()) {
double avg = optionalDouble.getAsDouble();
System.out.println(avg);
}
*/
}
}