Java 8 함수형 인터페이스, 람다, 스트림

Java 8 함수형 인터페이스, 람다, 스트림

Java 8

함수형 인터페이스

함수형 인터페이스 구현

@Functionalnterface public interface RunSomething { void doIt(); // 추상 메소드가 하나만 있다면 함수형 인터페이스이다. 앞에 abstract 생략 // void doItAgain(); // 추상 메소드가 하나를 초과하면 함수형 인터페이스가 아니다. } public class Foo { public static void main(String[] args) { // Java 8 이전 방식 // 익명 클래스 RunSomething runSomething = new RunSomething() { @Override public void doIt() { System.out.println("dohyun"); } }; // Java 8 람다 표현식 RunSomething runSomething = () -> System.out.println("dohyun"); } }

자바에서 제공하는 함수형 인터페이스

Function

// Function<> 구현방법 1. public class Plus10 implements Function { @Override public Integer apply(Integer Integer) { return integer + 10; } } // Function<> 구현방법 2. public class Foo { public static void main(String[] args) { // apply Function plus10 = (i) -> i + 10; plus10.appy(1); // compose Function multi2 = (i) -> i * 2; plus10.compose(multi2).apply(2); // 먼저 2를 곱하고 10을 더한다. //andThen plus10.andThen(multi2).apply(2); // 먼저 10을 더하고 2를 곱한다. } }

BiFunction

두 개의 값(T, U)를 받아서 R 타입을 리턴하는 함수 인터페이스 : R apply(T t, U u)

Consumer

T 타입을 받아서 아무값도 리턴하지 않는 함수 인터페이스 : void Accept(T t)

함수 조합용 메소드 : andThen

Supplier

public class Foo { // get public static void main(String[] args) { Supplier get10 = () -> 10; get10.get(); } }

Predicate

public class Foo { // test public static void main(String[] args) { Predicate str = (s) -> s.startsWith("dohyun"); str.test("dohyun world") // return true } }

람다 표현식

public class Foo { public static void main(String[] args) { Foo foo = new Foo(); foo.run(); } private void run() { int baseNumber = 10; //로컬 클래스 class LocalClass { void printBaseNumber() { int baseNumber = 11; // 쉐도윙 ! System.out.println(baseNumber); // 11 } } //익명 클래스 Consumer integerConsumer = new Consumer() { @Override public void accept(Integer integer) { System.out.println(baseNumber); } }; //람다 IntConsumer printInt = (i) -> { // 만약 i -> baseNumber 라면 같은 스콥이기 때문에 컴파일 에러 ! System.out.println( i + baseNumber); }; /** 셋의 공통점 : 로컬 변수를 참조할 수 있다. 로컬, 익명 클래스 vs 람다 : 로컬, 익명 클래스는 쉐도윙하고 ( run() 안에 새로운 스콥 생성 ), 람다는 쉐도잉하지 않는다 ( run() 과 같은 스콥 ). **/ } }

메소드 레퍼런스

스태틱 메소드 참조 : 타입::스태틱 메소드

특정 객체의 인스턴스 : 메소드 참조 객체 레퍼런스::인스턴스 메소드

임의 객체의 인스턴스 : 메소드 참조 타입::인스턴스 메소드

생성자 참조 타입 : ::new

Java 8 API의 기본 메소드와 스태틱 메소드

public class App { public static void main(String[] args) { List name = new ArrayList<>(); name.add("dohyun"); name.add("github"); //forEach name.forEach( s -> { System.out.println(s); }); //stream name.stream() .map(String::toUpperCase) .filter( s -> s.startsWith("K")) .count(); //removeIf name.removeIf( s -> s.startsWith("K")); } }

Iterable의 기본 메소드

forEach()

spliterator()

Collection의 기본 메소드

stream() / parallelStream()

removeIf(Predicate)

spliterator()

Comparator의 기본 메소드 및 스태틱 메소드

reversed()

thenComparing()

static reverseOrder() / naturalOrder()

static nullsFirst() / nullsLast()

static comparing()

Stream

간단하게 말해, 연속된 데이터들을 처리하는 오퍼레이션들의 집합이다.

비유하자면 컨베이어 벨트에 데이터를 하나씩 흘려보내면서 특정 오페레이션을 수행하는 것이다.

스트림 자체가 데이터를 담고 있는 저장소 ( 컬렉션 ) 이 아니다.

손쉽게 병렬 처리가 가능하다.

public class App { public static void main(String[] args) { List names = new ArrayList<>(); name.add("dohyun"); name.add("github"); Stream stringStream = names.stream() .map(s -> s.toUpperCase()); names.forEach(System.out::printLn); // 데이터가 그대로 소문자로 남아있다. } }

스트림 파이프라인

0 또는 다수의 중개 오퍼레이션 (intermediate operation)과 한개의 종료 오퍼레이션 (terminal operation)으로 구성한다.

스트림의 데이터 소스는 오직 터미널 오퍼네이션을 실행할 때에만 처리한다.

중개 오퍼레이션

Stream을 리턴한다.

Stateless / Stateful 오퍼레이션으로 더 상세하게 구분할 수도 있다. (대부분은 Stateless지만 distinct나 sorted 처럼 이전 이전 소스 데이터를 참조해야 하는 오퍼레이션은 Stateful 오퍼레이션이다.)

filter, map, limit, skip, sorted, ...

종료 오퍼레이션

Stream을 리턴하지 않는다.

collect, allMatch, count, forEach, min, max, ...

Stream API 예제

걸러내기 - Filter(Predicate)

List springClasses = new ArrayList<>(); List javaClasses = new ArrayList<>(); // spring 으로 시작하는 수업 springClasses .stream() .filter(oc -> oc.getTitle().startsWith("spring")) // OnlineClass 타입 그대로 .forEach(oc -> System.out.println(oc.getId())); // close 되지 않은 수업 springClasses.stream() .filter(oc -> !oc.isClosed()) .forEach(oc -> System.out.println(oc.getId())); // 수업 이름만 모아서 스트림 만들기 springClasses.stream() .map(ec -> oc.getTitle()) // String 타입으로 변경 .forEach(oc -> System.out.println(oc.getId())); List> dohyunEvents = new ArrayList<>(); dohyunEvents.add(springClasses); dohyunEvents.add(javaClasses); // 두 수업 목록에 들어있는 모든 수업 아이디 출력 dohyunEvents.stream() .flatMap(list -> list.stream()) .forEach(oc -> System.out.println(oc.getId())); // 10부터 1씩 증가하는 무제한 스트림 중에서 앞에 10개 빼고 최대 10개 까지만 Stream.iterate(10, i -> i + 1) .skip(10) .limit(10) .forEach(System.out::println); // 자바 수업 중에 Test 가 들어있는 수업이 있는 지 확인 javaClasses.stream() .anyMatch(oc -> oc.getTitle().contains("Test")); // 스프링 수업 중에 제목이 spring 이 들어간 것만 모아서 List 로 만들기 springClasses.stream() .filter(oc -> oc.getTitle().contains("spring")) ,map(oc -> oc.getTitle()) .collect(Collectors.toList());

Optional

메소드에서 작업 중 특별한 상황에서 값을 제대로 리턴할 수 없는 경우 선택할 수 있는 방법

예외를 던진다. (비싸다, 스택트레이스를 찍어두니까.)

null을 리턴한다. (비용 문제가 없지만 그 코드를 사용하는 클리어인트 코드가 주의해야 한다.)

(자바 8부터) Optional을 리턴한다. (클라이언트에 코드에게 명시적으로 빈 값일 수도 있다는 걸 알려주고, 빈 값인 경우에 대한 처리를 강제한다.)

Optional

오직 값 한 개가 들어있을 수도 없을 수도 있는 컨네이너.

public Optional getProgress() { return Optional.ofNullable(progress); }

주의할 것

리턴값으로만 쓰기를 권장한다. (메소드 매개변수 타입, 맵의 키 타입, 인스턴스 필드 타입으로 쓰지 말자.)

Optional을 리턴하는 메소드에서 null을 리턴하지 말자.

프리미티브 타입용 Optional을 따로 있다. OptionalInt, OptionalLong,...

Collection, Map, Stream Array, Optional은 Opiontal로 감싸지 말 것.

Optional API 예제

public class App { public static void main(String[] args) { List springClasses = new ArrayList<>(); Optional spring = springClasses.stream() .filter(oc -> oc.getTitle().startsWith("spring")) .findFirst(); spring.isPresent(); // 있는 지 없는 지 spring.get(); // Optional 안의 값을 꺼낸다. 만약 Optional 안에 값이 없다면? NosuchElementException ! spring.ifPresent(oc -> { System.out.println(oc.getTitle()); }); OnlineClass onlineClass = spring.orElse(createNewClasses()); // spring 이 있던 없던 createNewClasses() 를 실행한다. OnlineClass onlineClass = spring.orElseGet(() -> createNewClasses()); // spring 이 있는 경우 createNewClasses() 를 실행하지 않는다. OnlineClass onlineClass = spring.orElseThrow(() -> { return new IllegalStateException(); }); Optional onlineClass = spring.filter(oc -> oc.getId() > 10); // 빈 Optional Optional integer = spring.map(oc -> oc.getId()); } private static OnlineClass createNewClasses() { return new OnlineClass(10, "New class", false); } }

REFERENCES

백기선 님 더 자바, Java 8

from http://rohyun.tistory.com/21 by ccl(A) rewrite - 2021-11-04 16:28:05