✏️[모던 자바 인 액션, 전문가를 위한 자바 8, 9, 10 기법 가이드] 스터디 관련 책 내용을 정리한 글입니다.
📌 이 장의 내용
- 자바가 거듭 변화하는 이유
- 컴퓨팅 환경의 변화
- 자바에 부여되는 시대적 변화 요구
- 자바 8과 자바 9의 새로운 핵심 기능 소개
1.1 역사의 흐름은 무엇인가?
자바 역사를 통틀어 가장 큰 변화가 자바 8에서 일어났다.
자바 1.0 : 스레드와 스레드 락, 메모리 모델을 지원하지만, 저수준 기능 활용 힘듦
자바 5 : 스레드 풀, 병렬 실행 컬렉션 등의 아주 강력한 도구 도입
자바 7 : 병렬 실행에 도움을 줄 수 있는 포크 및 프레임워크를 제공했지만 활용 어려움
자바 8 : 스트림 API, 메서드 참조와 람다, 인터페이스와 디폴트 메서드 제공
1.2 왜 아직도 자바는 변화하는가?
1.2.1 프로그래밍 언어 생태계에서 자바의 위치
자바는 캡슐화 덕분에 C에 비해 소프트웨어 엔지니어링적인 문제가 훨씬 적고, 객체지향의 정신적인 모델 덕분에 '모든 것은 객체다'로 요약할 수 있습니다.
1.2.2 스트림 처리
스트림이란 한 번에 한 개씩 만들어지는 연속적인 데이터 항목들의 모임입니다.
자바 8에서는 java.util.stream 패키지에 스트림 API가 추가되었습니다. 스트림 패키지에 Stream <T>는 T형식으로 구성된 일련의 항목을 의미합니다. 우선, 스트림 API를 조립라인처럼 어떤 항목을 연속으로 제공하는 기능이라고 생각하면 됩니다. 스트림 API의 핵심은 기존에 한 번에 한 항목을 처리했던 작업을 고수준으로 추상화해서 일련의 스트림으로 만들어 처리한다는 것이다. 또한, 스트림 파이프라인을 이용해서 입력 부분을 여러 CPU 코어에 쉽게 할당할 수 있다는 부가적인 이득도 얻을 수 있습니다. 그리고 스레드라는 복잡한 작업을 사용하지 않으면서 병렬성도 얻을 수 있습니다.
1.2.3 동작 파라미터화로 메서드에 코드 전달하기
코드의 일부를 API로 전달하는 기능을 동작 파라미터화라고 부릅니다.
💡 동작 파라미터화가 왜 중요할까?
스트림 API는 연산의 동작을 파라미터 화할 수 있는 코드를 전달한다는 사상에 기초하기 때문입니다.
1.2.4 병렬성과 공유 가변 데이터
스트림 메서드로 전달하는 코드의 동작 방식을 조금 바꿔 병렬성을 얻을 수 있습니다. 스트림 메서드로 전달하는 코드는 다른 코드와 동시에 실행하더라도 안전하게 실행될 수 있어야 합니다. 보통 다른 코드와 동시에 실행하더라도 안전하게 실행할 수 있는 코드를 만들려면 공유된 가변 데이터에 접근하지 않아야 합니다. 하지만 공유된 변수가 객체가 있으면 병렬성에 문제가 발생합니다.
기존처럼 synchronized를 이용해서 공유된 가변 데이터를 보호하는 규칙을 만들 수 있습니다. 하지만, 자바 8을 이용하면 기존의 자바 스레드 API보다 쉽게 병렬성을 활용할 수 있습니다. 다중 프로세싱 코어에서 synchronized를 사용하면 생각보다 훨씬 더 비싼 대가를 치러야 할 수 있습니다.
공유되지 않은 가변 데이터, 메서드, 함수 코드를 다른 메서드로 전달하는 두 가지 기능은 함수형 프로그래밍 패러다임의 핵심적인 사항입니다. 반면, 명령형 프로그래밍 패러다임에서는 일련의 가변 상태로 프로그램을 정의합니다.
1.3 자바 함수
프로그래밍 언어에서 함수(function)라는 용어는 메서드(method), 특히 정적 메서드(static method)와 같은 의미로 사용됩니다. 자바의 함수는 이에 더해 수학적인 함수처럼 사용되며 부작용을 일으키지 않은 함수를 의미합니다.
프로그래밍의 핵심은 값을 바꾸는 것이고, 이 값을 일급 값(시민)이라고 부릅니다. 자바 프로그래밍 언어의 다양한 구조체(메서드, 클래스 같은)가 값의 구조를 표현하는 데 동무가 될 수 있습니다. 하지만 프로그램을 실행하는 동안 이러한 모든 구조체를 자유롭게 전달할 수는 없습니다. 이렇게 전달할 수 없는 구조체는 이급 값(시민)입니다. 위에서 언급한 값은 모두 일급 자바 시민이지만 메서드, 클래스 등은 이급 자바 시민에 해당합니다.
인스턴스화한 결과가 값으로 귀결되는 클래스를 정의할 때 메서드를 유용하게 활용할 수 있지만 메서드와 클래스는 그 자체로 값이 될 수 없습니다. 예를 들어, 런타임에 메서드를 전달할 수 있다면 즉, 메서드를 일급 시민으로 만들면 프로그래밍에 유용하게 활용할 수 있습니다.
따라서, 자바 8 설계자들은 이급 시민을 일급 시민으로 바꿀 수 있는 기능을 추가했습니다.
1.3.1 메서드와 람다를 일급 시민으로
자바 8의 설계자들은 메서드를 값으로 취급할 수 있게 설계하기로 했습니다. 자바 8에서 메서드를 값으로 취급할 수 있는 기능은 스트림 같은 다른 자바 8의 기능을 토대로 제공했습니다.
첫 번째로 메서드 참조라는 새로운 자바 8의 기능이 있습니다.
// 자바 8 이전
File[] hiddenFiles = new File(".").listFiles(new FileFilter() {
public boolean accept(File file) {
return file.isHidden(); // 숨겨진 파일 필터링
}
});
// 자바 8 이후
File[] hiddenFiles = new File(".").listFiles(File::isHidden);
자바 8의 메서드 참조 :: 를 이용해서 isHidden 함수를 listFiles에 직접 전달할 수 있습니다. 자바 8에서는 더 이상 메서드는 이급 값이 아닌 일급 값입니다. 기존의 객체 참조(new 키워드로 객체를 참조)로 객체를 주고받았던 것처럼 자바 8에서 File::isHidden 메서드 참조를 만들어 전달할 수 있습니다.
람다 : 익명 함수
자바 8에서는 메서드를 일급 값으로 취급할 뿐 아니라 람다(익명 함수)를 포함하여 함수도 값으로 취급할 수 있습니다.
직접 메서드를 정의할 수도 있지만, 이용할 수 있는 클래스나 메서드가 없을 때 새로운 람다 문법을 이용하면 코드를 간결하게 할 수 있습니다.
1.3.2 코드 넘겨주기 : 예제
Apple 클래스와 getColor 메서드가 있고, Apples 리스트를 포함하는 변수 inventory가 있습니다.
이때, 모든 녹색 사과를 선택해서 리스트를 반환하는 프로그램을 구현하면 다음과 같습니다.
// 사과를 녹색으로 필터링하고싶으면 다음처럼 코드를 구현하면 됩니다.
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;
}
자바 8에서는 다음과 같이 구현할 수 있습니다.
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);
프레디케이트(predicate)란 무엇인가?
수학에서는 인수로 값을 받아 true나 false로 반환하는 함수를 프레디케이트라고 합니다.
자바 8에서 Function <Apple, Boolean> 같이 코드를 구현할 수 있지만 Predicate <Apple>을 사용하는 것이 더 표준적인 방식입니다.
1.3.3 메서드 전달에서 람다로
한 두 번만 사용할 메서드를 매번 정의하는 문제가 생기면 자바 8에서 익명 함수 또는 람다라는 개념을 이용해 해결할 수 있습니다.
filter(inventory, (Apple a) -> Green.equals(a.getColor()));
//또는 다음과 같이 구현할 수 있습니다.
filterApples(inventory, (Apple a) -> a.getWeight() > 150);
filterApples(inventory, (Apple a) -> a.getWeight() < 80 || RED.equals(a.getColor()));
즉, 한 번만 사용할 메서드는 따로 정의를 구현할 필요가 없습니다. 하지만 람다가 몇 줄 이상으로 길어진다면, 코드가 수행하는 일을 잘 설명하는 이름을 가진 메서드를 정의하고 메서드 참조를 활용하는 것이 좋습니다.
1.4 스트림
기존의 컬렉션 API에서는 for-each 루프를 통한 반복 과정을 직접 처리했습니다. 이러한 방식을 외부 반복(external iteration)이라고 합니다. 반면, 스트림 API를 이용하면 루프를 신경 쓸 필요가 없습니다. 내부 반복(internal iteration) 방식을 사용해 API 내부에서 모든 데이터가 처리되기 때문입니다.
1.4.1 멀티 스레딩은 어렵다.
자바 8은 스트림 API로 컬렉션을 처리하면서 발생하던 두 가지 문제를 해결했습니다.
컬렉션을 처리하면서 발생하는 모호함과 반복적인 코드의 문제 그리고 멀티 코어의 활용이 어렵다는 점이다.
컬렉션은 어떻게 데이터를 저장하고 접근할지에 중점을 두고, 스트림은 데이터에 어떤 계산을 할 것인지 묘사하는 것에 중점을 둡니다.
1.5 디폴트 메서드와 자바 모듈
자바 9의 모듈 시스템은 모듈을 정의하는 문법을 제공하므로 이를 이용해 패키지 모음을 포함하는 모듈을 정의할 수 있습니다. 모듈 덕분에 JAR 같은 컴포넌트에 구조를 적용할 수 있으며 문서화와 모듈 확인 작업이 용이해졌습니다. 또한, 자바 8에서는 인터페이스를 쉽게 바꿀 수 있도록 디폴트 메서드를 지원합니다. 예를 들어, 자바 8에서 List에 sort 메서드를 직접 호출할 수 있는데, 자바 8의 List 인터페이스에 sort 메서드가 디폴트 메서드로 추가되었기 때문입니다.
default void sort(Comparator<? super E> c) {
Collections.sort(this, c);
}
인터페이스에 새로운 메서드를 추가한다면 인터페이스를 구현하는 모든 클래스는 새로 추가된 메서드를 구현해야 합니다. 결정적으로 자바 8은 구현 클래스에서 구현하지 않아도 되는 메서드를 인터페이스에 추가할 수 있는 기능을 제공합니다. 디폴트 메서드를 이용하면 기존의 코드를 건드리지 않고 원래의 인터페이스 설계를 자유롭게 확장할 수 있습니다.
자바 8에서는 인터페이스 규격 명세에 default라는 새로운 키워드를 지원해 줍니다.
1.6 함수형 프로그래밍에서 가져온 다른 유용한 아이디어
예를 들어, 함수형 언어에서는 명시적으로 서술형의 데이터를 이용해서 Null을 회피하는 기법이 있습니다. 자바 8에서는 NPE를 피할 수 있도록 도와주는 Optional <T> 클래스를 제공해 줍니다. Optional <T>는 값을 갖거나 갖지 않을 수 있는 컨테이너 객체이며, 값이 없는 상황을 어떻게 처리할지 명시적으로 구현하는 메서드를 포함하고 있습니다. 따라서 , Optional <T>를 사용하면 NPE를 피할 수 있습니다. 즉, 형식 시스템을 이용해서 변수에 값이 없을 때 어떻게 처리할지 명시할 수 있습니다.
'📚 서적 및 학습자료 > 모던 자바 인 액션[2회독]' 카테고리의 다른 글
[모던 자바 인 액션] 3장 동작 람다 표현식(1) (1) | 2023.12.03 |
---|---|
[모던 자바 인 액션] 2장 동작 파라미터화 코드 전달하기 (0) | 2023.12.02 |
[모던 자바 인 액션] 20장 OOP와 FP의 조화 : 자바와 스칼라 비교 (0) | 2023.04.02 |
[모던 자바 인 액션] 19장 함수형 프로그래밍 기법 (0) | 2023.04.02 |
[모던 자바 인 액션] 18장 함수형 관점으로 생각하기 (0) | 2023.04.02 |