종류 | 람다 | 메소드 참조 |
---|---|---|
정적(static) 메소드 참조 | (x) → ClassName.method(x) | ClassName::method |
인스턴스 메소드 참조 | (obj, x) → obj.method(x) | ClassName::method |
메소드를 참조해서 매개 변수의 정보 및 리턴 타입을 알아내어 람다식에서 불필요한 매개변수를 제거하는 것이 목적
(left, right) -> Math.max(left, rigth);
== Math::max
package chap12;
import java.util.function.IntBinaryOperator;
public class MethodReferencesExample {
public static void main(String[] args) {
IntBinaryOperator operator;
// 정적 메서드 호출
//operator = ((left, right) -> Calculator.staticMethod(left, right)); // == operator = Calculator::staticMethod;
operator = Calculator::staticMethod;
System.out.println("정적 메서드 호출 결과 : " + operator.applyAsInt(1, 2));
// 인스턴스 메소드 호출
Calculator calculator = new Calculator();
// operator = (left, right) -> calculator.method(left, right); // == operator = calculator::method;
operator = calculator::method;
System.out.println("인스턴스 메서드 호출 결과 : " + operator.applyAsInt(3, 5));
}
}
람다식에서 제공되는 a 매개변수의 메서드를 호출해서 b 매개변수를 매개값으로 사용하는 경우
(a, b) -> {a.instanceMethod(b);}
// 해당 코드의 메서드를 표현하면 a 클래스 이름 뒤에 ::를 붙이고 메서드 이름 기술
클래스::instanceMethod
메서드 참조는 생성자 참도도 포함
생성자를 참조한다는 것은 객체를 생성하는 것을 의미
단순히 메소드 호출로 구성된 람다식을 메소드 참조로 대치할 수 있듯이, 단순히 객체를 생성하고 리턴하는 람다식은 생성자 참조로 대치할 수 있다.
(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) {
// 두개 차이가 없는데 ctrl 누르고 클릭해서 매개변수 확인
Function<String, Member> function = (x) -> new Member(x);
// 생성자 참조
function = Member::new;
function.apply("string");
BiFunction<String, String, Member> function2 = (x, y) -> new Member(x, y);
// 생성자 참조
function2 = Member::new;
function2.apply("string", "string2");
}
}
스트림은 데이터의 흐름
자바 8부터 사용할 수 있는 기능이며 배열이나 컬렉션을 가공하여 원하는 결과를 얻을 수 있다.
흐름 생성 > 가공 (조건절로 필터링, 람다식. 메서드 참조) > 결과 수집 및 반환
// Collection 인터페이스의 메서드로 제공
Stream<E> stream()
// ex) 스트림을 사용하지 않았을 때
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
for (int n: numbers) {
if (n % 2 == 0) {
System.out.println(n);
}
}
// ex) 스트림을 사용했을 때
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
numbers.stream()
.filter(n -> n % 2 == 0)
.forEach(System.out::println);
각 단계 (생성, 가공, 결과)마다 데이터를 다루기위한 함수가 존재
이름 | 설명 |
---|---|
BaseStream | 모든 스트림에서 사용할 수 있는 공통 메소드들이 정의되어 있습니다. 코드에서 직접 사용하지는 않습니다. |
Stream | 객체 요소를 처리하는 스트림입니다. |
IntStream | int 형을 처리하는 스트림입니다. |
LongStream | long 형을 처리하는 스트림입니다. |
DoubleStream | double 형을 처리하는 스트림입니다. |
“java.util.stream” 패키지에는 스트림 인터페이스가 정의되어 있고 BaseStream을 부모로해서 자식 인터페이스들이 상속 밭는 구조를 이루고 있다.
컬렉션 타입(Collection, List, Set)의 경우 디폴트 메소드 stream을 이용하여 스트림을 만들 수 있다.
배열은 Arrays.stream 메서드를 사용하여 스트림을 생성한다.
range, rangeClosed 메서드를 이용하여 특정 범위의 숫자를 가지는 스트림을 생성할 수 있다.
range, rangeClosed의 차이는 두번째 인자인 범위의 끝 값이 포함 되냐 안되냐 차이다. (후자가 포함)
DoubleStream doubleStream = new Random().doubles(5); // 난수 5개 생성
Files 클래스의 정적 메서드인 lines와 BufferedReader의 lines 메서드로 해당 파일의 각 라인을 스트링 타입의 스트림으로 만들 수 있다.
Stream.concat 메서드를 이용하여 두 개의 스트림을 연결할 수 있다.
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;
import static java.lang.Class.forName;
public class StreamExample {
public static void main(String[] args) throws IOException {
// 1. 컬렉션으로 스트림 생성
List<String> list = Arrays.asList("1", "2", "3", "4");
Stream<String> stringStream = list.stream();
// 2. 배열로 스트림 생성
String[] array = {"1", "2", "3", "4"};
Stream<String> stringStream2 = Arrays.stream(array);
// 3. 숫자 범위 스트림 생성
IntStream intStream = IntStream.range(1, 5); // 범위 지정 [1,2,3,4] 마지막 값 제외
LongStream longStream = LongStream.rangeClosed(1, 5); // 범위 지정 [1,2,3,4,5] 마지막 값 포함
DoubleStream doubleStream = DoubleStream.of(1.0, 2.0, 3.0); // 값 직접 기입 [1.0, 2.0, 3.0]
// 4. 파일을 통한 스트림 생성
Stream<String> fileStream = Files.lines(Paths.get("file1.txt"), Charset.forName("UTF-8"));
fileStream.forEach(System.out::println);
// 5. 스트림 연결해서 하나의 스트림 생성
Stream<Integer> intStream1 = Stream.of(1, 2, 3);
Stream<Integer> intStream2 = Stream.of(4, 5, 7);
Stream<Integer> concated = Stream.concat(intStream2, intStream1);
concated.forEach(System.out::println); // == concated.forEach(x -> System.out.println(x));
}
}
// 결과
first line
new jeans
4
5
7
1
2
3
하나의 래퍼 클래스를 말함.
해당 클래스 내에는 객체를 가지고 있는데 그 객체는 실제 주소값을 가진 값일수도 null 값일수도 있다. 이때 optional을 감싸서 사용하는 이유 → NullPointException을 방지하기 위해서.
// student 객체가 null일 경우 NullPointerException 발생
Student student = null;
// null 값에 getName이라는 메소드는 존재하지 않기 때문
student.getName();
null 값을 가진 객체를 참조하려고 했을 때 NPE가 발생하는데, NPE는 컴파일 시점에는 발견할 수 없기 때문에 프로그래머는 항상 객체가 null값이 될 수 있음을 인지하고 소스코드를 작성해야한다.
컬렉션의 경우 동적으로 생성되는 경우가 많다.
List<Integer> list = null;
list.stream().forEach(System.out::println); // NPE, null값은 stream이라는 메서드를 가지고 있지 않기 때문.
// NPE 방지
List<Integer> list = null;
Optional.ofNullable(list)
.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 객체를 반환함. |
기본적으로 해당 클래스는 get() 메서드를 사용하여 Optional 객체에 저장된 값을 반환하지만, 만약 해당 객체가 null일 경우에는 예외가 발생한다.
안전하게 객체를 꺼내오기 위해서는 null일 경우 사용할 default 값을 정할 수 있다.
이때 사용하는 메서드가 orElse(), orElseGet()메서드이고 orElseGet() 메서드는 인수로 람다식을 사용할 수 있다.
Optional.ofNullable(stringList)
.get() // stringList가 null일 경우 NoSuchElementException 예외 발생!!
List<String> newList = Optional.ofNullable(list)
.orElse(Collections.emptyList());
List<String> newList = Optional.ofNullable(list)
.orElseGet(Collections::emptyList); // 람다 표현식을 넣는 경우
메소드 | 설명 |
---|---|
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;
// Optional
Optional<List<String>> optional = Optional.ofNullable(list);
optional.orElseGet(Collections::emptyList) // == optional.orElseGet(() -> Collections.emptyList());
.forEach(System.out::println); // == .forEach(x -> System.out.println(x));
Optional.ofNullable(list)
.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(456); // value로 null이 들어가면 오류가 발생한다.
Optional<Integer> optional3 = Optional.ofNullable(3281);
// Optional 객체 꺼내기 .get(), orElse(), orElseGet(), orElseThrow()
Integer value = empty.orElse(123);
System.out.println(value); // 123
// empty2.orElseGet(() -> 12);
optional3.orElseThrow();
Integer i = optional2.orElseThrow(NoSuchElementException::new); // == Integer i = empty.orElseThrow(() -> new NoSuchElementException() );
System.out.println(i);
// isPresent() 값을 체크하는 목적으로 많이 사용
if(optional3.isPresent()){
Integer integer = optional3.get();
System.out.println(integer);
}
}
}
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 예외 발생
// double avg = list.stream()
// .mapToInt(Integer::intValue)
// .average()
// .getAsDouble();
//
// System.out.println(avg);
// NoSuchElementException 예외 발생 안함
// optional 객체 안전하게 꺼내는 방법 1
OptionalDouble optionalDouble = list.stream()
.mapToInt(Integer::intValue)
.average();
if(optionalDouble.isPresent()) {
double avg = optionalDouble.getAsDouble();
System.out.println(avg);
}
// optional 객체 안전하게 꺼내는 방법 2
double result = optionalDouble.orElse(0.0); // 0.0 이라는 값 defalut로 설정
System.out.println(result); // 0.0
//
List<Integer> list2 = new ArrayList<>();
OptionalDouble optionalDouble2 = list2.stream()
.mapToInt(Integer::intValue)
.average();
optionalDouble.ifPresent(System.out::println); //== optionalDouble.ifPresent(avg -> System.out.println(avg));
}
}