해당 포스팅에서는 자세한 설명보다는 "대략적으로 어떻게 변하였다"와 "변화된 간단한 코드폼"만 제시합니다.
앞으로 이어질 포스팅에서 각 부분에 대하여 자세히 설명하겠습니다.
1. 역사의 흐름은 무엇인가?
2. 왜 아직도 자바는 변화하는가?
3. 자바 함수
4. 스트림
5. 디폴트 메서드와 자바 모듈
java8
은 간결한 코드, 멀티 코어 프로세서의 쉬운 활용을 기반으로 합니다.
과거의 과거.. 싱글 코어 프로세서
를 사용할 때에는 1개의 작업에 대하여 병렬적으로 처리를 할 필요도 없고 오히려 스위칭 비용이 더 나오지 않을까 기대도 해봅니다.
현재에는 멀티 코어 프로세서
를 사용하지 않는 환경을 찾아보기가 힘듭니다.
하지만 java7
이전의 코드에서는 병렬 처리를 하기위한 코드가 복잡하였습니다.
java5, java7에서 병렬 처리를 위해 추가된 도구
thread pool, concurrent collection, fork/join framework
Single core processor multiple task
https://www.makeuseof.com/single-core-processor-run-multiple-tasks/
parallelism on single core
https://stackoverflow.com/questions/12140201/task-parallel-library-parallelism-on-single-core
java 7 이전의 병렬 처리
public static void main(String[] args) {
int[] numbers = {1, 2, 3, 4, 5};
ExecutorService executor = Executors.newFixedThreadPool(numbers.length);
for (int i = 0; i < numbers.length; i++) {
executor.submit(new Worker(numbers[i]));
}
executor.shutdown();
}
private static class Worker implements Runnable {
private final int number;
public Worker(int number) {
this.number = number;
}
@Override
public void run() {
// 병렬 처리할 작업 수행
System.out.println("Processed number: " + number);
}
}
java 8 이후의 병렬 처리
parallel()
만 추가하면 병렬 처리를 수행합니다.
int[] numbers = {1, 2, 3, 4, 5};
Arrays.stream(numbers)
.parallel()
.forEach(number -> {
// 병렬 처리할 작업 수행
System.out.println("Processed number: " + number);
});
Stream?
Stream
을 Java
api와 비교할 때 Collection
이 자주 등장합니다.
Stream vs Collection
1. Data Modification
2. External Iteration Vs Internal Iteration
3. Traversal
4. Eager Construction Vs Lazy Construction
Collection은 데이터의 저장에 관한 것이고 Stream은 데이터의 처리에 관한 것, 외부 반복과 내부 반복등의 차이가 존재하며 자세한 설명은이어지는 포스팅에서 다루겠습니다.
Map을 활용한 문제 해결
int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
Map<Integer, List<Integer>> result = new HashMap<>();
for (int number : numbers) {
if (number % 2 == 0) {
if (!result.containsKey(0)) {
result.put(0, new ArrayList<>());
}
result.get(0).add(number);
} else {
if (!result.containsKey(1)) {
result.put(1, new ArrayList<>());
}
result.get(1).add(number);
}
}
VS
Stream을 활용한 문제 해결
int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
Map<Integer, List<Integer>> result = Arrays.stream(numbers)
.boxed()
.collect(Collectors.groupingBy(number -> number % 2 == 0 ? 0: 1));
FP의 함수에 대한 이해도가 떨어져 일반적인 의미의 메서드와 함께 포괄하여 제목에서 동작으로 표현하였습니다.
1급시민이란 아래의 조건을 충족하는 것을 의미합니다.
간단하게 말해 일반적인 값과 동등하게 취급하는 것입니다.
이로써 동작을 전달하거나 조합하거나 저장하며 더욱 다양한 상황에 사용할 수 있습니다.
java7
까지도 익명 클래스를 사용하여 메서드를 1급시민 취급할 수 있으나 사용하는데 불필요한 코드가 많이 붙었습니다.
java8
부터 Lambda, Function api, Method References, Stream이 도입되고 익명 클래스, 스태틱 메소드, 불필요한 메소드들이 많이 제거되었습니다.
동작이 1급 시민이 아니라면 (익명 클래스)
public interface Arithmeticable {
int doArithmetic(int value);
}
public class Coin {
private int value = 1;
public void arithmetic(Arithmeticable arithmetic) {
value = arithmetic.doArithmetic(value);
}
public int getValue() {
return value;
}
}
main() {
Coin coin = new Coin();
// 익명 클래스
coin.arithmetic(new Arithmeticable() {
@Override
public int doArithmetic(int value) {
return value + 100;
}
});
}
동작이 1급 시민 (Method References)
public class Plus100{
// 스태틱 메소드
public static Integer doArithmetic(Integer value) {
return value + 100;
}
}
public class Coin {
private int value = 1;
public void arithmetic(Arithmeticable arithmetic) {
value = arithmetic.doArithmetic(value);
}
public int getValue() {
return value;
}
}
main() {
Coin coin = new Coin();
coin.arithmetic(Plus100::doArithmetic);
}
동작이 1급 시민 (Lambda)
public class Coin {
private int value = 1;
public void arithmetic(Function<Integer, Integer> arithmetic) {
value = arithmetic.apply(value);
}
public int getValue() {
return value;
}
}
main() {
Coin coin = new Coin();
coin.arithmetic((value) -> value + 100);
}
해당 기능도 java8
에서 추가되었으며 새로 추가된 기능과의 호환성을 위한 기능입니다.
java
의 인터페이스는 상속한 클래스들이 인터페이스가 가지고 있는 추상 메서드를 모두 구현해야하는 규칙이 있습니다.
하지만 Default keyword를 사용하면 Interface에서 body가 구현된 method를 사용할 수 있습니다.
간단한 예시
public interface Executable {
void abstractExecute();
// interface이지만 완성된 기능이 포함
default void defaultExecute() {
System.out.println("execute");
}
}
Collection Interface에 구현된 default method입니다.
stream()을 통하여 java7
이전에 구현된 기능에도 호환성에 대하여 큰 문제없이
새롭게 추가된 api를 사용할 수 있습니다.
하지만 java
는 본래 다중 상속을 막은 언어입니다.
해당 기능의 취지를 잘 이해하고 사용하지 않고 default method를 사용한다면 다중 상속을 하는 꼴이 됩니다.
현재 포스팅하는 글쓴이도 경험이 없어 다중 상속의 리스크가 어떤 것인지는 정확히 파악할 수가 없습니다.
같이 학습하면서 왜 문제가 되는지 알아보면 좋을 것 같습니다.
해당 포스팅에서 정리한것 외에도 Optional이라는 큰 기능이 추가되었습니다.
글쓴이가 판단하기에는 단순히 java8
부터는 추가된 기능만 숙달하는 것은 큰 의미가
없다고 생각이 듭니다.
물론 단순히 사용하는 것 만으로도 큰 혜택을 누릴 수 있는 부분이 많지만
java
와 함수형 프로그래밍, 분산 처리가 굉장히 밀접해지고 있다고 생각이 듭니다.
본질적인 부분의 이해도가 높아질수록 현대적으로 자바를 더 잘 사용할 수 있을 것 같습니다.
앞으로 포스팅에서 더 자세히 설명해보고 포스팅하도록 하겠습니다.
감사합니다.