📌 섹션 0 : 소개

➕ Java 8 소개

1. LTS(Long-Term-Support) 와 비-LTS 버전의 차이

“I propose that after Java 9 we adopt a strict, time-based model with a new feature release every six months, update releases every quarter, and a long-term support release every three years.”

가장 큰 차이는 표현 그대로 지원의 차이라고 한다. 위 글과 같이 Java9 버전부터 6개월 마다 배포를 하고, LTS의 경우 3년 마다 배포를 하겠다고 한다. 그러므로 이와같은 특징들을 가진다.

  • 매년 3월과 9월에 새 버전을 배포한다.
  • 비-LTS의 배포 주기는 6개월이며, 지원 기간 또한 6개월 이다.
  • LTS의 배포 주기는 3년이므로, 매 6번째 배포판이 LTS가 된다.
  • LTS의 지원 지간은 5년 이상으로 JDK를 제공하는 벤더와 이용하는 서비스에 따라 다르다.

위와 같은 특징을 가지므로, 실제 서비스 운영 환경(Production)에서는 LTS 버전을 권장한다.

➕ 주요 기능

  • 람다 표현식, 메소드 레퍼런스, 스트림 API, Optional, ...

📌 섹션 1 : 함수형 인터페이스와 람다

➕ 함수형 인터페이스와 람다 표현식 소개

1. 함수형 인터페이스 (Functional Interface)

  • 추상 메소드를 하나만 가지고 있는 인터페이스를 말한다.
  • @FuncationInterface 애노테이션을 사용하면 추상 메소드를 2개 이상 선언 시 컴파일 에러가 발생한다.

2. 람다 표현식(Lambda Expressions)

// 익명 내부 클래스 anonymous inner class
RunSomething runSomething = new RunSomething() {
    @Override
    public void doIt() {
        System.out.println("Hello");
    }
};

// 람다 표현식
RunSomething runSomething1 = () -> System.out.println("Hello");

3. 자바에서 함수형 프로그래밍

  • 함수를 First Class Object로 사용할 수 있다.
  • 순수 함수(Pure Function)
    • 상태가 없다 → 함수 밖에 있는 값을 사용하지 않는다.
    • 사이드 이펙트가 없다. → 함수 밖에 있는 값을 변경하지 않는다.
    • 불변성을 가진다.
  • 고차 함수(Higher-Order Function)
    • 함수가 함수를 매개변수로 받을 수 있고, 함수를 리턴할 수도 있다.

➕ 자바에서 제공하는 함수형 인터페이스

  • java.lang.funcation 패키지
  • Function<T, R>
    • T 타입을 받아서 R 타입을 리턴하는 함수 인터페이스
    • R apply(T t) 메소드 사용, 함수 조합용 메소드로 andThen, compose가 있다.
  • BiFunction<T, U, R>
    • 두 개의 값(T, U)를 받아서 R 타입을 리턴하는 함수 인터페이스
    • R apply(T t) 메소드 사용
  • Consumer
    • T 타입을 받아서 아무값도 리턴하지 않는 함수 인터페이스
    • void accept(T t) 메소드 사용, 함수 조합용 메소드로 andThen가 있다.
  • Supplier
    • T 타입의 값을 제공하는 함수 인터페이스
    • T get() 메소드 사용
  • Predicate
    • T 타입을 받아서 boolean을 리턴하는 함수 인터페이스
    • boolean test(T t) 메소드 사용, 함수 조합용 메소드로 and, or, negate가 있다.
  • UnaryOperator
    • Function<T, R> 의 특수한 형태로, 하나의 타입을 받아서 동일한 타입을 리턴하는 함수 인터페이스
  • BinaryOperator
    • BiFunction<T, U, R> 의 특수한 형태로, 동일한 타입의 두 값을 받아서 동일한 타입을 리턴하는 함수 인터페이스

➕ 람다 표현식

1. 형식

(인자 리스트) -> {바디}
  
// 인자가 없을 떄
Supplier<Integer> get10 = () -> {
    return 10;
};

// 인자를 받을 때
BinaryOperator<Integer> sum = (a, b) -> a + b;

/*
* 인자의 타입은 생략이 가능, 컴파일러가 추론(infer)하지만 명시할 수도 있다.
* 여러 줄인 경우에 {} 사용, 한 줄인 경우에 생략 가능하며, return도 생략이 가능하다.
* */

2. 변수 캡처(Variable Capture)

/*
 * 중첩클래스(Nested Class)의 경우 지역 변수(Local Variable)를 참조하는 경우 변수캡처(Variable Capture)가 일어난다.
 * Java8 이전에는 final 키워드가 있어야만 참조가 가능했다.
 * Java8 부터는 final 키워드는 해당 변수가 사실상 final인 경우(Effective final)에는 생략이 가능하다.
 * 람다(Lambda)의 경우도 지역 변수를 참조하는 경우 변수캡처가 발생한다.
 * 차이점은 Scope이다.
 * 중첩클래스의 경우 해당 클래스의 Scope이 새로 생성되기 때문에, 그 안에서 지역 변수와 같은 이름의 변수선언 시 쉐도윙(Shadowing)이 발생한다.
 * 하지만, 람다의 Scope는 새로운 Scope이 생성되는 것이 아닌 람다를 감싸고 있는 Scope과 같기 때문에 컴파일 에러를 발생시킨다.
 * */
private void run() {
    int baseNumber = 10;

    // 지역 중첩 클래스(Local Inner Class)
    // run() 메소드의 baseNumber를 변수캡처하여 사용하는 경우
    class LocalClass{
        void printBaseNumber(){
            System.out.println(baseNumber);
        }
    }

    // 익명 중첩 클래스(Anonymous Inner Class
    // run() 메소드의 baseNumber를 쉐도윙(Shadowing)하는 경우
    Consumer<Integer> integerConsumer = new Consumer<Integer>() {
        int baseNumber = 20;

        @Override
        public void accept(Integer integer) {
            System.out.println(baseNumber);
        }
    };

    // 람다에서 local variable 참조하는 경우
    IntConsumer printInt = (i) -> System.out.println(i + baseNumber);
    printInt.accept(10);
}

➕ 메소드 레퍼런스

1. 메소드 레퍼런스를 통해 참조하는 방법

/* 1.스태틱 메소드 참조 */
UnaryOperator<String> hi = Greeting::hi;
System.out.println(hi.apply("haeny"));

/* 2.특정 객체의 인스턴스 메소드 참조 */
Greeting greeting = new Greeting();
UnaryOperator<String> hello = greeting::hello;
System.out.println(hello.apply("haeny"));

/* 3.생성자 참조 */
// 인자가 없는 생성자를 참조
Supplier<Greeting> greetingSupplier = Greeting::new;

// String 인자를 받는 생성자를 참조
Function<String, Greeting> greetingFunction = Greeting::new;

/* 4.임의 객체의 인스턴스 메소드 참조 */
String[] names = {"haeny", "juhyun", "devillian"};

// Java8 이전
Arrays.sort(names, new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
        return 0;
    }
});

// Java8에서 Comparator가 FunctionalInterface가 되었음.
Arrays.sort(names, (o1, o2) -> 0);

// 람다 자리에 메소드 레퍼런스를 사용
Arrays.sort(names, String::compareToIgnoreCase);

📖 REFERENCE

Oracle Java SE Support Roadmap
Lambda Expressions
Nested Classes
Method References

0개의 댓글