11 분 소요

1.1 역사의 흐름은 무엇인가?

자바 8은 자바 역사를 통틀어 가장 큰 변화가 일어났다. 기존의 자바는 대부분 CPU 코어 중 하나만을 사용했다. 하지만 멀티코어 CPU가 대중화되며 듀얼 혹은 쿼드 코어 이상을 지원하는 CPU가 내장되어 있는 기기들이 보급되었고, 이는 자바 8에 큰 영향을 미쳤다.

자바 8등장 이전에도 남은 코어를 활용하기 위해 스레드를 사용하는 것이 좋다고 누군가가 조언을 했을 것이다. 하지만 스레드는 관리가 어렵고 많은 문제를 발생할 수 있다. 자바는 이러한 병렬 실행 환경을 쉽게 관리하고 에러가 덜 발생하는 방향으로 진화하려 노력했다. 스레드 풀(thread pool), 병렬 실행 컬렉션(concurrent collection), Fork/Join 프레임워크를 제공했지만 개발자가 활용하기는 쉽지 않았다. 자바 8은 병렬 실행을 새롭고 단순한 방식으로 접근할 수 있는 방법을 제공한다.

자바가 변화하는 근본적인 이유 중 하나는 멀티코어 병렬성을 더 쉽게 이용할 수 있도록 하기 위해서다.

자바 8은 간결한 코드, 멀티코어 프로세서의 쉬운 활용이라는 두 가지 요구사항을 기반으로 새로운 기술을 제공한다. 제공하는 새로운 기술의 큰 틀은 아래와 같다.

  • 스트림 API (Stream API)
    • 병렬 연산을 지원하는 새로운 API
    • 고수준 언어로 원하는 동작을 표현하면, 구현에서 최적의 저수준 실행방법을 선택하는 방식으로 동작. 스트림 API는 구현의 역할을 수행
    • 에러를 자주 일으키며 멀티코어 CPU를 이용하는 것보다 비용이 훨씬 비싼 키워드 synchronized를 사용하지 않아도 된다.

멀티코어 CPU의 각 코어는 캐시를 포함하고 있다. 락을 사용하면 이러한 캐시가 동기화되어야 하므로 속도가 느린 캐시 일관성 프로토콜 인터코어 통신(cache-coherency-protocol intercore communication)이 이루어진다.

  • 메서드에 코드를 전달하는 기법 (Techniques for passing code to a method)
    • 메서드 참조와 람다, 코드 전달 뿐만 아니라 결과를 반환하고 다른 자료구조로 전달할 수도 있다.
    • 새롭고 간결한 방식으로 동작 파리미터화(behavior parameterization)를 구현할 수 있다.
    • 함수형 프로그래밍에서 위력을 발휘한다. 코드를 전달하거나 조합해서 자바의 강력한 프로그래밍 도구로 활용할 수 있다.
  • 인터페이스의 디폴트 메서드 (Default method of interface)
    • 인터페이스, 라이브러리의 간결성을 유지 및 재컴파일을 줄일 수 있다.


1.2 왜 아직도 자바는 변화하는가?

새로운 언어가 등장하면 진화하지 않은 기존 언어는 도태된다. 즉, 생태계와 비슷하게 진화(성장)하지 않으면 소멸하고 만다. 많은 사람이 완벽한 언어를 원하지만 현실적으로 그런 언어는 존재하지 않으며 모든 언어가 장단점을 갖고 있다. 자바는 1995년 첫 베타 버전이 공개된 이래 경쟁 언어를 대신해 커다란 생태계를 성공적으로 구축했다. 자바가 어떻게 그러한 성공을 거둘 수 있었을까?

1.2.1 프로그래밍 언어 생태계에서 자바의 위치

자바는 처음부터 많은 유용한 라이브러리를 포함하는 잘 설계된 객체지향 언어로 시작해 커다란 생태계를 구축한 언어가 될 수 있었다.

  • 캡슐화를 지원해 소프트웨어 엔지니어링적인 문제가 훨씬 적다.
  • 객체지향의 정신적인 모델 역할.
  • 스레드와 락을 이용한 소소한 동시성 지원. 하지만 하드웨어 중립적 메모리 모델 때문에 멀티코어 프로세서에서 문제가 발생.
  • JVM 바이트 코드로 컴파일하는 특징(OS 독립적, 모든 브라우저에서 가상 머신 코드를 지원) 때문에 인터넷 애플리케이션의 주요 언어가 되었다.

하지만 프로그래밍 언어는 빅데이터라는 큰 변화의 바람이 불게 되었다. 빅데이터는 테라바이트 이상의 데이터셋을 의미하며, 이 빅데이터를 멀티코어 컴퓨터컴퓨팅 클러스터를 이용해 효과적으로 처리할 필요성이 커졌다. 즉, 병렬 프로세싱을 활용해야 하는 데 지금까지의 자바로는 충분히 대응할 수 없었다.

자바 8은 더 다양한 프로그래밍 도구 그리고 다양한 프로그래밍 문제를 더 빠르고 정확하며 쉽게 유지보수할 수 있다는 장점을 제공한다. 시장에서 요구하는 기능을 제공하기 위해 크게 세 가지 개념을 모태로 자바 8은 유용한 기능들을 제공한다. 세 가지 개념을 차례대로 살펴본다.

병렬 프로세싱이 중요한 자바의 발전 원동력 중 하나이지만, 큰 시스템의 설계 방식도 자바의 발전 원동력 중 하나이다. 최근 외부에서 큰 하위시스템 컴포넌트를 추가하고 다른 벤더가 만든 컴포넌트를 이용해 개발하는 사례가 늘고 있다. 자바 8, 9에서는 이런 설계 스타일에 적응할 수 있도록 디폴트 메서드와 모듈을 제공한다.

1.2.2 스트림 처리 (stream processing)

자바 8의 첫 번째 프로그래밍 개념은 스트림 처리다.

  • 스트림(stream): 한 번에 한 개씩 만들어지는 연속적인 데이터 항목들의 모임

    • 프로그램은 입력 스트림에서 데이터를 한개씩 읽어 들이고, 출력 스트림으로 데이터를 한 개씩 기록한다.
    • 프로그램의 출력 스트림은 다른 프로그램의 입력 스트림이 될 수 있다.

기존의 자바에서는 표준 입력 System.in에서 데이터를 읽고 처리하여 표준 출력 System.out으로 기록한다.

스트림을 자동차의 생산 공장 라인에 비유할 수 있다. 각 작업장에서는 자동차를 받아 작업을 수행하고 다음 작업장에 결과물을 보낸다. 각 조립 라인은 물리적인 순서로 한 개씩 운반하지만 각각의 작업장에서는 동시에 작업을 처리하고 있다.

자바 8은 java.util.stream 패키지에 스트림 API가 추가되었다.

  • Stream<T>: T(type) 형식으로 구성된 일련의 항목

스트림 API는 가볍게 어떤 항목을 연속으로 제공하는 어떤 기능이라고 생각하면 된다. 작업을 진행하는 파이프라인을 만드는 데 필요한 많은 메서드를 제공한다. 기존의 자바는 한 번에 한 항목을 처리했지만, 스트림 API는 작업을 고수준으로 추상화해서 일련의 스트림으로 만들어 처리할 수 있다. 또 스트림 파이프라인을 이용해서 입력 부분을 여러 CPU 코어에 쉽게 할당해 병렬성을 얻을 수 있다.

1.2.3 동작 파라미터화로 메서드에 코드 전달하기

자바 8의 두 번째 프로그래밍 개념은 코드 일부를 API로 전달하는 기능이다.

  • 동작 파라미터화(behavior parameterization): 메서드를 다른 메서드의 인수로 넘겨주는 기능

메서드에 파라미터를 제공하면 더 다양한 기능을 구현할 수 있다. sort 메서드가 기본으로 오름차순 정렬만 제공하지만, 파라미터를 추가한다면 역순 정렬 등 다양한 정렬을 수행할 수 있을 것이다.

예를 들어, 2013UK0001, 2014US0002, 2014KR0003, … 등의 형식을 갖는 송장ID가 있다고 가정한다. 처음 4개의 숫자는 연도, 다음 2 글자는 국가 코드, 마지막 4개의 숫자는 고객ID를 의미한다. 원활한 송장 시스템 구축을 위해 고객 ID 또는 국가 코드순으로 정렬해야 한다. 이를 위해 sort 메서드를 이용하려면 기준에 맞게 정렬하도록 따로 코드를 제공해야 한다.

고객 ID로 정렬하기 위해 compareUsingCustomerId()를 구현해 제공할 수 있다. 하지만 자바 8이전에서는 메서드를 다른 메서드로 전달할 방법이 없었다. 자바 8에서는 메서드를 다른 메서드의 인수로 넘겨주는 기능을 제공한다. 이런 기능을 이론적으로 동작 파라미터화라고 부른다.

스트림 API는 연산의 동작을 파라미터화할 수 있는 코드를 전달한다는 사상에 기초하기 때문에 동작 파라미터화는 매우 중요한 기능이다.

1.2.4 병렬성과 공유 가변 데이터

자바 8의 세 번째 프로그래밍 개념은 병렬성을 공짜로 얻을 수 있다는 개념이다. 세상에 공짜가 없듯이 병렬성을 얻는 대신 스트림 메서드로 전달하는 코드의 동작 방식이 기존의 코드와 다르다는 점이다.

스트림 메서드로 전달하는 코드는 다른 코드와 동시에 실행하더라도 안전하게 실행될 수 있어야 한다.

  • 스트림 메서드는 공유된 가변 데이터(shared mutable data)에 접근하지 않아야 한다.

    • 이러한 함수를 순수 함수, 부작용 없는 함수, 상태 없는 함수라 부른다.
    • 공유된 변수나 객체가 존재하면 병렬성에 문제가 발생한다.

자바 8이전에서는 synchronized를 이용해 공유된 가변 데이터를 보호하는 규칙을 만들 수 있지만 시스템 성능에 악영향을 미친다. 스트림을 이용하면 기존의 자바 스레드 API보다 쉽게 병렬성을 활용할 수 있다. 다중 프로세싱 코어에서 synchronized를 사용하면 생각보다 훨씬 더 비싼 대가(시스템 성능에 악영향)를 치러야 한다.

공유되지 않은 가변 데이터, 메서드, 함수 코드를 다른 메서드로 전달하는 기능은 함수형 프로그래밍 패러다임의 핵심적인 사항이다.

1.2.5 자바가 진화해야하는 이유

자바의 변화는 컴파일을 할 때 더 많은 에러를 검출할 수 있고, 리스트의 유형을 알 수 있어 가독성이 좋아졌다는점 등 많은 편리함을 가져다 주었다. 자바 8의 가장 큰 변화는 고전적인 객체지향에서 벗어나 함수형 프로그래밍으로 다가섰다는 점이다. 이로써 객체지향 패러다임과 함수형 패러다임의 장점을 모두 활용할 수 있게 되어, 어떤 문제를 더 효율적으로 해결할 수 있는 다양한 도구를 제공하게 된다.

프로그래밍 언어는 하드웨어나 프로그래머 기대의 변화에 부응하는 방향으로 변환해야 한다. 멀티코어 CPU가 대중화 되고, 프로그래머는 좀 더 효율적인 병렬성을 원하기 때문에 언어는 계속해서 발전해 나가야 한다.


1.3 자바 함수

  • 자바의 함수: 수학적인 함수처럼 사용되며 부작용을 일으키지 않는 함수를 의미한다.

함수란 메서드(method) 특히 정적 메서드(static method)와 같은 의미로 사용된다. 자바 8은 함수를 새로운 값의 형식으로 추가했다. 이는 스트림과 연계될 수 있도록 함수를 만들었기 때문이다.

자바 프로그램에서 조작할 수 있는 값은 기본값, 객체(객체의 참조)의 값이 있다. 객체는 new 또는 팩토리 메서드 또는 라이브러리 함수를 이용해 값을 얻을 수 있다. 프로그래밍 언어의 핵심은 값을 바꾸는 것이다. 이 값을 일급값(first-class)이라고 부르고, 다양한 구조체(메서드, 클래스)는 값의 구조를 표현하는 데 도움을 준다. 구조체는 자유롭게 전달할 수 없기 때문이 이급값(second-class)이라 부른다.

  • 일급값(일급 시민)의 세 가지 조건
    • 변수(variable)에 담을 수 있어야 한다
    • 인자(parameter)로 전달할 수 있어야 한다.
    • 반환값(return value)으로 전달할 수 있어야 한다.

인스턴스화한 결과가 값으로 귀결되는 클래스를 정의할 때 메서드를 아주 유용하게 활용할 수 있지만 기존의 자바는 메서드와 클래스는 그 자체로 값이 될 수 없다. 그래서 자바 8은 이급시민인 메서드를 일급 시민으로 만드는 방법을 추가하게 된다.

1.3.1 메서드와 람다를 일급 시민으로

자바 8은 메서드를 값으로 취급할 수 있게 만들었다. 메서드를 값으로 취급할 수 있는 기능은 스트림 같은 다른 자바 8의 기능의 토대를 제공한다.

  • 메서드 참조(method reference): :: 를 이용해 해당 메서드를 값으로 사용하라는 의미
    • 메서드를 값으로 즉, 일급값으로 사용할 수 있다.
File[] hiddenFiles = new File(".").listFiles(new java.io.FileFilter() {
    public boolean accept(File file) {
        return file.isHidden();
    }
});

위 예제는 File클래스에 이미 isHidden()이 존재하는 데 굳이 FileFilter로 메서드를 복잡하게 감싼 다음에 인스턴스화를 진행했다. 자바 8에서는 단 한줄로 위 코드를 작성할 수 있다.

File[] hiddenFiles = new File(".").listFiles(File::isHidden);
// File::isHidden -> File클래스에 isHidden()이 존재, 해당 메서드를 값으로 사용해라

기존의 자바는 new객체 참조를 생성해 객체를 주고받았지만, 자바 8에서는 메서드 참조를 만들어 전달할 수 있게 되었다.

  • 람다(lambda, 익명 함수(anonymous functions)): 메서드 참조를 위해 클래스를 만든 다음에 클래스 내부에 메서드를 정의해서 사용하면 ‘왜 굳이 이렇게 해야하나?’ 라고 생각할 수 있다. 자바에서 제공하는 클래스의 메서드는 참조하기 편하지만 사용자 정의 메서드는 불편함을 느낄 수 있다. 그래서 람다 문법을 이용하면 클래스를 만들어 메서드를 정의하는 것 보다 더 간결하게 코드를 구현할 수 있다.

1.3.2 예제: 코드 넘겨주기

색(color), 무게(weight)를 인스턴스 멤버로 가지는 Apple 클래스가 존재한다. 이때 모든 녹색 사과를 선택해 리스트로 반환하는 메서드를 기존의 자바에서는 아래와 같이 작성한다.

public static List<Apple> filterGreenApples(List<Apple> inventory) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : inventory) {
        if ("green".equals(apple.getColor())) {
            result.add(apple);
        }
    }
    return result;
}

누군가는 일정 무게를 지정해 기준값을 넘는 사과의 리스트를 얻고싶어할 수 있다.

public static List<Apple> filterHeavyApples(List<Apple> inventory) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : inventory) {
        if (apple.getWeight() > 150) {
            result.add(apple);
        }
    }
    return result;
}

두 메서드는 복사&붙여넣기 후 for문 안의 조건식만 다르게 설정하면 작성할 수 있다. 만약 복사&붙여넣기한 코드에 문제가 있다면 붙여넣기한 모든 코드를 수정해야한다는 위험을 안고 있다.

자바 8에서는 코드를 인수로 넘겨줄 수 있으므로 filter 메서드를 중복으로 구현할 필요가 없다.

public static boolean isGreenApple(Apple apple) {
    return "green".equals(apple.getColor());
}

public static boolean isHeavyApple(Apple apple) {
    return apple.getWeight() > 150;
}

public 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;
}

---
filterApples(inventory, Apple::isGreenApple);
filterApples(inventory, Apple::isHeavyApple);

만약 사과가 재배된 국가를 인스턴스변수로 추가해 특정 국가의 사과만 얻고 싶다면 적은양의 코드로 기능을 추가할 수 있다. 이게 자바 8의 강점이다.

public static boolean isKRApple(Apple apple) {
    return "KR".equals(apple.getCountry());
}

filterApples(inventory, Apple::isKRApple);

Predicate: 수학에서 인수로 값을 받아 true or false를 반환하는 함수.

1.3.3 메서드 전달에서 람다로

기존 자바의 코드보다 확연히 간단해졌지만, 람다(익명 함수)를 이용하면 더 간결한 코드를 작성할 수 있다. 한두 번만 사용할 메서드를 매번 정의하는 것은 귀찮은 일이다.

filterApples(inventory, (Apple a) -> "green".equals(a.getColor()) );
filterApples(inventory, (Apple a) -> a.getWeight() > 150);
filterApples(inventory, (Apple a) -> "KR".equals(a.getCountry()) );

한 번만 사용할 메서드는 따로 정의를 구현할 필요가 없다. 하지만 람다가 길어지면(구현이 복잡하면) 메서드를 정의하고 메서드 참조를 활용하는 것이 바람직하다. 코드의 명확성, 즉 코드는 다른 사람도 읽기 편해야한다.

자바 8은 스트림 API를 통해 filter와 비슷한 동작을 수행하는 연산집합을 제공한다. 컬렉션과 스트림 간 변환할 수 있는 메서드도 제공하니 매우 유용하게 활용가능할 것이다.


1.4 스트림

거의 모든 자바 애플리케이션은 컬레션을 만들고 활용한다. 컬렉션으로는 많은 문제를 해결할 수 있지만 복잡한 동작을 해야하는 기능은 중첩된 제어 흐름 문장이 많아서 코드를 읽고 이해하기가 힘들어질 수 있다.

자바 8에서 제공하는 스트림 API를 사용한다면 복잡한 컬렉션을 사용한 메서드를 간략화할 수 있다.

스트림 API와 컬렉션 API는 상당히 다른 방식으로 데이터를 처리한다.

  • 반복 과정
    • 컬렉션: for-each 루프를 이용해 각 요소를 직접 반복하며 작업을 수행, 외부 반복(external iteration)
    • 스트림: 라이브러리 내부에서 모든 데이터가 처리, 내부 반복(internal iteration)
  • 큰 데이터 처리
    • 컬렉션: 단일 스레드로 처리
    • 컬렉션: 병렬 스레드로 처리

1.4.1 멀티스레딩은 어렵다

스레드 API로 멀티스레딩 코드를 구현해 병렬성을 이용하는 것은 어렵다. 스레드를 잘 제어하지 못하면 원치 않는 값이 나올 수 있기 때문이다.

스트림 API컬렉션을 처리하면서 발생하는 모호함과 반복적인 코드 문제멀티코어 활용 어려움 두 문제를 모두 해결했다.

  • 반복적인 코드 문제: 라이브러리에서 반복되는 패턴을 제공

    • 주어진 조건에 데이터를 필터링, 데이터를 추출, 데이터를 그룹화하는 등의 기능을 제공
  • 멀티코어 활용 문제: 포킹 단계를 통해 해결

    • 두 CPU를 가진 컴퓨터에서 리스트에 담긴 데이터를 필터링 하는 포킹 단계

      1. 포크: 리스트를 두 부분으로 쪼개 두 CPU에 할당

      2. 필터: 각 CPU에 담긴 데이터를 필터링

      3. 결과 합침: 하나의 CPU가 필터링한 결과를 합친다.

  • 스트림 API: 데이터에 어떤 계산을 할 것인지 묘사하는 것에 중점

    • 스트림 내 요소를 쉽게 병렬로 처리할 수 있는 환경을 제공한다는 것이 핵심
  • 컬렉션 API: 어떻게 데이터를 저장하고 접근할지에 중점

컬렉션을 가장 빨리 필터링할 수 있는 방법은 스트림으로 바꾸고, 병렬 처리 후 컬렉션으로 다시 복원하는 것이다.

import static java.util.stream.Collectors.toList;

List<Apple> heavyApples =
    inventory.parallelStream().filter((Apple a) -> a.getWeight() > 150)
        .collect(toList());

자바 8은 병렬성을 제공하기 위해 두 가지 마법을 제공한다.

  1. 라이브러리에서 분할을 처리한다. 즉, 큰 스트림을 작은 스트림으로 분할해 병렬로 처리할 수 있게 한다.
  2. 전달된 메서드가 상호작용을 하지 않는다면 가변 공유 객체를 통해 공짜로 병렬성을 누릴 수 있다.


1.5 디폴트 메서드와 자바 모듈

요즘 자바는 외부에서 만들어진 컴포넌트를 이용해 시스템을 구축하는 경향을 보인다. 지금까지 자바에서는 특별한 구조가 아닌 평범한 자바 패키지 지합을 포함한 JAR파일을 제공하는 것이 전부였다.

자바 9의 모듈 시스템은 모듈을 정의하는 문법을 제공해 이를 이용해 패키지 모음을 포함하는 모듈을 정의할 수 있다. 모듈 덕분에 JAR같은 컴포넌트에 구조를 적용할 수 있고, 문서화와 모듈 확인 작업이 쉬워졌다.

기존 자바에서 인터페이스를 업데이트하려면 해당 인터페이스를 구현하는 모든 클래스도 업데이트 해야하므로 인터페이스의 업데이트는 불가능에 가까웠다. 자바 8은 디폴트 메서드(default method)로 인터페이스를 쉽게 바꿀 수 있도록 지원한다. 디폴트 메서드는 특정 프로그램 구현의 도움을 주는 기능이 아니라 미래에 쉽게 변화할 수 있는 환경을 제공하는 기능이다.

List<Apple> heavyApples =
    inventory.parallelStream().filter((Apple a) -> a.getWeight() > 150)
        .collect(toList());

자바 8이전에는 List<T>에서 parallelStream()을 지원하지 않기 때문에 컴파일이 불가능하다. 이를 해결하는 방법은 인터페이스를 만들어 Collection 인터페이스에 parallelStream()을 추가하고 ArrayList 클래스에서 메서드를 구현하는 것이다. 인터페이스에 새로운 메서드를 추가할 때 마다 인터페이스를 구현하는 모든 클래에 새로 추가된 메서드를 구현해야하므로 너무 큰 비용이 들어가게 된다.

자바 8은 구현 클래스에서 구현하지 않아도 되는 메서드를 인터페이스에 추가할수 있는 디폴트 메서드 기능을 제공한다. 이 기능을 이용하면 기존의 코드를 건드리지 않고도 원래의 인터페이스 설계를 자유롭게 확장할 수 있다.

default void sort(Comparator<? super E> c) {
    Collections.sort(this, c);
}


1.6 함수형 프로그래밍에서 가져온 다른 유용한 아이디어

지금까지 살펴본 자바에 포함된 함수형 프로그래밍의 핵심적인 아이디어는 두 가지이다.

  1. 메서드와 람다를 일급값으로 사용
  2. 가변 공유 상태가 없는 병렬 실행을 이용해서 효율적이고 안전하게 함수나 메서드를 호출

스트림 API는 위 두가지 아이디어를 모두 활용한다.

함수형 언어의 유용한 기능 중 하나는 명시적으로 서술형 데이터 형식을 이용해 null을 회피하는 기법이 있다.

  • 자바 8에서는 NullPointerException을 피할 수 있도록 Optional<T> 클래스를 제공한다.
    • Optional<T> 클래스: 값을 갖거나 갖지 않을 수 있는 컨테이너 객체.
      • 값이 없는 상황을 어떻게 처리할지 명시적으로 구현하는 메서드를 포함. 즉, 형식 시스템을 이용해 어떤 변수에 값이 없을 때 어떻게 처리할지 명시할 수 있다.
  • 자바의 if-then-else 보다 패턴 매칭 기법이 더 정확한 비교를 구현할 수 있다.
    • 자바 8은 패턴 매칭을 완벽하게 지원하지 않는다. 왜 자바의 switch문은 문자열과 기본값만 이용할까?
      • 객체지향 설계에서 클래스 패밀리를 방문할 때 방문자 패턴(visitor pattern)을 이용해 각 객체를 방문한 다음 원하는 작업을 수행한다.
    • 패턴 매칭은 switch를 확장한 것으로 데이터 형식 분류와 분석을 한 번에 수행할 수 있다.


1.7 Summary

  • 자바 8은 프로그램을 더 효과적이고 간결하게 구현할 수 있는 새로운 개념과 기능을 제공한다.
  • 함수는 일급값이다. 메서드를 어떻게 함수형값으로 넘겨주는지, 람다(익명 함수)를 어떻게 구현하는지 기억하자.
  • 스트림과 컬렉션을 적절하게 활용하면 스트림의 인수를 병렬로 처리할 수 있으며 더 가독성 좋은 코드를 구현할 수 있다.
  • 대규모 컴포넌트 기반 프로그래밍 그리고 진화하는 시스템의 인터페이스를 적절하게 대응하기 위해 자바 8, 9에서는 모듈 시스템과 디폴트 메소드를 이용해 기존 인터페이스를 구현하는 클래스를 바꾸지 않고도 인터페이스를 변경할 수 있도록 한다.

댓글남기기