Language Study/Java

[모던 자바 인 액션] 자바 8,9,10,11: 무슨 일이 일어나고 있는가?

지미닝 2024. 7. 23. 16:02
  • 자바가 거듭 변화하는 이유
  • 컴퓨팅 환경의 변화
  • 자바에 부여되는 시대적 변화 요구
  • 자바 8과 자바 9의 새로운 핵심 기능 소개

 

역사의 흐름은 무엇인가?

 자바 역사를 통틀어 가장 큰 변화가 Java 8 에서 일어났다. 물론 이후의 버전에서도 큰 변화가 있었으나, 자바 8만큼 획기적이거나 생산성이 바뀌는 것은 아닐 것이다.

 

 멀티코어 CPU 대중화 같은 하드웨어적인 변화도 영향을 미쳤다. 자바 8 등장 이전에는 스레드를 사용하는 것이 좋다고 그랬겠지만, 자바는 병렬 실행 환경을 쉽게 관리하고 에러가 덜 발생하는 방향으로 진화하려고 노력했다. 이전까지는 스레드 풀이나 병렬 실행 컬렉션, 포크/조인 프레임워크를 제공했으나, 쉽지 않았다고 한다.

 

 

🍃자바 8에서 새롭게 지원한 기술들

  • 스트림 API
  • 메서드를 코드에 전달하는 기법
  • 인터페이스의 디폴트 메서드

스트릠 API덕분에 메서드에 코드를 전달하는 간결 기법(메서드 참조와 람다)인터페이스의 디폴트 메서드가 존재할 수 있음을 알 수 있다. 여기서 메소드에 코드를 전ㄷ라하는 기법을 이용하면, 새롭고 간결한 방식으로 동작  파라미터화를 구현할 수 있다!!!

 

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

 자바는 처음부트 스레드와 락을 이용한 소소한 동시성도 지원했기에 시작부터 좋았다. 코드를 JVM 바이트 코드로 컴파일하는 특징과 모든 브라우저에서 가상 머신 코드를 지원하기에 자바는 Internet Application Program의 주요한 언어가 되었다. 하지만 프로그래밍 언어 생태계에 변화가 계속되며 빅데이터(테라바이트 이상의 데이터셋)라는 도전에 직면하게 되어 멀티코어 컴퓨터나 컴퓨터 클러스터를 이용해서 빅데이터를 효과적으로 처리할 필요성이 커지며 병렬 프로세싱을 활용해야하는데 지금까지의 자바로는 충분히 대응하기 어려웠다.

 

스트림 처리

  스트림이란 한 번에 한 개씩 만들어지는 연속적인 데이터 항목들의 모임이다. 이론적으로 프로그램은 입력 스트림에서 데이터를 한개씩 읽어들이며 마찬가지로 출력 스트림으로 데이터를 한 개씩 기록한다.Java 8에는 java.util.stream 패키지에 스트림 API가 추가되었다. 스트림을 통해 우리가 하려는 작업을 고수준으로 추상화해서 일련의 스트림으로 만들어 처리할 수 있게 되었다. 또한 스트림 파이프라인을 이용하여 입력 부분을 여러 CPU 코어에 쉽게 할당할 수 있다는 부가적인 이득도 얻을 수 있다.. 스레드라는 복잡한 작업을 사용하지 않으면서도 꽁짜로 병렬성을 얻을 수 있게 되었다!

 

 

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

 코드의 일부를 API로 전달할 수 있게 되었다. 자바 8에서는 메서드를 다른 메서드의 인수로 넘겨주는 기능을 제공하는데, 이런 기능을 이론적으로 동작 파라미터화라고 부른다.

 

병렬성과 공유 가변 데이터

 스트림 메서드로 전달하는 코드의 동작방식을 조금 바꿔야한다. 스트림 메서드로 전달하는 코드는 다른 코드와 동시에 실행하더라도 안전하게 실행될 수 있어야 한다. 보통 다른 코드와 동시에 실행 하더라도 안전하게 실행할 수 있는 코드를 만들려면 공유된 가변 데이터에 접근 하지 않아야 한다. 이러한 함수를 순수함수, 부작용 없는 함수, stateless 함수라고 부른다. 공유된 변수나 객체가 생기면 병렬성에 문제를 갖게 된다.

 


자바 함수

 함수라는 용어는 메서드 특히 정적 메서드와 같은 의미로 사용된다. 자바 함수는 이에 더해 수학적인 함수와 같이 사용되며 부작용을 일으키지 않는 함수를 의미한다.

 

🤔 함수가 왜 필요할까?

 프로그래밍 언어의 핵삼은 값을 바꾸는 것이다. 역사적으로, 그리고 전통적으로 프로그래밍 언어에서는 이 값을 "일급 시민(퍼스트 클래스)"라고 불렀다. 그리고, 메서드/클래스와 같은 것들은 값의 구조를 표현하는데 도움이 될 수 있으나, 코드가 실행하는 동안 이러한 모든 구조체를 자유롭게 전달할 수 없는데 이것들을 이급 시민이라고 부른다. 

 인스턴스화한 결과가 값으로 귀결되는 클래스를 정의할 때 메서드를 아주 유용하게 활용할 수 있으나, 여전히 그 자체로 값이 될 수 없었기에 자바 8 설계지들은 이급 시민을 일급 시민으로 바꿀 수 있는 기능들을 추가했다.

 

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

메소드 참조를 활용해 이미 함수를 준비시켜, 직접 전달할 수 있게 되었다. 

File[] hiddenFiles = new File(".").listFiles(File::isHidden);

 

이제 메서드가 더이상 이급값이 아닌 일급값이 되었다. 기존에는 객체 참조로 이리저리 객체를 주고받았으나, 이제는 메소드 참조를 통해서 전달할 수 있게 되었다!

 

람다 : 익명함수

자바 8에서는 메서드를 일급값으로 취급할 뿐 아니라 람다를 포함하여 함수도 값으로 취급할 수 있다. 매번 한두번 사용할 메서드를 정의하는 일은 귀찮기에 다음과 같이 구현할 수 있게 되었다.

filterApples(inventory, (Apple a) -> a.getWeight() > 150 );
filterApples(inventory, (Apple a) -> a.getWeight() < 80 || RED.equals(a.getColor()) );

한 번만 사용할 메서드는 따로 정의를 구현할 필요가 없다. 

 

하지만 람다가 몇 줄 이상으로 길어진다면(즉, 조금 복잡한 동작을 수행하는 상황) 익명 람다 보다는 코드가 수행하는 일을 잘 설명하는 이름을 가진 메서드를 정의하고 메서드 참조를 활용 하는 것이 바람직하다. 코드의 명확성이 우선시되어야 한다.

 

스트림

거의 모든 자바 애플리케이션은 컬렉션을 만들고 활용한다. 하지만 컬렉션으로 모든 문제가 해결되는 것은 아니다. 이런 방식의 반복을 외부 반복이라고 한다. 반면 스트림 API를 이용하면 루프를 신경 쓸 필요가 없다. 스트림 API에서는 라이브러리 내부에서 모든 데이터가 처리된다. 이 와 같은 반복을 내부 반복이라고 한다.

 외부반복을 활용할 경우 오랜 시간이 걸릴 수 있다. 하지만 멀티 코어에 서로다른 CPU 코어에 작업을 할당한다면 처리시간을 줄일 수 있다.