자바8

  1. 람다메서드

  2. 메서드 참조

    1. :: 문법을 통해 메서드 참조가 가능하다.

    2. 스태틱 메서드

      boolean isReal = list.stream().anyMatch(u -> User.isRealUser(u));
      //이렇게
      boolean isReal = list.stream().anyMatch(User::isRealUser);
      
    3. object의 메서드

      User user = new User();
      boolean isLegalName = list.stream().anyMatch(user::isLegalName);
      
    4. type의 메서드

      long count = list.stream().filter(String::isEmpty).count();
      
    5. 생성자

      Stream<User> stream = list.stream().map(User::new);
      
      public class MethodReference {
          List<String> withoutMethodReference =
                  cars.stream().map(car -> car.toString())
                          .collect(Collectors.toList());
      }
      
      //이렇게 변경
      public class MethodReference {
          List<String> methodReference = cars.stream().map(Car::toString)
                  .collect(Collectors.toList());
      }
      
  3. 디폴트 메서드

    1. 인터페이스는 기본적으로 메서드 시그니처만 가지고 있어야 하며 구현부를 가지면 안된다.

    2. 인터페이스는 구현한 모든 클래스에서 모든 메서드를 구현해야 한다.

    3. 이 때문에, 나중에 특정 클래스에서만 사용할 메서드를 추가하려 하면 모든 구현 클래스도 동일하게 구현해줘야 하는 문제가 생긴다.

    4. 이 때 default 메서드를 사용하면 해당 인터페이스는 default 메서드를 기본적으로 구현부까지 가지고 있는 셈이기 때문에, 다른 구현 클래스에서 구현해주지 않아도 된다. 추후에 다른 구현 클래스에서 언제든 사용할 수 있으며 오버라이딩도 할 수 있다.

      public class DefaultMethods {
      
          public interface Logging {
              void log(String message);
      
              default void log(String message, Date date) {
                  System.out.println(date.toString() + ": " + message);
              }
          }
      }
      
  4. Optional<T>

    1. NullPointerException(NPE) 을 더 가독성좋게 처리하는 방법

      Optional<String> optional = Optional.empty();
      
      String str = "value";
      Optional<String> optional = Optional.of(str);
      
      Optional<String> optional = Optional.ofNullable(getString());
      
      List<String> list = getList();
      List<String> listOpt = list != null ? list : new ArrayList<>();
      
      List<String> listOpt = getList().orElseGet(() -> new ArrayList<>());
      
    2. 아래 코드에서 User가 사용하는 .map() 함수는 getAdress()의 반환값을 Optional<Address>로 바꾸고, getStreet()의 반환값을 Optional<String>으로 바꿔준다.

    3. 그래서 함수 반환값이 null일 경우 map() 함수는 비어있는 Optional을 반환한다.

      User user = getUser();
      if (user != null) {
          Address address = user.getAddress();
          if (address != null) {
              String street = address.getStreet();
              if (street != null) {
                  return street;
              }
          }
      }
      return "not specified";
      
      Optional<User> user = Optional.ofNullable(getUser());
      String result = user
        .map(User::getAddress)
        .map(Address::getStreet)
        .orElse("not specified");
      
    4. 만약 함수가 Optional<T>를 반환한다면, flatMap() 함수를 사용해야 한다.

      Optional<OptionalUser> optionalUser = Optional.ofNullable(getOptionalUser());
      String result = optionalUser
        .flatMap(OptionalUser::getAddress)
        .flatMap(OptionalAddress::getStreet)
        .orElse("not specified");
      
    5. NPE 처리 방법 개선

      String value = null;
      String result = "";
      try {
          result = value.toUpperCase();
      } catch (NullPointerException exception) {
          throw new CustomException();
      }
      
      //이렇게
      String value = null;
      Optional<String> valueOpt = Optional.ofNullable(value);
      String result = valueOpt.orElseThrow(CustomException::new).toUpperCase();
      
  5. 타입 어노테이션

Type Annotations and Pluggable Type Systems (The Java™ Tutorials >
Learning the Java Language > Annotations)

  1. 지역 변수 정의

    1. @NotNull 어노테이션을 붙인 변수가 컴파일 할 때 null이면 에러 발생
    public class TypeAnnotations {
    
        public static void main(String[] args) {
            @NotNull String userName = args[0];
        }
    }
    
  2. 생성자 호출

    1. 객체가 비어있을 시 에러 발생
    public class TypeAnnotations {
    
        public static void main(String[] args) {
            List<String> request =
                    new @NotEmpty ArrayList<>(Arrays.stream(args).collect(
                            Collectors.toList()));
        }
    }
    
  3. 타입 캐스팅

  4. 제네릭

    1. 해당 List에 있는 모든 String의 포맷을 제한하고자 할 때 사용 (추가 정보 필요)
    public class TypeAnnotations {
    
        public static void main(String[] args) {
            List<@Email String> emails;
        }
    }
    

자바9

  1. Try-with-resources

    1. 기존에는 try - catch 구문안에 사용할 객체를 사용 전에 생성하고 구문이 끝나면 수동으로 해제작업을 해야하지만 이젠 try문의 매개변수로 사용 객체를 생성해줄 수 있다. 구문이 끝나면 자동으로 해제된다.
    2. 구문에서 자동으로 .close() 함수를 실행해주는 것이기 때문에 AutoCloseable 클래스를 구현한 객체에서만 사용할 수 있다.
    public class TryWithResources {
        public static void main(String[] args) {
            BufferedReader br = new BufferedReader(
                    new StringReader("Hello world example!"));
            try {
                System.out.println(br.readLine());
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    //이렇게
    public class TryWithResources {
        public static void main(String[] args) {
            final BufferedReader br3 = new BufferedReader(
                    new StringReader("Hello world example3!"));
            try (BufferedReader reader = br3) {
                System.out.println(reader.readLine());
            } catch (IOException e) {
                System.out.println("Error happened!");
            }
        }
    }
    
    //또는
    MyAutoCloseable mac = new MyAutoCloseable();
    try (mac) {
        // do some stuff with mac
    }
     
    try (new MyAutoCloseable() { }.finalWrapper.finalCloseable) {
       // do some stuff with finalCloseable
    } catch (Exception ex) { }
    
  2. 내부 익명 클래스에 다이아몬드 연산자 적용

    1. <>() { some.. } 처럼 <>를 사용하여 컴파일러 타입 추론을 유도하는 기능은 익명 내부 클래스에서는 사용 불가했으나 가능해졌다.
    public class DiamondOperator {
    
        StringAppender<String> appending = new StringAppender<>() {
            @Override
            public String append(String a, String b) {
                return new StringBuilder(a).append("-").append(b).toString();
            }
        };
        
        public abstract static class StringAppender<T> {
            public abstract T append(String a, String b);
        }
    }
    
    //또는
    FooClass<Integer> fc = new FooClass<>(1) { // anonymous inner class
    };
     
    FooClass<? extends Integer> fc0 = new FooClass<>(1) { 
        // anonymous inner class
    };
     
    FooClass<?> fc1 = new FooClass<>(1) { // anonymous inner class
    };
    
  3. private interface method

    1. default 메서드에서 사용할 복잡한 기능을 분리구현하는 용도로 쓰인다.
    public class PrivateInterfaceMethods {
    
        public static void main(String[] args) {
            TestingNames names = new TestingNames();
            System.out.println(names.fetchInitialData());
        }
    
        public static class TestingNames implements NamesInterface {
            public TestingNames() {
            }
        }
    
        public interface NamesInterface {
            default List<String> fetchInitialData() {
                try (BufferedReader br = new BufferedReader(
                        new InputStreamReader(this.getClass()
                                .getResourceAsStream("/names.txt")))) {
                    return readNames(br);
                } catch (IOException e) {
                    e.printStackTrace();
                    return null;
                }
            }
    
            private List<String> readNames(BufferedReader br)
                    throws IOException {
                ArrayList<String> names = new ArrayList<>();
                String name;
                while ((name = br.readLine()) != null) {
                    names.add(name);
                }
                return names;
            }
        }
    }
    
    //또는
    interface InterfaceWithPrivateMethods {
        private static String staticPrivate() {
            return "static private";
        }
        
        private String instancePrivate() {
            return "instance private";
        }
        
        default void check() {
            String result = staticPrivate();
            InterfaceWithPrivateMethods pvt = new InterfaceWithPrivateMethods() {
                // anonymous class
            };
            result = pvt.instancePrivate();
        }
    }}
    
  4. 불변 Set

    1. Set.of() 를 통해 불변 set 객체를 생성할 수 있다.

    2. 따라서 생성, 추가 등 조작을 가할 경우 UnsupportedOperationException 이 발생한다.

      Set<String> strKeySet = Set.of("key1", "key2", "key3");
      
  5. Stream에 Optional 적용 가능

    List<String> filteredList = listOfOptionals.stream()
      .flatMap(Optional::stream)
      .collect(Collectors.toList());
    
  6. Multi Resolution Image API

    1. 한 개 객체로 여러 해상도를 지원하기위한 API

      BufferedImage[] resolutionVariants = ....
      MultiResolutionImage bmrImage
        = new BaseMultiResolutionImage(baseIndex, resolutionVariants);
      Image testRVImage = bmrImage.getResolutionVariant(16, 16);
      assertSame("Images should be the same", testRVImage, resolutionVariants[3]);