모던 자바 인 액션 1장 학습하고 정리한 내용입니다.
자바 8은 다음의 목표를 달성하기 위해 등장했다.
1. 간결한 코드
2. 멀티코어 프로세서의 쉬운 활용
자바 1.0은 Thread, Lock, Memory Model을 지원 -> 저수준 기능을 온전히 사용하기란 어렵다.
자바5는 Thread pool, Concurrent Collection(병렬 실행 컬렉션) 등의 강력한 도구를 제공
자바7은 병렬 실행에 도움을 줄 수 있는 fork/join 프레임워크를 제공함 -> 여전히 활용 어려움
자바 8은 병렬 실행 환경을 쉽게 관리하고 에러가 덜 발생하는 방향으로 진화했다.
자바 9는 리액티브 프로그래밍
이라는 병렬 실행 기법을 지원한다. (RxJava를 표준 방식으로써 지원)
자바8은 위의 목표를 다음과 같은 기능으로 달성했다.
1. 스트림 API
스트림 라이브러리
가 최적의 저수준 실행 방법을 선택하는 방식으로 동작synchronized
키워드를 사용하지 않아도 된다. (context switching 비용 절감)2. 메서드에 코드를 전달하는 기법
3. 인터페이스의 디폴트 메서드
과연 자바는 어떻게 변화하여 지금까지 살아남았는가?
자바는 출발이 좋았다.
1. 처음부터 많은 유용한 라이브러리를 포함하는, 잘 설계된 OOP언어로 시작함
- 캡슐화 -> C에 비해 SW Engineering 문제가 훨씬 적다
- Object Oriented -> WIMP 프로그래밍 모델에 쉽게 대응 가능
- WIMP: 웹 서버를 운영하는 데 자주 사용되는 소프트웨어 모음
2. 처음부터 Thread, Lock을 이용한 Concurrency를 지원함
3. '소스 코드를 JVM 바이트 코드로 컴파일하는 특징' 덕분에 과거의 인터넷 환경에 적합했다. (애플릿)
4. 임베디드 컴퓨팅 분야를 장악
- HW 성능 개선 -> C/C++에 비교되는 한계 극복
- 실행 시간(성능) 중요성 < 개발 시간 중요성
그런데 시간이 지나면서, 프로그래머에게 빅데이터 처리라는 새로운 과제가 등장했다.
멀티코어 컴퓨터, 컴퓨팅 클러스터를 이용해서 빅데이터를 효과적으로 처리해야 하는데, 초기 버전의 자바는 이에 부적합했다.
자바 8은 기존의 자바에는 없던 완전히 새로운 개념과 기능을 제공함으로써 시장이 요구하는 기능을 제공한다.
스트림
이란 한 번에 한 개씩 만들어지는 연속적인 데이터 항목들의 모임이다.
프로그램은 입력 스트림에서 데이터를 한 개씩 읽어 들이며, 마찬가지로 출력 스트림으로 데이터를 한 개씩 기록한다.
어떤 프로그램의 출력 스트림은 다른 프로그램의 입력 스트림이 될 수 있다.
자바 8에는 java.util.stream
패키지에 스트림 API가 추가되었다.
스트림 API의 핵심은
자바 8에서는 메서드를 다른 메서드의 인수로 넘겨주는 기능을 제공한다.
이러한 기능을 이론적으로 동작 파라미터화(behavior parameterization)
라고 부른다.
스트림 API는 연산의 동작으로 '파라미터화할 수 있는 코드'를 전달한다
는 사상에 기초한다.
스트림 메서드로 전달하는 코드는 다른 코드와 동시에 실행하더라도 안전하게 실행될 수 있어야 한다.
즉, 공유된 가변 데이터에 접근해서는 안 된다. 다른 말로는 해당 코드는 순수 함수
이어야 한다.
이전에는 synchronized
키워드를 통해 concurrency problem을 해결했는데, 이는 성능에 좋지 못하다.
자바 8은 스트림을 사용해서 기존의 스레드 API보다 쉽게 병렬성을 활용할 수 있도록 한다.
Generic이 등장하여 List
가 List<String>
으로 변경되었다. 이러한 변화를 통해 컴파일 타임의 애러 검출, 가독성이라는 편리함을 누릴 수 있게 되었다.
자바 8을 통해 자바는 고전적인 객체지향에서 벗어나 함수형 프로그래밍으로 다가섰다. 즉, 자바 8을 통해 두 가지 프로그래밍 패러다임의 장점을 모두 활용할 수 있게 되었다.
언어는 HW, 프로그래머의 기대에 부응하는 방향으로 발전해야 한다. 자바도 이에 따라서 변화해야 한다.
자바의 함수는 부작용을 일으키지 않는 함수
를 의미한다.
왜 함수가 필요할까? 프로그래밍 언어의 핵심은 값을 바꾸는 행위
이다.
전통적으로 프로그래밍 언어에서 값을 일급 시민
이라고 부른다. 자바에서는 기본값, 객체가 일급 시민에 해당한다.
값 = 변경될 수 있으며, 전달될 수 있는 것 = 기본값, 객체
자바의 다양한 구조체(메서드, 클래스 등)이 값의 구조를 표현하는 데 도움을 준다.
그러나 프로그램을 실행하는 동안, 이러한 모든 구조체를 자유롭게 전달할 수는 없다.
전달할 수 없는 구조체는 이급 시민
이라 부른다.
이급 시민 = 전달될 수 없는 것 = 메서드, 클래스
자바 8에서는 함수를 새로운 값
의 형태으로 추가했다.
멀티코어에서 병렬 프로그래밍을 활용할 수 있는 스트림과 연계될 수 있도록 함수를 만들었기 때문이다.
즉, 자바 8은 메서드를 일급 시민
으로 사용할 수 있도록 했다.
자바 8은 메서드를 값으로 취급할 수 있도록 했고, 이는 스트림과 같은 다른 자바 8기능의 토대가 되었다.
자바 8의 메서드 참조 (method reference)
기능은 메서드를 일급 시민으로 사용할 수 있도록 한다.
File[] hiddenFiles = new File(".").listFiles(File::isHidden);
Object Referecne를 통해 instance를 전달하는 것 처럼, File
클래스의 isHidden
메서드에 대한 Method Reference
를 생성하여 메서드를 파라미터로 사용할 수 있도록 한다.
익명함수 (lambda)도 일급 함수로 사용될 수 있다.
(int x) -> x + 1 // 파라미터 int x에 1을 더한 값을 반환
굳이 클래스를 만들어 메서드로 사용하지 않아도 된다는 편리함이 있다.
거의 모든 자바 App은 Collection을 활용한다.
그런데 로직(fileter 등)이 복잡해질 경우, Collection을 사용한 코드는 길어지고 중복이 발생한다.
이를 스트림 API를 통해 개선할 수 있다.
for-each loop를 통해 각 요소를 반복하면서 작업을 수행하는 외부 반복(external iteration)
에 반해, 스트림 API에서는 라이브러리 내부에서 내부 반복(interanl iteration)
을 통해 모든 데이터가 처리된다.
또 다른 문제로 Collection을 사용했을 때의 성능 문제를 꼽을 수 있다.
거대한 크기의 Collection을 단일 CPU로는 처리하기 힘들다.
그렇다고 이전 자바의 쓰레드 API로 멀티스레딩 코드를 구현해서 병렬성을 이용하기도 여의치 않다.
스트림 API는 두 가지 문제를 해결했다.
1. Collection을 처리하면서 발생하는 '코드의 모호함', '반복'
2. 멀티코어 활용 어려움
스트림 API는 기존 컬랙션을 사용했을 때 반복 등장하는 패턴을 제공한다.
필터링
추출
그룹화
이러한 동작을 사용함과 동시에 쉬운 병렬화라는 장점을 누릴 수 있다.
1. 포크: 한 CPU는 리스트의 앞 부분, 한 CPU는 리스트의 뒷 부분을 처리하도록 요청
A B C | D E
2. 필터: 각 CPU는 자신이 맡은 리스트를 처리
B C | E
3. 결과 합침: 하나의 CPU가 두 결과물을 정리
B C E
컬랙션은 어떻게 데이터를 저장, 접근할지에 대해 중점을 두는 반면, 스트림은 데이터에 어떤 계산을 할 것인지 묘사하는 것에 중점을 둔다.
예전에는 외부에서 만들어진 컴포넌트를 사용하기 위해선 JAR 파일을 이용해야만 했다. 그리고 불러온 컴포넌트가 작업 중인 기존 코드와 호환되도록 인터페이스를 변경해야 했다. 이는 곳 인터페이스를 상속한 코드들의 변경을 의미한다.
List.stream()
을 사용하는 코드를, 자바 8 이전 환경에서 실행하려고 한다.Collection
인터페이스에 stream
을 메소드로 추가한다.Collection
을 구현하는 concrete class들이 전부 stream
메서드를 override해야 한다. 자바 8은 인터페이스를 쉽게 바꿀 수 있도록 디폴트 메소드를 지원한다.
다만 디폴트 메소드를 직접 구현하는 상황은 흔치 않다.
자바 8에서는 NullPointer Exception을 피할 수 있도록 Optional<T>
클래스를 제공한다.
Optional<T>
은 값을 갖지 않을 수 있는 컨테이너 객체다.
Optional<T>
은 값이 없는 상황을 어떻게 처리할지 명시적으로 구현하는 메서드를 포함한다.