ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Part1] Java8 In action - Chapter3 - 1
    Java8 In Action 2022. 7. 27. 11:13
    반응형

    해당 내용은 Java8 In Action 책을 요약 및 정리한 내용입니다.

    좀 더 자세히 알고 싶으신 분들은 책을 사서 읽어보심을 추천드립니다.!

    3.1 람다란 무엇인가

    Entity

    AppleEntity.java

    package Part1.Chapter3.Chapter3_3_1.entity;
    
    public class AppleEntity {
    
        private String color;
        private Integer weight;
    
        public AppleEntity() {}
    
        public AppleEntity(String color, Integer weight) {
            this.color = color;
            this.weight = weight;
        }
    
        public String getColor() {
            return color;
        }
        public void setColor(String color) {
            this.color = color;
        }
        public Integer getWeight() {
            return weight;
        }
        public void setWeight(Integer weight) {
            this.weight = weight;
        }        
    }

    Main

    package Part1.Chapter3.Chapter3_3_1;
    
    import java.util.Arrays;
    import java.util.Comparator;
    import java.util.List;
    
    import Part1.Chapter3.Chapter3_3_1.entity.AppleEntity;
    
    /*
     * 3.1 람다란 무엇인가?
     * 
     * 람다 표현식은 메서드로 전달할 수 있는 익명 함수를 단순화한 것이라고 할 수 있다.
     * 람다 표현식은 이름은 없지만
     *         - 파라미터 리스트
     *         - 바디
     *         - 반환 형식
     *         - 발생할 수 있는 예외 리스트
     * 는 가질 수 있다.
     * 
     * 람다의 특징으로
     *         익명
     *             보통의 메서드와 달리 이름이 없으므로 익명이라 표현한다.
     *         함수
     *             람다는 메서드처럼 특정 클래스에 종속되지 않으므로 함수라고 부른다.
     *             하지만 메서드처럼 파라미터 리스트, 바디, 반환 형식, 가능한 예외 리스트를 포함한다.
     *        전달
     *            람다 표현식을 메서드 인수로 전달하거나 변수로 저장할 수 있다.
     *        간결성
     *            익명 클래스처럼 많은 코드를 구현할 필요가 없다.
     *
     * 람다(lambda)라는 용어는 람다 미적분학 학계에서 개발한 시스템에서 유래했다.
     * 람다 표현식이 중요한 이유는 코드를 전달하는 과정에서 자질구레한 코드가 많이 생긴다. 
     * 그러나 람다를 이용하면 간결한 방식으로 코드를 전달할 수 있다.
     */
    public class Main_3_1 {
        public static void appleComparator(List<AppleEntity> inventory) {
            /*
             * 익명 클래스를 이용한 정렬.
             */
            Comparator<AppleEntity> byWeight = new Comparator<AppleEntity>() {
    
                @Override
                public int compare(AppleEntity o1, AppleEntity o2) {
                    return o1.getWeight().compareTo(o2.getWeight());
                }
            };
    
            inventory.sort(byWeight);
    
            inventory.stream()
                .forEach((AppleEntity apple) -> System.out.println("[Anonymous Class - Ascending] color : " + apple.getColor() 
                + ", weight : " + apple.getWeight() ));
    
            /*
             * 람다 표현식으로 이용한 정렬.
             * 
             * 람다는 세 부분으로 이루어진다.
             *         (AppleEntity o1, AppleEntity o2) : 람다 파라미터
             *         -> : 화살표
             *         o2.getWeight().compareTo(o1.getWeight()) : 람다 바디 
             *             - 파라미터 리스트
             *                 Comparator의 compare 메서드의 파라미터
             *            - 화살표
             *                화살표(->)는 람다의 파라미터 리스트와 바디를 구분한다.
             *            - 람다의 바디
             *                두 사과의 무게를 비교한다. 람다의 반환값에 해당하는 표현식이다.
             * 
             * 아래는 자바 8에서 지원하는 다섯 가지 람다 표현식 예제다.
             *         - (String s) -> s.length()
             *             String 형식의 파라미터 하나를 가지며 int를 반환한다.
             *             람다 표현식에서는 return이 함축되어 있으므로 return 문을
             *             명시적으로 사용하지 않아도 된다.
             * 
             *         - (AppleEntity apple) -> apple.getWeight() > 150
             *             AppleEntity 형식의 파라미터 하나를 가지며
             *             boolean(사과가 150그램보다 무거운지 결정)을 반환한다.
             * 
             *         - (int x, int y) -> {
             *             System.out.println("Result : ");
             *             System.out.println(x + y);
             *         }
             *        int 형식의 파라미터를 두 개 가지며 리턴값이 없다(정확히 void 리턴)
             *        이 예제에서 볼 수 있듯이 람다 표현식은 여러 행의 문장을 포함할 수 있다.
             *    
             *        - () -> 42
             *            파라미터가 없으며 int를 반환한다.
             *
             *        - (AppleEntity o1, AppleEntity o2) -> o2.getWeight().compareTo(o1.getWeight())
             *            AppleEntity 형식의 파라미터를 두 개 가지며 int(두 사과의 무게 비교 결과)를 반환한다.
             *
             * 자바 설계자는 이미 C#이나 스칼라 같은 비슷한 기능을 가진 다른 언어와 비슷한 문법을 
             * 자바에 적용하기로 했다. 아래는 람다의 기본 문법이다.
             *         (parameters) -> expression
             * 또는 다음처럼 표현할 수 있다.(중괄호 이용)
             *         (parameters) -> { expression }
             * 
             * 아래는 람다 예제와 사용 사례 리스트를 보여준다.
             *        - 불린 표현식 : (List<String> list) -> list.isEmpty()
             *        - 객체 생성 : () -> new AppleEntity()
             *        - 객체에서 소비 : 
             *            (AppleEntity apple) -> {
             *                System.out.println(apple.getWeight());
             *            }
             *        - 객채에서 선택/추출 : (String s) -> s.length();
             *        - 두 값을 조합 : (int a, int b) -> a * b
             *        - 두 객체 비교 :
             *            (AppleEntity o1, AppleEntity o2) -> o2.getWeight().compareTo(o1.getWeight())
             */
            byWeight = (AppleEntity o1, AppleEntity o2) -> o2.getWeight().compareTo(o1.getWeight());
    
            inventory.sort(byWeight);
    
            inventory.stream()
            .forEach((AppleEntity apple) -> System.out.println("[Lambda - Descending] color : " + apple.getColor() 
            + ", weight : " + apple.getWeight() ));
        }
    
        public static void main(String[] args) {
            List<AppleEntity> inventory = Arrays.asList(new AppleEntity("green", 100)
                    , new AppleEntity("red", 120)
                    , new AppleEntity("green", 150));
    
            appleComparator(inventory);
    
        }
    
    }

    3.2 어디에,어떻게 람다를 사용할까

    Main

    package Part1.Chapter3.Chapter3_3_2;
    /*
     * 3.2 어디에, 어떻게 람다를 사용할까?
     * 
     * 함수형 인터페이스라는 문맥에서 람다 표현식을 사용할 수 있다.
     * 
     * 
     * 3.2.1 함수형 인터페이스
     * 앞 전에서 만든 Predicate<T>인터페이스로 필터 메서드를 파라미터화할 수 있음을 기억하는가?(못해도 한다고 하자.)
     * 바로 Predicate<T>가 함수형 인터페이스이다. Predicate<T>는 오직 하나의 추상 메서드만 지정하기 때문이다.
     * 
     * public interface Predicate<t> {
     *         boolean test(T t);
     * }
     * 
     * 간단히 말해 함수형 인터페이스는 "정확히 하나의 추상 메서드를 지정하는 인터페이스" 다.
     * 지금까지 살펴본 자바 API의 함수형 인터페이스로 Comprator, Runnable 등이 있다.
     * (아래에 나오는 인터페이스는 자바 API의 함수형 인터페이스다.)
     * 
     * java.util.Comparator
     * public interface Comparator<T> {
     *         int compare(T o1, T o2);
     * }
     * 
     * java.lang.Runnable
     * public interface Runnable {
     *         void run();
     * }
     * 
     * java.awt.event.ActionListener
     * public interface ActionListener exends EventLstener {
     *         void actionPerformed(ActionEvent e);
     * }
     * 
     * java.util.concureent.Callable
     * public interface Callable<V> {
     *         V call();
     * }
     * 
     * java.security.PrivilegedAction
     * public interface PrivilegedAction<V> {
     *         T run();
     * }
     *
     * 함수형 인터페이스로 뭘 할 수 있을까?(많은걸 할수 있으니깐 만들어겠지..)
     * 람다 표현식으로 함수형 인터페이스의 추상 메서드 구현을 직접 전달할 수 있으므로
     *         전체 표현식을 함수형 인터페이스의 인스턴스로 취급
     *         (기술적으로 따지면 함수형 인터페이스를 concrete 구현한 클래스의 인스턴스)
     * 할 수 있다.
     * 
     * Note
     *         인터페이스는 디폴트 메서드(인터페이스의 메서드를 구현하지 않은 클래스를 
     *         고려해서 기본 구현을 제공하는 바디를 포함하는 메서드)를 포함할 수 있다.
     *         여기서 많은 디폴트 메서드가 있더라도 추상 메서드가 오직 하나면 함수형 인터페이스다.
     */
    public class Main_3_2 {
    
        public static void process(Runnable r) {
            r.run();
        }
    
    
        /*
         * 3.2.1 함수형 인터페이스 예제
         * 
         * 아래 예제의 코드들은 Runnable이 오직 하나의 추상 메서드 run을
         * 정의하는 함수형 인터페이스이므로 올바른 코드다.
         */
        static void runnable() {
            // 람다 사용.
            Runnable r1 = () -> System.out.println("hello hell 1?");        
            process(r1);
    
            // 익명 클래스 사용.
            process(new Runnable() {
    
                @Override
                public void run() {
                    System.out.println("hello hell 2?");
    
                }
            });
    
            // 직접 전달된 람다 표현식.
            process(() -> System.out.println("hello hell 3?"));
        }
    
        /*
         * 3.2.2 함수 디스크립터
         * 
         * 함수형 인터페이스의 추상 메서드 시그너처(signature)는 람다 표현식의 시그너처를 가리킨다.
         * 람다 표현식의 시그너처를 서술하는 메서드를 함수 디스크립터(function descriptor)라고 한다.
         * 예를 들어
         *         Runnable에 유일한 추상 메서드인 run메서드는 인수와 반환값이 없으므로
         *         Runnable 인터페이스는 인수와 반환값이 없는 시그너처로 생각할 수 있다.
         * 
         * 람다 표현식의 유효성을 확인하는 방법은 일단 람다 표현식은 변수에 할당하거나 함수형 인터페이스를
         * 인수로 받는 메서드로 전달할 수 있으며, 함수형 인터페이스의 추상 메서드와 같은 시그너처를 갖는다는
         * 사실을 기억하자.
         * 
         * 함수형 인터페이스에 추상 메서드로 전달 되는 람다 표현식은 해당 메서드의 반환값 혹은 타입, 인수 타입 혹은 갯수에
         * 맞춰서 표현식을 전달 해야 한다.
         *         Runnable의 run 메서드는 반환값은 void이고 인수는 없다. 
         *         위 예제 코드에서
         *             process(() -> System.out.println("hello hell 3?"));
         *         코드를 람다 표현식으로 직접 전달 하였다.          
         *         여기서 직접 전달된 람다 표현식을 분석 하자면 () 인수가 없고 메서드 바디 영역에서는 println메서드만 호출하고
         *         반환값이 없다. 즉, run 함수 시그너처와 같은 인수가 없고 반환값이 없는 형태와 동일하므로 올바른 람다 표현식이다.
         */
        public static void main(String[] args) {
            runnable();
        }
    
        /*
         * @FunctionalInterface는 무엇인가?
         * 새로운 자바 API를 살펴보면 함수형 인터페이스에 @FunctionalInterface라는 어노테이션이 추가 되었다.
         * @FunctionalInterface는 함수형 인터페이스임을 가리키는 어노테이션이다.
         * @FunctionalInterface로 인터페이스를 선언했지만 실제로 함수형 인터페이스가 아니면 컴파일러가 에러를 발생시킨다.
         */
    }

    3.3 람다 활용-실행 어라운드 패턴

    Interface

    BufferedReaderProcessor.java

    package Part1.Chapter3.Chapter3_3_3.buffer.inter;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    
    @FunctionalInterface
    public interface BufferedReaderProcessor {
        String process(BufferedReader b) throws IOException;
    }

    File

    Main_3_3.txt

    이거슨 첫 번째 라인 입니다.
    이거슨 두 번재 라인 입니다.

    Main

    package Part1.Chapter3.Chapter3_3_3;
    
    import java.io.BufferedReader;
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.IOException;
    import java.io.UnsupportedEncodingException;
    import java.net.URLDecoder;
    import java.util.function.Function;
    
    import Part1.Chapter3.Chapter3_3_3.buffer.inter.BufferedReaderProcessor;
    
    /*
     * 3.3 람다 활용 : 실행 어라운드 패턴
     * 
     * 자원 처리(예를 들면 데이터베이스의 파일 처리)에 사용하는 순환 패턴(recurrent pattern)은
     *         자원을 열고,
     *         처리한 다음에,
     *         자원을 닫는
     * 순서로 이루어진다. 설정(setup)과 정리(cleanup)과정은 대부분 비슷하다.
     * 즉, 실제 자원을 처리하는 코드를 설정과 정리 두 과정이 둘러싸는 형태를 갖는다.
     * 
     *         초기화/준비 코드
     *         작업 A
     *         정리/마무리 코드
     *         
     *         혹은
     * 
     *         초기화/준비 코드
     *         작업 B
     *         정리/마무리 코드
     * 
     * 와 같은 형식의 코드를 실행 어라운드 패턴(execute around pattern)이라고 한다.
     */
    public class Main_3_3 {
        public static FileReader getExmpFile() throws FileNotFoundException, UnsupportedEncodingException {        
            return new FileReader(URLDecoder.decode(Main_3_3.class.getResource("").getPath() + "Main_3_3.txt", "UTF-8"));
        }
    
        /*
         * 3.3.1 1단계 : 동작 파라미터화를 기억하라.
         * 
         * 아래 코드는 한 줄만 읽을 수 있다. 만약 한 번에 두 줄을 읽거나 가장 자주 사용되는 단어를 반환하려면 어떻게 해야 할까?
         * 기존의 설정, 정리 과정은 재사용하고 badProcessFile 메서드만 다른 동작을 수행하도록 명령할 수 있다면 좋을 것이다.
         * 이를 위해 badProcessFile의 동작을 파라미터화 하는 것이다. 
         */
        public static String badProcessFile() throws IOException {
            /*
             * 아래의 try 문법을 보면 다른 try랑 특이한데 해당 문법은 try-with-resources문법으로
             * 자바 7에 추가된 새로운 기능이다. 이를 사용하면 자원을 명시적으로 닫을 필요가 없다.
             * (즉, close 같은 메서드를 호출하지 않아도 된다.)
             */
            try(BufferedReader br = new BufferedReader(getExmpFile()) ) {
                return br.readLine();
            }
        }
    
        /*
         * 3.3.2 2단계 : 함수형 인터페이스를 이용해서 동작 전달.
         * 
         * 함수형 인터페이스 자리에 람다를 사용할 수 있다. 따라서 BufferedReader -> String과 IOException을 던질(throw)수 있는
         * 시그너처와 일치하는 함수형 인터페이스를 만들어야 한다.
         * (BufferedReaderProcessor라는 함수형 인터페이스를 만들어서 인수로 받음.)
         */
        public static String functionInterfaceProcessFile(BufferedReaderProcessor b) throws IOException {
            try(BufferedReader br = new BufferedReader(getExmpFile()) ) {
                return b.process(br);
            }
        }
    
        public static void main(String[] args) throws IOException {
            System.out.println("badProcessFile : " + badProcessFile());
    
            /*
             * 3.3.3 3단계 : 동작 실행!
             * 
             * 이제 BufferReaderProcessor에 정의된 process 메서드의 시그너처(BufferedReader -> String)와
             * 일치하는 람다를 전달할 수 있다.
             * 람다 표현식으로 함수형 인터페이스의 추상 메서드 구현을 직접 전달할 수 있으며 전달된 코드는 
             *         "함수형 인터페이스의 인스턴스로 전달된 코드와 같은 방식으로 처리"
             * 한다. 따라서 functionInterfaceProcessFile 바디 내에서 BufferedReaderProcessor 객체의 process를
             * 호출할 수 있다.
             */
            // 람다 표현식
            System.out.println(functionInterfaceProcessFile((BufferedReader br) 
                    -> "functionInterfaceProcessFile : " + br.readLine() + br.readLine()) );
    
            // 익명 클래스
            System.out.println(functionInterfaceProcessFile(new BufferedReaderProcessor() {
                    @Override
                    public String process(BufferedReader b) throws IOException {
                        return "익명 클래스 :" + b.readLine() + b.readLine();
                    }
                }) );
    
            /*
             * 예외, 람다, 함수형 인터페이스의 관계
             * 
             * 함수형 인터페이스는 확인된 예외를 던지는 동작을 허용하지 않는다. 즉, 예외를 던지는 람다 표현식을 만들려면
             * 확인된 예외를 선언하는 함수형 인터페이스를 직접 정의하거나 람다를 try / catch 블록으로 감싸야 한다.
             * 
             * 본 코드에서 사용 된 BufferedReaderProcessor에서 process 메서드 시그너처에 명시적으로 IOException을 선언.
             * 
             * 그러나 우리는 BufferedReaderProcessor는 Function<T, R> 형식의 함수형 인터페이스를 기대하는 API를 사용하고 있으며
             * 직접 함수형 인터페이스를 만들기 어려운 상황이다. 이런 상황에서는 아래 코드와 같이 명시적으로 확인된 예외를 잡을 수 있다.
             */
            Function<BufferedReader, String> f = (BufferedReader br) -> {
                try {
                    /*
                     * 강제로 Exception을 확인하고 싶다면 아래 return을 주석처리하고 
                     * throw new IOException(); 부분을 주석을 해제 한다.
                     */
                    // throw new IOException();
                    return br.readLine();
                } catch (IOException e) {
                    System.out.println("!!!!! 강제로 Exception이 발생 하였습니다. !!!!!");
                    throw new RuntimeException(e);
                }
            };
    
            System.out.println("try-catch Function : " + f.apply(new BufferedReader(getExmpFile()) ));
    
        }
    
    }

    3.4 함수형 인터페이스 사용

    Main

    package Part1.Chapter3.Chapter3_3_4;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    import java.util.function.Consumer;
    import java.util.function.Function;
    import java.util.function.IntPredicate;
    import java.util.function.Predicate;
    
    /*
     * 3.4 함수형 인터페이스 사용.
     * 
     * 함수형 인터페이스는 오직 하나의 추상 메서드를 지정한다.
     * 함수형 인터페이스의 추상 메서드는 람다 표현식의 시그너처를 묘사하고 추상 메서드 시그너처를 함수 디스크립터(function descriptor)라고 한다.
     * 다양한 람다 표현식을 사용하려면 공통의 함수 디스크립터를 기술하는 함수형 인터페이스 집합이 필요하다.
     */
    public class Main_3_4 {
    
        /*
         * 3.4.1 Predicate
         * 
         * java.util.function.Predicate<T> 인터페이스는 test라는 추상 메서드를 정의하며
         * test는 제네릭 형식 T의 객체를 인수로 받아 boolean을 반환한다.
         */
        public static <T> List<T> filter(List<T> list, Predicate<T> p) {
            List<T> result = new ArrayList<>();
    
            for(T s : list) {
                if(p.test(s)) {
                    result.add(s);
                }
            }
    
            return result;
        }
    
        /*
         * 3.4.2 Consumer
         * 
         * java.util.function.Consumer<T> 인터페이스는 제네릭 형식 T 객체를 받아서, void를 반환하는 accept라는 추상 메서드를 정의한다.
         * T 형식의 객체를 인수로 받아서 어떤 동작을 수행하고 싶을 때 Consumer 인터페이스를 사용할 수 있다.
         */
        public static <T> void forEach(List<T> list, Consumer<T> c) {
            for(T s : list) {
                c.accept(s);
            }
        }
    
        /*
         * 3.4.3 Function
         * 
         * java.util.function.Function<T, R>인터페이스는 제네릭 형식 T를 인수로 받아서 제네릭 형식 R 객체를 반환하는 Apple라는 추상 메서드를 정의한다.
         * 입력을 출력으로 매핑하는 람다를 정의할 때 Function 인터페이스를 활용할 수 있다.
         * 
         * 아래 예제는 String 리스트를 인수로 받아 각 String의 길이를 포함하는 Intger 리스트로 반환하는 예 이다.
         */
        public static <T, R> List<R> map(List<T> list, Function<T, R> f) {
            List<R> result = new ArrayList<>();
    
            for(T s : list) {
                result.add(f.apply(s));
            }
    
            return result;
        }
    
        public static void main(String[] args) {
            /*
             * 3.4.1 Predicate
             * Predicate의 test 메서드 시그너쳐는 인수로 제너릭 형식을 받고 반환값을 boolean으로 반환한다.
             * 해당 시그너처와 동일하게 
             *         (String s) - 인수는 제너릭 형식이기 때문에 어떤 형태로 인수를 전달하여도 상관없다.
             *         !s.isEmpty - boolean으로 반환값을 하였기 때문에 test 메서드 시그너처와 동일하다.
             * 하였기 때문에 람다 표현식으로 올바른 표현식이다. 
             */
            Predicate<String> nonEmptyStrPredicate = (String s) -> !s.isEmpty();        
            System.out.println("Predicate : " + filter(Arrays.asList("kim", "", "sung", "wook", "") , nonEmptyStrPredicate));
    
            /*
             * 3.4.2 Consumer
             */
            Consumer<String> nameStrConsumer = (String s) -> System.out.println("Consumer : " + s);
            forEach(Arrays.asList("kim", "sung", "wook"), nameStrConsumer);
    
            /*
             * 3.4.3 Function
             */
            Function<String, Integer> strLenFunction = (String s) -> s.length();
            System.out.println("Function : " 
                    + map(Arrays.asList("kim", "sung", "wook", "kim sung", "kim sung wook"), strLenFunction));
    
            /*
             * 기본형 특화
             * 
             * 자바의 모든 형식은 참조형(reference type - Byte, Integer, Object, List 등) 아니면 기본형(primitive type - int, double, byte, char 등)
             * 에 해당한다. 하지만 제네릭 파라미터는 제네릭의 내부 구현 때문에 참조형만 사용할 수 있다.
             * 자바에서는 기본형을 참조형으로 변환할 수 있는 기능을 제공한다. 이 기능을 박싱(boxing)이라고 한다.
             * 반대로 참조형을 기본형으로 변환하는 반대 동작을 언박싱(unboxing)이라고 한다.
             * 그리고 프로그래머가 편리하게 코드를 구현할 수 있도록 박싱과 언방식으로 자동으로 해주는 오토박싱(autoboxing)이라는 기능도 제공한다.
             */
    
            /*
             * int가 Integer로 박싱이 되는 예제이다.
             * 하지만 이런 변환 과정은 비용이 소모된다. 박싱한 값은 기본형을 감싸는 래퍼며 힙에 저장된다.
             * 따라서 박싱한 값은 메모리를 더 소비하며 기본형을 가져올 때도 메모리를 탐색하는 과정이 필요하다.
             */
            List<Integer> intList = new ArrayList<>();
            for(int i = 0; i < 5; i++) {
                intList.add(i);
            }
    
            intList.stream()
                .forEach((Integer i) -> System.out.println("int -> Integer Boxing : " + i));
    
            /*
             * 자바 8에서는 기본형을 입출력으로 사용하는 상황에서 오토박싱 동작을 피할 수 있도록
             * 특별한 함수형 인터페이스를 제공한다.
             * 
             * 아래 예제는 IntPredicate는 1000이라는 값을 박싱하지 않지만, Predicate<Integer> 중 인수가 1000인 값은
             * Integer 객체로 박싱한다. 
             */
            IntPredicate evenNumbers = (int i) -> i % 2 == 0;
            System.out.println("IntPredicate : " + evenNumbers.test(1000));
    
            Predicate<Integer> oddNumbers = (Integer i) -> i % 2 == 1;
            System.out.println("boxing - Predicate<Integer> : " + oddNumbers.test(1000));
            System.out.println("not boxing - Predicate<Integer> : " + oddNumbers.test(new Integer(1001)));
    
            /*
             * 일반적으로 특정 형식을 입력으로 받는 함수형 인터페이스의 이름 앞에서는 DoublePredicate, IntConsumer,
             * LongBinaryOperator, IntFunction처럼 형식명이 붙는다.
             * Function 인터페이스는 ToIntFunction<T>, IntToDoubleFunction 등의 다양한 출력 형식 파라미터를 제공한다.
             * 
             * 자바 8의 대표적인 함수형 인터페이스
             * 함수형 인터페이스 : Predicate<T>
             *        함수 디스크립터 : T -> boolean
             *        기본형 특화 : 
             *            IntPredicate, LongPredicate, DoublePredicate
             *
             * 함수형 인터페이스 : Consumer<T>
             *         함수 디스크립터 : T -> void
             *         기본형 특화 :
             *             IntConsumer, LongConsumer, DoubleConsumer
             * 
             * 함수형 인터페이스 : Function<T, R> 
             *         함수 디스크립터 : T -> R
             *         기본형 특화 :
             *             IntFunction<R>, IntToDoubleFunction, IntToLongFunction, 
             *             LongFunction<R>, LongToDoubleFunction, LongToIntFunction,
             *             DoubleFunction<R>, ToIntFunction<T>, ToDoubleFunction<T>,
             *             ToLongFunction<T>
             * 
             * 함수형 인터페이스 : Supplier<T>
             *         함수 디스크립터 : () -> T
             *         기본형 특화 :
             *             BooleanSupplier, IntSupplier, LongSupplier, DoubleSupplier             
             * 
             * 함수형 인터페이스 : UnaryOperator<T>
             *         함수 디스크립터 : T -> T 
             *         기본형 특화 :
             *             IntUnaryOperator, LongUnaryOperator, DoubleUnaryOperator
             * 
             * 함수형 인터페이스 : BinaryOperator<T>
             *         함수 디스크립터 : (T, T) -> T
             *         기본형 특화 :
             *             IntBinaryOperator, LongBinaryOperator, DoubleBinaryOperator
             * 
             * 함수형 인터페이스 : BiPredicate<L, R>
             *         함수 디스크립터 : (L, R) -> boolean
             *         기본형 특화 :         
             * 
             * 함수형 인터페이스 : BiConsumer<T, U> 
             *         함수 디스크립터 : (T, U) -> void
             *         기본형 특화 :
             *             ObjIntConsumer<T>, ObjLongConsumer<T>, ObjDoubleConsumer<T>
             * 
             * 함수형 인터페이스 : BiFunction<T, U, R>
             *         함수 디스크립터 : (T, U) -> R 
             *         기본형 특화 :
             *             ToIntBiFunction<T, U>, ToLongBiFunction<T, U>, ToDoubleBiFunction<T, U>
             * 
             * 사용 사례, 람다 예제, 사용할 수 있는 함수형 인터페이스 등을 총체적으로 제공한다.
             * 람다와 함수형 인터페이스 예제
             * 사용 사례
             *         불린 표현
             *             람다 예제 : (List<String> list) -> list.isEmpty()
             *             대응하는 함수형 인터페이스 : Predicate<List<String>>
             *         객체 생성
             *             람다 예제 : () -> new AppleEntity(10)
             *             대응하는 함수형 인터페이스 : Supplier<AppleEntity>
             *         객체에서 소비
             *             람다 예제 : (AppleEntity appleEntity) -> System.out.println(a.getWeight())
             *             대응하는 함수형 인터페이스 : Consumer<AppleEntity>
             *         객체에서 선택/추출
             *             람다 예제 : (String s) -> s.length()
             *             대응하는 함수형 인터페이스 : Function<String, Integer> 또는 ToIntFunction<String>
             *         두 값 조합
             *             람다 예제 : (int a, int b) -> a * b
             *             대응하는 함수형 인터페이스 : IntBinaryOperator
             *         두 객체 비교
             *             람다 예제 : (AppleEntity a1, AppleEntity a2) -> a1.getWeight().compareTo(a2.getWeight())
             *             대응하는 함수형 인터페이스 : Comparator<AppleEntity> 또는 
             *                 BiFunction<AppleEntity, AppleEntity, Integer> 또는
             *                 ToIntBiFunction<AppleEntity, AppleEntity>
             */
    
        }
    
    }

    람다 표현식

    • 동작 파라미터화를 이용해서 변화하는 요구사항에 효과적으로 대응하는 코드를 구현할 수 있음을 확인 했다.
      • 또한 정의한 코드 블록을 다른 메서드로 전달할 수 있고,
      • 정의한 코드 블록을 특정 이벤트(예를 들면 마우스 클릭)가 발생할 때 실행 되도록 설정 하거나
      • 알고리즘의 일부(150그램 이상의 사과와 같은 프레디게이트)로 실행되도록 설정할 수 있다. 따라서 동작 파라미터화를 이용하면 더 유연하고 재사용할 수 있는 코드를 만들 수 있다.
    • 익명 클래스로 다양한 동작을 구현할 수 있지만 만족할 만큼 코드가 깔끔하지 않다. 깔끔하지 않는 코드는 동작 파라미터를 실전에 적용하는 것을 막는 요소다.
    • 람다 표현식은 익명 클래스처럼 이름이 없는 함수면서 메서드를 인수로 전달할 수 있으므로 일단은 람다 표현식이 익명 클래스와 비슷한 것이라고 생각하자.

    요약

    • 람다 표현식은 익명 함수의 일종이다. 이름은 없지만, 파라미터 리스트, 바디, 반환 형식을 가지며 예외를 던질 수 있다.

    • 람다 표현식은 간결한 코드를 구현할 수 있다.

    • 함수형 인터페이스는 하나의 추상 메서드만을 정의하는 인터페이스다.

    • 함수형 인터페이스를 기대하는 곳에서만 람다 표현식을 사용할 수 있다.

    • 람다 표현식을 이용해서 함수형 인터페이스의 추상 메서드를 즉석으로 제공할 수 있으며 "람다 표현식 전체가 함수형 인터페이스의 인스턴스로 취급된다."

    • java.util.function 패키지는

      • Predicate
      • Function<T, R>
      • Supplier
      • Consumer
      • BinaryOperator

      등을 포함해서 자주 사용하는 다양한 함수형 인터페이스를 제공한다.

    • 자바 8은 Predicate와 Function<T, R>같은 제네릭 함수형 인터페이스와 관련한 박싱 동작을피할 수 있도록 IntPredicate, IntToLongFunction 등과 같은 기본형 특화 인터페이스도 제공한다.

    • 실행 어라운드 패턴(예를 들면 자원 할당, 자원 정리 등 코드 중간에 실행해야 하는 메서드에 꼭 필요한 코드)을 람다와 활용하면 유연성과 재사용성을 추가로 얻을 수 있다.

    • 람다 표현식의 기대 형식을 "대상 형식"이라고 한다.

    • 메서드 레퍼런스를 이용하면 기존의 메서드 구현을 재사용하고 직접 전달할 수 있다.

    • Comparator, Predicate, Function 같은 함수형 인터페이스는 람다 표현식을 조합할 수 있는 다양한 디폴트 메서드를 제공한다.

    반응형

    댓글

Designed by Tistory.