1장: 자바의 역사
자바 8,9,10,11: 무슨 일이 일어나고 있는가?
자바 역사의 흐름
자바 8에서 가장 큰 변화가 나타났다.
함수형 프로그래밍 지원 (스트림 API, 람다)
인터페이스의 디폴트 메서드
병렬 실행 기법
기존에는 저수준 기능만을 제공하여 스레드를 사용하여 병렬 실행 환경을 관리하는 것이 어려웠다.
자바 5에서는 스레드 풀, concurrent collection을, 자바 7에서는 fork/join 프레임워크를 제공했지만 직접 활용하기엔 쉽지 않았다.
자바 8의 경우 병렬 실행을 새롭고 단순한 방식으로 접근할 수 있도록 제공한다.
자바 9에서는 리액티브 프로그래밍을 지원하며 RxJava를 표준 방식으로 제공한다.
프로그래밍 언어 생태계
C, C++은 프로그래밍 안전성이 낮아 프로그램이 예기치 않게 종료되거나 보안 약점이 존재할 수 있지만 작은 런타임 풋프린트로 운영체제 및 다양한 임베디드 시스템에서 여전히 많이 사용되고 있다.
런타임 풋프린트에 여유가 있는 애플리케이션의 경우 보통 Java, C#같은 안전한 형식의 언어를 사용한다.
자바는 어떻게 성장했는가?
풍부한 라이브러리와 잘 설계된 객체 지향 언어
초기 브라우저에서 지원하는 JVM 바이트코드로 컴파일하여 안전하게 실행할 수 있었다.
Write Once Run Anywhere 으로 JVM만 있다면 어디든 코드를 실행할 수 있다.
자바가 대학교로 깊숙이 자리잡아 졸업생들이 자바를 업계에서 활용했다.
자바 8에 추가된 개념
스트림
한 번에 한 개씩 만들어지는 연속적인 데이터들의 모임
일반적으로 입력 스트림에서 데이터를 한개씩 읽어 출력 스트림으로 하나씩 기록한다.
출력 스트림은 다른 프로그램의 입력 스트림이 될 수 있다.
유닉스의
cat file1 file2 | tr "[A-Z]" "[a-z]" | sort | tail -3
명령어는 두 파일을 연결하고, 단어를 소문자로 바꾼 후, 단어를 사전순으로 정렬해, 가장 마지막에 위치한 세 단어를 출력한다.유닉스에서는 위 작업들을 병렬로 실행하며, 이 때
sort
는cat
이나tr
연산이 완료되지 않은 시점에서 행을 처리하기 시작할 수 있다.쉽게 말하면 모든 행이 준비된 후에 한번에 sorting 하는 게 아니라, 하나씩 데이터를 추가하며 sorting 해주게 된다.
데이터가 하나씩 컨베이어 벨트처럼 쭉 이동하고 있으므로, 모든 작업장에서 병렬적으로 연산이 되고 있다고 이해하면 된다.
동작 파라미터화
메서드를 다른 메서드의 인수로 넘겨주는 기능을 제공한다.
즉, 동작(메서드)을 파라미터화 해서 전달 가능하다.
함수형 프로그래밍 기술을 응용해 동작 파라미터를 활용할 수 있다.
병렬성과 공유 가변 데이터
병렬성을 위해 스트림 메서드로 전달하는 코드는 공유된 가변 데이터에 접근하지 않아야 한다.
여러 메서드를 실행하며 동시에 공유된 가변 데이터를 변경하는 불상사가 발생하면 안된다.
즉, pure, side-effect-free, stateless 함수여야 한다.
synchronized를 사용하면 코드가 순차적으로 실행되도록 하여 병렬을 무력화시켜 성능에 악영향을 미친다.
자바 함수
자바 8에서 함수는 새로운 값의 형식으로 추가되었다.
병렬 프로그래밍을 활용할 수 있는 스트림과 연계될 수 있도록 하기 위함이다.
함수를 값으로 사용하면 더 쉽게 프로그램을 구현할 수 있다는 것이 스칼라와 그루비 등으로 증명되었다.
아래에서 다루는 내용들을 통해 자세하게 장점을 확인해보자!
메서드 참조
기존에는 메서드를 다른 메서드의 인자로 넣으려면 익명 클래스의 메서드를 구현해야만 했다.
다음과 같이 익명 클래스를 사용하면 각 행이 무슨 작업을 하는지 한눈에 파악하기 어렵다.
자바 8의 메서드 참조 기능을 사용해 아래와 같이 바로 특정 메서드를 다른 메서드의 인자로 넣게 되면, "현재 디렉토리에서 모든 숨김 파일을 찾아 결과를 반환한다"라는 의미를 쉽게 이해할 수 있다.
람다
익명 함수라고도 부른다.
클래스가 없어도 메서드를 정의할 수 있어 간결하게 코드를 구현할 수 있다.
리스트에 특정 함수를 기준으로 필터링된 객체들만 추가하는 메서드를 만드는 요구사항을 생각해보자.
Apple의 색깔이 초록색인 것만 리스트로 모으는 메서드와 무게가 150g 이상인 것만 리스트로 모으는 메서드가 필요하다고 한다면 아래와 같이 Predicate 인터페이스의 구현체를 인자로 받도록 하여 중복을 줄일 수 있다.
Predicate 인터페이스: 입력 인자를 받아 true나 false를 반환하는 함수형 인터페이스이다. 즉 어떠한 입력 인자를 받아 boolean을 반환하도록 만든 메서드는 이 인터페이스의 구현체에 해당하게 된다.
다음과 같이 메서드를 인자로 넣어 실행시킬 수 있다.
간결한 로직이며 한번만 사용된다면, 따로 메서드를 구현할 필요 없이 람다를 넣어 바로 전달해줄 수 있다.
물론 조금 복잡한 동작을 수행하려 한다면 메서드를 정의해 참조하도록 하는 것이 가독성이 좋다.
병렬성을 위해 단순히 filter 등과 같은 몇몇 일반적인 라이브러리 메서드를 추가하는 대신 자바 8에서는 filter와 비슷한 동작을 수행하는 연산집합을 포함하는 스트림 API를 제공한다.
스트림
컬렉션을 반복하면서 특정 조건을 만족하는 것을 찾거나, 특정 데이터만 추출해내거나 데이터를 그룹화하는 등의 여러 작업을 진행하는 것은 가독성이 좋지 않으며 멀티스레딩으로 구현하기 어렵다.
이를 쉽게 이해할 수 있도록 스트림은 여러 API를 제공한다.
기존에는 컬렉션을 활용할 때 for-each 루프를 이용하여 각 요소를 반복하면서 작업을 수행했다. (외부 반복)
스트림 API를 이용하면 컬렉션을 스트림으로 바꾸고 라이브러리 내부에서 모든 데이터를 처리할 수 있다.(내부 반복)
스트림의 병렬 API를 사용하면 서로 다른 CPU 코어에 각 스레드를 할당해 처리 시간을 줄일 수 있다.
디폴트 메서드와 자바 모듈
디폴트 메서드를 제공하여 기존 인터페이스를 구현하는 클래스를 모두 바꾸지 않고도 쉽게 인터페이스를 변경할 수 있다.
디폴트 메서드는 인터페이스의 구현체에서 반드시 Override하지 않아도 된다.
여러 인터페이스 구현 시 다중 디폴트 메서드가 존재할 때 어떻게 처리하는지는 9장에서 다룬다.
자바 9부터 모듈 시스템을 제공하여 JAR같은 컴포넌트에 구조를 적용할 수 있다.
Optional<T>를 제공해 NullPointerException을 피할 수 있다.
케이스로 정의하는 함수형 프로그래밍의 기능인 패턴 매칭 기법을 지원한다.
데이터 형식 분류와 분석을 한 번에 수행할 수 있다.
Last updated