728x90
자바 역사를 통틀어 가장 큰 변화가 자바 8에서 일어났다.

 

1.1 역사의 흐름은 무엇인가?

  • 자바 8을 이용하면 자연어에 더 가깝게 간단한 방식으로 코드를 구현할 수 있다.
  • 지금까지의 대부분의 자바 프로그램은 코어 중 하나만을 사용했다.
    • 나머지 코어는 유휴 idle 상태로 두거나, 운영체제나 바이러스 검사 프로그램과 프로세스 파워를 나눠서 사용

 

  • 자바 8 등장 이전 : 나머지 코어 활용 → 스레드
    • 스레드 사용은 관리가 어렵고 많은 문제가 발생할 수 있음
    • 자바 1.0 : 스레드, 락, 메모리 모델 지원
    • 자바 5 : 스레드 풀(thread pool), 병렬 실행 컬렉션(concurrent collection)
    • 자바 7 : 포크/조인 프레임워크
    • 자바 8 : 스트림 API, 동작 파라미터화, 인터페이스의 디폴트 메서드
    • 자바 9 : 리액티브 프로그래밍(RxJava)

 

자바 8

  • 간결한 코드
  • 멀티코어 프로세서의 쉬운 활용
  • 스트림
    • DB 쿼리에서 고수준 언어로 동작 표현 → 자바에서 최적의 저수준 실행 방법 선택 후 동작
    • synchronized 를 사용하지 않아도 된다.
      • High Cost : 멀티 코어 CPU에서 각 코어는 별도 캐시를 두고 있는 데, 락을 사용하면 캐시가 동기화되어야함
        캐시 일관성 프로토콜 인터코어 통신(cache-coherency-protocol intercore communication) 발생
  • 함수형 프로그래밍(functional-style programming)

 

 


1.2 왜 아직도 자바는 변화하는가?

  • 완벽한 프로그래밍 언어

 

  • 시공을 초월하는 완벽한 언어를 원하지만 현실적으로 그런 언어는 존재하지 않다.
  • C, C++
    • 프로그래밍 안전성 부족 But, 작은 런타임 “Foot Print”
    • 👣 *Foot Print ?* → 하드웨어나 소프트웨어 단위가 차지하고 있는 공간의 크기 → 프로그램 실행 중에 사용하거나 참조하는 메인 메모리의 총량
    • 낮은 안정성 → 프로그램이 예기치 않게 종료되거나 바이러스 침투 가능

 

  • 자바의 위치
    • 잘 설계된 객체 지향 언어로 시작
    • 코드를 JVM 바이트 코드로 컴파일 하는 특징 → 모든 브라우저에서 JVM 지원
    • 인터넷 애플릿 프로그램의 주요 언어
    • 객체 지향
      1. 캡슐화
        • 엔지니어링적인 문제가 훨씬 적음
        • ‘모든 것은 객체다’로 대입
      2. 일단 만들면 모든 곳에서 실행 가능(write-once run-anywhere)
    • 초기에는 C, C++에 비해 추가적으로 드는 비용 때문에 자바에 대한 반감 존재
    • But, 하드웨어의 발전으로 실행시간 크게 지장 ❌
  • 자바 8에서 제공하는 기능의 모태 프로그래밍 개념들
    1. 더 다양한 프로그래밍 도구
    2. 다양한 프로그래밍 문제를 더 빠르고 정확하며 쉽게 유지보수의 장점 제공

 

 

1.2.2 스트림 처리

  • 스트림 처리(stream processing)
    • 스트림 : 한 번에 한 개씩 만들어지는 연속적인 데이터 항목들의 모임
    • 유닉스, 리눅스의 많은 프로그램 : 표준 입력에서 데이터 Read → 표준 출력으로 데이터 Write

 

  • e.g. Unix Command
$ cat file1 file2 | tr "[A-Z]" "[a-z]" | sort | tail -3

유닉스에서는 여러 명령을 병렬로 실행

 

  • 스트림 API
    • 어떤 항목을 연속으로 제공하는 어떤 기능
    • Before : 한 번에 한 항목 처리
    • After(자바 8) : 고수준 추상화 → 일련의 스트림으로 만들어서 처리
      스트림 파이프라인을 이용하여 입력 부분을 여러 CPU 코어에 쉽게 할당

 

 

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

  • 동작 파라미터화(behavior parameterization)
    • 코드 일부를 API로 전달하는 기능 (= 메서드를 다른 메서드의 인수로 넘겨주는 기능)

 

 

1.2.4. 병렬성과 공유 가변 데이터

‘병렬성을 공짜로 얻을 수 있다’

  • 스트림 메서드로 전달하는 코드의 동작 방식 변경
  • 스트림 메서드로 전달하는 코드는 다른 코드와 동시에 실행하더라도 안전하게 실행될 수 있어야 한다.

 

  • 안전하게 실행?
    • 공유된 가변 데이터(shared mutable data)에 접근 하지 않아야한다.
    • (순수 함수, 부작용 없는 함수, 상태 없는 함수)

 

  • 공유된 변수를 동시에 바꾸려고 할 때
    • synchronized : 공유된 가변 데이터를 보호하는 규칙 생성
    • But, synchronized 는 시스템에 악영향을 미친다.

 

  • 자바 8 스트림을 이용해서 병렬성 활용 가능

 

 

1.2.5. 자바가 진화해야 하는 이유

  • 자바 8의 큰 변화 : 고전적인 객체 지향 → 함수형 프로그래밍

✨ 언어는 하드웨어나 프로그래머 기대의 변화에 부응하는 방향으로 변화해야 한다.

 

 

1.3 자바 함수

  • 함수 = (정적) 메서드
  • 자바 8에서는 함수를 값 처럼 취급
  • 자바 프로그램에서 조작할 수 있는 값
    • 기본값 (e.g. int , boolean)
    • 객체(객체의 참조) (e.g. new )

✨ 프로그래밍 언어의 핵심 : 값을 바꾸는 것

→ 일급값(first class : 퍼스트 클래스)

  • 런타임 시, 메서드 전달하면 프로그래밍에 유용하게 활용 가능

 

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

  • 메서드 참조(method reference)

e.g.

File[] hiddenFiles = new File(".").listFiles(new FileFilter() {
    public boolean accept(File file) {
            return file.isHidden();
    }
});
  • FileFilter로 isHidden을 감싼 후 인스턴스화
File[] hiddenFiles = new File(".").listFiles(File::isHidden);
  • 메서드 참조(이 메서드를 값으로 사용하라)를 이용해, listFiles에 직접 전달
  • 기존에 비해 문제 자체를 더 직접적으로 설명

 

 

  • 람다 : 익명 함수
    • 람다를 포함하여 함수도 값으로 취급 가능

  • 숨겨진 파일 필터링 방식

 

 

1.3.2 코드 넘겨주기

  • e.g. Apple 클래스와 getColor 메서드 존재
    <사과 무게 | 색상으로 필터링>
// 녹색 사과 필터링 메서드
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;
}

// 150그램 이상 사과 필터링 메서드
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;
}
  • 중복 코드 (copy & paste) 개선
public static boolean isGreenApple(Apple apple) {
    return GREEN.equals(apple.getColor());
}

public static boolean isHeavyApple(Apple apple) {
    return apple.getWeight() > 150;
}

public interface Predicate<T>{
    boolean test(T t);
}

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) : boolean 값을 리턴하는 함수

 

 

1.3.3 메서드 전달에서 람다로

  • 한 두번만 사용할 메서드 정의 → 비효율적
  • 따로 정의를 구현하지 않고 람다를 이용하여 구현한다.
filterApples(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()));
  • But, 람다가 몇 줄 이상으로 길어지게 된다면 메서드를 정의하고 메서드 참조를 활용하는 게 바람직하다.
    코드의 명확성이 우선
728x90