#01장 자바 8, 9, 10 그리고 11에는 무슨일이 일어나고 있는가?

Raoul-Gabriel Urma 저, 우정은 역「Modern Java in Action, 2019」를 읽고 정리하였습니다.

1.1 주목할 만한 이야기

자바 8은 역사상 가장 큰 변화가 있었다. 자바9(리액티브 프로그래밍인 병렬 실행기법 지원. RxJava)는 획기적이거나 생산성이 바뀔정도는 아니었고 자바 10에서는 type inference 관련해 약간의 변화가 있었다.

8버전 이전의 코드와 비교를 해보자.

//자바 8 전
Collections.sort(inventory, new Comparator<Apple>() {
    public int compare(Apple a1, Apple a2){
        return a1.getWeight().compareTo(a2.getWeight());
    }
});

//자바 8
// 다른 메서드에 메서드를 넘기는 것이 가능해짐. 
inventory.sort(comparing(Apple::getWeight)

자바 8 이전에는, 코어들을 사용하기 위해 쓰레드를 잘 활용하라고 하는데, 실상 쓰레드를 다루는 일은 error-prone하고 어렵다. 자바 1.0에서도 쓰레드와 락이 있었고, 심지어 메모리 모델도 있었다. 그러나 비전문가들이 이 기능들을 사용하는데는 앞서말한 문제들이 있었다.

자바 5에서는 산업적으로 사용할 목적으로 쓰레드풀이나 동시적인 컬렉션들과 같은 블록들을 추가했었고, 자바 7에서는 fork/join 프레임워크와 병렬성을 좀더 실용적으로 만들었다. 하지만 여기까지도 사용하기 어려웠다. 자바 8에서 부터 실질적으로 사용하기 쉬운 병렬성이 나왔다.

  • 자바 8의 중요한 추가사항들

    • The Streams API

      • 스트림을 사용함으로써, synchronized를 사용하지 않아도 된다.

    • Techniques for passing code to methods

      • behavior parameterization을 구현할 수 있고, 떠오르는 컴퓨터 아키텍처로인 함수형 프로그래밍에서 위력을 발휘한다.

    • Default methods in interface

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

수백개의 프로그래밍 언어가 있고 살아남기 위해서는 진화가 필요하다. C나 C++의 경우는 오래됐고 프로그래밍 안정성이 부족하지만 runtime footprint가 작다는 강점이 있어 OS를 개발하거나 임베디드 시스템에서 아직도 많이 사용되고 있다. 그리고 Algol, COBOL, Pascal 같은 진화 하지 못한 언어들은 오늘날 살아남지 못하였다. 자바가 익숙하지 않았던 테라바이트 단위의 빅데이터를 처리하기 위해 새로운 개념 도입도 필요하게 되었다.

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

자바는 캡슐화와 write-once and run-anywhere와 같은 객체지향 모델 덕분에 입지를 발전해왔다.

참고, 오늘날 서브시스템을 담는 큰시스템 디자인이 주로 사용되는데, 디폴트 메소드와 모듈들을 도입해서 해결하였다.

1.2.2 스트림 프로세싱

  • 스트림은 한 번에 하나씩 생성되는 일련의 데이터 항목이다.

  • 자세한 설명은 4~7장에서 설명한다.

유닉스를 예로 들어보자.

1.2.3 동작 파라미터화(Behavior Parameterization)를 이용해 메서드에 코드 보내기

  • 자바 8 이전에는 메서드를 다른 메서드로 전달할 방법이 없었다.

  • 2, 3장에서 자세히 다룬다.

참고, 오늘날 서브시스템을 담는 큰시스템 디자인이 주로 사용되는데, 디폴트메소드와 모듈들을 도입해서 해결하였다.

1.2.4 병렬성(Parallelism)과 공유 가변 데이터 (Shared Mutable Data)

  • 안전하게 동시에 코드를 실행하기 위해서는 서로 공유된 데이터에 접근하지 않아야 한다.

  • 공유된 변수나 객체가 있으면 병렬성에 문제가 발생한다.

  • 접근할 수 없게 만든 함수를 pure, side-effect-free, stateless 함수라고 부른다.

  • 18, 19장에서 자세히 다룬다.

1.2.5 자바 진화의 필요성

  • 자바가 진화하면서...

  • 제너릭이 갑자기 나왔지만, 컴파일을 할때 더많은 에러를 검출할 수 있는 편리함을 주었다.

  • 틀에 박힌 iterator 대신 for-each 루프를 사용할 수 있게 되었다.

  • 버전 8부터 고전적인 객체지향의 틀에서 세어나와 함수형 프로그래밍을 도입하면서 객체지향과 함수형 프로그래밍의 두가지 장점을 모두 활용할 수 있게 되었다.

  • 요약, 하드웨어나 프로그래머 기대의 변화에 부응하는 방향으로 변화하였다.

1.3 자바에서의 함수

  • 프로그래밍에서 함수는 메서드 특히 static 메서드와 같은 의미로 사용된다.

  • 함수를 값처럼 취급한다는 장점이 있다.

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

  • 메서드를 값으로 취급할 수 있는 기능은, 스트림 같은 다른 자바 8기능의 토대를 제공했다.

  • 8버전에서 메서드가 이급값이 아닌 일급 값이 되었다.

메서드 참조가 무엇인지 코드를 보자.

1.3.2 코드 전달 예제

1.3.3 메서드 전달부터 람다까지

  • 한두번만 사용할 메서드를 매번 정의하는 것은 귀찮은 일이다.

  • 람다를 사용하면 더 간결해진다.

  • 복잡한 수행시 람다 사용보다는 메서드를 정의하고 사용한다.

1.4 스트림

  • 스트림 API를 사용하면 컬렉션 API와는 상당히 다른 방식으로 데이터를 처리할 수 있다.

  • 스트림 API에서는 라이브러리 내부에서 모든 데이터가 처리된다. (internal iteration)

1.4.1 멀티쓰레딩의 어려움

  • 멀티 스레딩 환경에서 각각의 스레드는 동시에 공유된 데이터에 접근하고 데이터를 갱신할 수 있었다. 이로써 스레드를 잘 제어하지 못하면 데이터 값이 이상한 방향으로 바뀔 수 있는 상황이 생긴다.

  • 스트림API를 사용하면서 반복적인 코드문제와 어려운 멀티코어 활용 문제를 해결하였다.

  • Forking Step - 두 CPU각 각각 앞뒤를 분담해 처리한다.

  • 컬렉션은 어떻게 데이터를 저장하고 접근할지에 중점, 스트림은 데이터에 어떤 계산을 할것인지 묘사하는것에 중점을 둔다.

  • 컬렉션을 가장 빠르게 처리하는 방법 - 컬렉션을을 스트림으로 바꾸고 병렬로 처리한 다음, 리스트로 다시 복원한다.

  • 7장에서 병렬 데이터 처리와 성능 자세히 다룬다.

1.5 디폴트 메서드와(Default methods) 자바 모듈

  • 인터페이스 변경시, 인터페이스를 구현하는 모든 클래스의 구현을 바꿔야 했다.

  • 이 문제를 디폴트 메서드가 해결해준다.

  • 미래에 프로그램이 쉽게 변화할 수 잇는 환경을 제공하는 기능이라 생각하면 된다.

여러 인터페이스에 다중 디폴트 메서드가 존재할 수 있다는 것은 다중상속이 허용된다는 얘기인가? 어느정도 그렇다. 이 얘기는 9장에서 다이아몬드 상속 문제에서 다룬다.

1.6 함수형 프로그래밍으로 부터온 좋은 생각들

  • 널포인터 예외를 피할 수 있다. Optional <T>

  • 19장에서 패턴매칭 자세히 다룬다.

정리

  • 자바 8의 핵심적인 추가는 흥미로운 새로운 컨셉들과 기능을 간결하고 효율적으로 작성하게 한다.

  • 8버전 이전에는 멀티코어 프로세서는 지원이 되지 않았다.

  • 함수는 일급 시민이다; 메서드가 어떻게 함수형 값으로 전달이 되었었고, 익명 함수(람다)가 어떻게 작성되었는지 기억하자.

  • 자바 8에서 스트림은 컬렉션의 많은 면모를 일반화하였다. 일밚화하였지만, 스트림은 더 읽기 쉬운 코드를 제공하고 스트림의 요소들이 병렬로 처리되게 해준다.

  • 자바는, 큰 컴포넌트 기반의 프로그래밍이나 발전하는 시스템의 인터페이스를 잘 다루지 못하였다. 그러나 지금은 자바9에서 시스템을 구조화하기 위해 모듈을 명시할 수 있고, 모든 구현부 클래스들의 수정없이 인터페이스만 디폴트 메서드를 추가함 으로써 손쉽게변경이 가능하게 되었다.

  • 함수형 프로그래밍으로 부터 온 개념들이 널과 패턴매칭을 다룰 수 있게 도와주었다.

Last updated