ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Part1] Java8 In action - Chapter2 - 2
    Java8 In Action 2022. 7. 27. 10:58
    반응형

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

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

    2.3 복잡한 과정 간소화

    자바는 클래스의 선언과 인스턴스화를 동시에 수행할 수 있도록 익명 클래스(anonymous class)라는 기법을 제공한다.

    Entity

    AppleEntity.java

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

    Interface

    ApplePredicate.java

    package Part1.Chapter2.Chapter2_2_3.filters.inter;
    
    import Part1.Chapter2.Chapter2_2_3.entity.AppleEntity;
    
    public interface ApplePredicate {
        boolean test(AppleEntity appleEntity);
    }

    Predicate.java

    package Part1.Chapter2.Chapter2_2_3.filters.inter;
    
    public interface Predicate<T> {
        boolean test(T t);
    }

    Main

    package Part1.Chapter2.Chapter2_2_3;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    import Part1.Chapter2.Chapter2_2_3.entity.AppleEntity;
    import Part1.Chapter2.Chapter2_2_3.filters.inter.ApplePredicate;
    import Part1.Chapter2.Chapter2_2_3.filters.inter.Predicate;
    
    /*
     * 2.3.1 익명 클래스
     * 
     * 익명 클래스는 자바의 지역 클래스(local class)와 비슷한 개념이다.
     * 말그대로 이름이 없는 클래스다.
     * 익명 클래스를 이용하면 클래스 선언과 인스턴스화를 동시에 할 수 있다.
     * 즉, 즉석에서 필요한 구현을 만들어서 사용할 수 있다.
     */
    public class Main_2_3 {
    
        public static List<AppleEntity> filterApples(List<AppleEntity> inventory, ApplePredicate p) {
            List<AppleEntity> result = new ArrayList<>();
    
            for(AppleEntity apple : inventory) {
                if(p.test(apple)) {
                    result.add(apple);
                }
            }
    
            return result;
        }
    
        /*
         * 이게 진짜 끝판왕이다. 자세한 설명은 생략..이 아니라 main 메서드를 참고하도록 한다.
         */
        public static <T> List<T> filter(List<T> list, Predicate<T> p) {
            List<T> result = new ArrayList<>();
    
            for(T e : list) {
                if(p.test(e)) {
                    result.add(e);
                }
            }
    
            return result;
        }
    
        public static void main(String[] args) {
            List<AppleEntity> inventory = Arrays.asList(new AppleEntity("green", 100)
                    , new AppleEntity("red", 120)
                    , new AppleEntity("green", 150));
    
            /*
             * 2.3.2 다섯 번째 시도 : 익명 클래스 사용(이 정도까지 했으면 그냥 내가 사장 하는게 나을듯 싶다...)
             * 
             * 익명 클래스로도 아직 부족한 점이 있다.(저자는 언제쯤 만족할건지 궁금하다..)
             * 1. 객체 생성 구문과 메서드 작성 부분 때문에 코드가 많은 공간을 차지한다.
             * 2. 많은 프로그래머가 익명 클래스의 사용에 익숙하지 않다.
             * 
             * 코드의 장황함(verbosity)은 나쁜 특성이다. 
             * 장황한 코드는 구현하고 유지보수하는데 시간이 오래 걸린다.
             * 한눈에 이해할 수 있는 코드가 좋은 코드다.(당연한 말인거 같은데..)
             */
            filterApples(inventory, new ApplePredicate() { // ApplePredicate는 인터페이스이다.
                @Override
                public boolean test(AppleEntity appleEntity) {
                    return "red".equals(appleEntity.getColor());
                }
            })
            .stream()
            .forEach((AppleEntity apple) -> System.out.println("[Anonymous Class] apple Color : " + apple.getColor()
                + ", apple weight : " + apple.getWeight() ));
    
            /*
             * 2.3.3 여섯 번째 시도 : 람다 표현식 사용
             * 
             * 람다 표현식을 사용하면 아래와 같이 간단(?)하게 재구현 할 수 있다.
             */
            filterApples(inventory, (AppleEntity apple) -> "red".equals(apple.getColor()) )
                .stream()
                .forEach((AppleEntity apple) -> System.out.println("[Lambda] apple Color : " + apple.getColor()
                    + ", apple weight : " + apple.getWeight() ));
    
            /*
             * 2.3.4 일곱 번째 시도 : 리스트 형식으로 추상화(여기까지 온거면 볼 장 다 본거 같다..)
             * 
             * 현재 filterApples는 Apple과 관련된 동작만 수행한다.
             * 하지만 Apple 이외의 다양한 물건에서 필터링이 작동하도록 리스트 형식을 추상화할 수 있다.
             * (왠지 느낌이 이번이 진짜 끝판왕 같다.. 레알루다가..)
             */
            filter(inventory, (AppleEntity apple) -> "red".equals(apple.getColor()) )
                .stream()
                .forEach((AppleEntity apple) -> System.out.println("[Last Boss] apple Color : " + apple.getColor()
                    + ", apple weight : " + apple.getWeight() ));
    
            filter(Arrays.asList(0, 1, 2, 3, 4, 5), (Integer i) -> i % 2 == 0 )
                .stream()
                .forEach((Integer i) -> System.out.println("[Last Boss] even number : " + i));
        }
    
    }

    2.4 실전 예제

    • 지금까지 동작 파라미터화가 변화하는 요구사항에 쉽게 적응하는 유용한 패턴임을 확인했다. 동작 파라미터화 패턴은 동작을(한 조각의 코드로) 캡슐화한 다음에 메서드로 전달해서 메서드의 동작을 파라미터화한다.
    • 자바 API의 많은 메서드를 다양한 동작으로 파라미터화 할 수 있다. 또한 이들 메서드를 익명 클래스와 자주 사용하기도 한다.

    Entity

    AppleEntity.java

    package Part1.Chapter2.Chapter2_2_4.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(int weight) {
            this.weight = weight;
        }    
    
    }

    Main

    package Part1.Chapter2.Chapter2_2_4;
    
    import java.util.Arrays;
    import java.util.Comparator;
    import java.util.List;
    import java.util.stream.IntStream;
    
    import Part1.Chapter2.Chapter2_2_4.entity.AppleEntity;
    
    public class Main_2_4 {
        /*
         * 2.4.1 Comparator로 정렬하기.
         * 
         * 우리의 갑님 농부님께서 무게를 기준으로 목록에서 정렬하고 싶다고 하셨다.
         * 그러나 갈대 같은 마음을 가진 농부님께서는 색을 기준으로 사과를 정렬하고 싶다고 하실거 같기도 하다.
         * 일상에서 흔히 일어나는 일이지 않는가?(....뒤..ㅈ...)
         * 
         * 그러나 걱정하지 말아라 자바에서는 java.util.Comparator 객체를 이용해서 sort의
         * 동작을 파라미터화할 수 있다. 
         */    
        static void appleCompare(List<AppleEntity> inventory) {
            /*
             * 아래와 같이 Comparator를 구현해서 sort 메서드의 동작을 다양화할 수 있다.
             * 아래 코드는 익명 클래스를 이용해서 무게가 적은 순으로 목록에서 사과를 정렬한다.
             */
            inventory.sort(new Comparator<AppleEntity>() {
    
                @Override
                public int compare(AppleEntity o1, AppleEntity o2) {
                    return o1.getWeight().compareTo(o2.getWeight());
                }            
            });
    
            inventory.stream()
                .forEach((AppleEntity apple) -> System.out.println("[Anonymous Class - Ascending] color : " + apple.getColor() 
                    + ", weight : " + apple.getWeight() ));
    
            /*
             * 아래 코드는 람다표현식으로 무게가 큰 순으로 목록에서 사과를 정렬한다. 
             */
            inventory.sort((AppleEntity a1, AppleEntity a2) -> a2.getWeight().compareTo(a1.getWeight()) );
    
            inventory.stream()
                .forEach((AppleEntity apple) -> System.out.println("[Lambda - Descending] color : " + apple.getColor() 
                    + ", weight : " + apple.getWeight() ));        
        }
    
        /*
         * 2.4.2 Runnable로 코드 블록 실행하기
         * 
         * 자신만의 코드 블록을 수행한다는 점에서 스레드 동작은 경량 프로세스와 비슷하다.
         * 각각의 스레드는 각기 다른 코드를 실행할 수 있다.
         * 나중에 실행될 코드 조각을 어떻게 표현할 수 있는가가 문제다.
         * 
         * 자바에서는 Runnable 인터페이스를 이용해서 실행할 코드 블록을 지정할 수 있다.
         */
        static void runnable() {
            // 익명 클래스.
            new Thread(new Runnable() {
    
                @Override
                public void run() {
                    /*
                     * for(int i = 0; i < 100; i++) {
                     *         System.out.println("Anonymous Class [" + i + "] hello gab nim");
                     * }
                     * 를 람다 형식으로 하면 아래와 같다.
                     */
                    IntStream.range(0, 100).forEach((int i) -> System.out.println("Anonymous Class [" + i + "] hello gab nim") );                
                }
            }).start();
    
            // 람다 표현식.
            new Thread(() -> IntStream.range(0, 100).forEach((int i) -> System.out.println("Lambda [" + i + "] hello gab nim") )).start();
        }
    
        static public void main(String[] args) {
            // 2.4.1 Comparator로 정렬하기.
            appleCompare(Arrays.asList(new AppleEntity("green", 90)
                    , new AppleEntity("green", 150)
                    , new AppleEntity("red", 120)));        
    
            // 2.4.2 Runnable로 코드 블록 실행하기.
            runnable();        
        }
    }

    동작 파라미터화 코드 전달하기

    • 동작 파라미터화(behavior parameterization)란 아직은 어떻게 실행할 것인지 결정하지 않은 코드 블록을 의미.

    • 이 코드 블록은 나중에 프로그램에서 호출한다. 즉, 코드 블록의 실행은 나중으로 미뤄진다. 예로 컬렉션을 처리할 때 다음과 같은 메서드를 구현한다고 가정하자.

      • 리스트의 모든 요소에 '어떤 동작'을 수행할 수 있음.
      • 리스트 관련 작업을 끝낸 다음에 '어떤 다른 동작'을 수행할 수 있음.
      • 에러가 발생하면 '정해진 어떤 다른 동작'을 수행할 수 있음

      같이 동작 파라미터화로 위 따옴표 영역에 다양한 기능을 수행할 수 있다.

    요약

    • 동작 파라미터화에서는 메서드 내부적으로 다양한 동작을 수행할 수 있도록 코드를 메서드 인수로 전달한다.
    • 동작 파라미터화를 이용하면 변화하는 요구사항에 더 잘 대응할 수 있는 코드를 구현할 수 있으며 나중에 엔지니어링 비용을 줄일 수 있다.
    • 코드 전달 기법을 이용하면 동작을 메서드의 인수로 전달할 수 있다. 익명 클래스로도 어느 정도 코드를 깔끔하게 만들 수 있지만 자바 8에서는 인터페이스를 상속받아 여러 클래스를 구현해야 하는 수고를 없앨 수 있는 방법을 제공한다.
    • 자바 API의 많은 메서드는 정렬, 스레드, GUI 처리등을 포함한 다양한 동작으로 파라미터화할 수 있다.
    반응형

    댓글

Designed by Tistory.