mm Home

Java 9 features 본문

개발/Java

Java 9 features

jess_m 2017. 11. 10. 16:24

최근 발표된 Java 9의 새로운 피쳐들에 대해 많은 글들이 올라온다. 

능력은 안되지만 나도 나름대로 공부한걸 정리해보았다.




 

try-with-resources

  • java 7 에서 추가되었던 try-with-resources 구문을 더 간결하게 사용할 수 있다.
  • try-with-resources : AutoCloseable 를 구현한 클래스에 대해 try 구문이 끝날때 자동으로 close 처리해주는 기능
  • 변경된 내용

    //java 9 이하
    try (ByteInputStream b = new ByteInputStream()) {
    } catch (Exception e) {
    }
      
    //java 9 
    try (a;
         b) {        //이전과 동일하게 다수의 자원 선언 가능.
    } catch (Exception e) {
     
    }
  • 추가 사항
    • try-with-resources 는 동작은 아래와 같다.
    • try(ByteInputStream b = new ByteInputStream()) {
          //code
      } catch(Exception e) {       //optional
      } finally {                  //optional
      }
        
      //위의 코드를 translation 하면 아래와 같다.
      //개인적으로 좀 간략하게 표현한 것. 더 구체적으로 보려면 java spec을 참고할 것.
        
      try {
          final ByteInputStream resource = new ByteInputStream();
          Throwable #primaryExc = null;   //try-with-resources 구문에서 자동으로 생성. Exception 처리용
        
          try {
              //code
          } catch(Throwable #t) {
              #primaryExc = #t;
              throw #t;
          } finally {
              if(resource != null) {
                  if(#primaryExc != null) {   //사용자가 지정한 코드(Block)이 실행하다 예외가 발생했다면 자원을 close처리 하고, 예외를 정보를 더함.
                      try{
                          resource.close();
                      } catch(Throwable #suppressedExc) {
                          //실제 코드를 사용하며 발생된 Exception이 개발자 입장에서 주(primary)예외임(#primaryExc). 
                          //따라서 close()를 발생시키다 발생된 예외는 sub Exception임(#suppresedExc). 주 예외에 sub 예외를 더함.
                          #primaryExc.addSuppressed(#suppresedExc); 
                      }
                  } else {    //발생된 예외가 없다면, 자원을 close 처리함.
                      resource.close();
                  }
              }
          }
        
      } catch(Exception e) {       //optional
      } finally {                  //optional
      }
        
  • 조금 더 쉽게 표현하면
    • try 구문이 실행되고 나서 바로 close 처리를 진행한다.
    • try 구문에서든 close() 메소드 안에서 에러가 발생했다면 catch() 로직을 타게 된다.
    • try → close() → catch() → finally 순.
    • 따라서 close()에서 에러가 발생할 수 있는 가능성이 있다면 catch나 finally에서 예외처리를 해주어야 할 것이다.

 

 

 


 

interface에 private 접근자 사용 가능

  • java 8 에서 인터페이스에 대해 default와 static 메소드 선언이 가능해짐.
  • 추상화를 하는 과정중에 default와 static 메소드를 캡슐화할 필요가 생겼기 때문에 java 9에서는 private 접근자가 가능해지도록 변경함.

 

 



 

immutable collection factory 추가

  • java 9 미만에서 Collection 에 대해서 immutable 하려면 Collections(java8 util)을 사용하거나 Third-party 쓰거나 직접 구현하거나... 하여튼 사용에 불편했음
  • immutable Collection Factory 추가
  •         Map<String, Object> immutableMap = Map.of("test", "value");
     
            List<String> immutableList = List.of("1","2");
            immutableList.add("3");         // runtime error ... 컴파일단계에서 잡히지 않음
      

 



 

Java 9 Diamond Operator for Anonymous Inner Classes

  • 익명클래스에 대한 타입 추론 가능
  • public abstract class MyHandler<T> {
     
        abstract void handle();
    }
     
    private void test() {
     
        //java 8에서 컴파일 에러 <> 안에 Integer 선언 해야함.. java 9 에서 정상 작동
        MyHandler<Integer> intHandler = new MyHandler<>() {
            public void handle() {
            }
        };
         
    }

 



 

Optional Class imporvement

  • or() 메소드 추가 : value가 비어있을 때 or 메소드를 실행한 결과의 Optional을 돌려줌 (lazy)
  • Optional<String> result = value.or(() -> defaultValue);
  • ifPresentOrElse() 메소드 추가 : value에 값이 있을 때 특정 행동을 하고 싶다면..
  • Optional<Integer> value = Optional.of(1);
    value.ifPresentOrElse( v -> v++; );
  • Stream() 메소드 추가 : Optional 클래스도 Stream 처리가 가능해짐.
  • Optional<Integer> a = Optional.of(4);
    List<Integer> b = List.of(1,2,3);
     
    List<Integer> collect = Stream.concat(a.stream(), b.stream()).filter(i -> i>2).collect(Collectors.toList());

 

 

 


 

Stream API Improvements

  • takeWhile/dropWhile 추가
    • takeWhile : Predicate가 만족하는 때까지만 순회하여 요소들만 take함.
    • dropWhile : Predicate가 만족할 때까지 순회하여 drop함. (순회한 subset을 drop)
  • iterate 메소드 추가
    • 기존에 있던 iterate 는 무한한 iterate
    • Stream.iterate(0, i -> i < 10, i -> i + 1)
        .forEach(System.out::println); //스트림 생성 자체를 유한하게 생성할 수 있음
        
      Stream.iterate(1, i -> i++).limit(1); // 기존에는 이런식으로 사용함
  • ofNullable 메소드 추가
    • 스트림의 요소에 null이 포함되지 않기를 바라는 경우에, ofNullable을 사용할 수 있다. 값이 비어있다면 Optional.empty() 반환.
    • collection.stream()
        .flatMap(s -> Stream.ofNullable(map.get(s)))
        .collect(Collectors.toList());
        
      //스트림은 null 값을 가질 수 있다.
      //위의 코드는 null 값을 제외하여 collect 하는 코드이다. ofNullable로 null체크 하는 부분을 없앨 수 있다.

 




 

@Deprecated Improvements

  • 기존 Deprecated는 컴파일할 때 warning 경고만 해주고 언제부터 시작됐는지, 앞으로 삭제될 예정인지에 대한 명세가 없었음.
  • since(언제부터)와 forRemoval(이후에 삭제될 예정인지)이라는 변수 추가.

 

 



 

 

Java 9 REPL(Read Evaluate Print Loop) : JShell

 

 

 

 


 

HTTP 2 Client

  • HTTP/2.0 을 지원하는 HttpClient 클래스가 추가됨.
  • sendAsync 메소드를 통해 CompletableFuture를 반환하는 비동기 개발도 가능.
  • 간략히 HTTP/2.0 의 장점을 보면
    • Multiplexed Stream : 한 커넥션으로 동시에 여러개의 메시지를 주고 받을 수 있음. (응답도 순서에 상관없이 받는다.) - HTTP/1.1의 Keep-Alive, Pipelining 의 개선
    • Stream Prioritization : 특정 자원의 수신이 늦어질 경우, 의존성있는 자원도 렌더링이 늦어지는 문제가 발생할 수 있다. 2.0에서 자원간 의존관계를 설정하여 해결할 수 있음.
    • Server Push : 클라이언트의 요청없이도 서버는 자원을 마음대로 보내줄 수 있다.
    • Header Compression : HTTP 특성상 헤더가 중복적으로 전송하는 경우가 많다. 여러 요청에 대해 중복값이 존재하는 경우 압축을 통해 성능을 향상시킴
  • try {
        HttpClient httpClient = HttpClient.newHttpClient(); //Create a HttpClient
        System.out.println(httpClient.version());
     
        HttpRequest httpRequest = HttpRequest.newBuilder().uri(new URI("https://www.daum.net/")).GET().build(); //Create a GET request for the given URI
        HttpResponse < String > httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandler.asString());
     
        System.out.println(httpResponse.statusCode());
        System.out.println(httpResponse.body());
    } catch (Exception e) {
        System.out.println("message " + e);
    }

 

 

 

 

 

Process API Improvements

 

 

 

 

 

 

Unified JVM Logging

  • 로깅을 하기 위한 최소한의 로깅 API와 해당 메시지의 사용자를위한 서비스 인터페이스를 제공한다.
  • 기존에 로깅을 이용하려한다면 logback이나 log4j 를 클래스패스내에 두어야 했다. (slf4j 의존성도 추가해야함.. 귀찮)
    • Java 9에서 특정 로깅 구현체가 존재하지 않는다면, JDK의 기본 로거가 사용된다. 
  • 실제 서비스에서 사용할 용도는 아닌것 같다.
  • private static System.Logger LOGGER1 = System.getLogger("MyLogger");
    private static System.Logger LOGGER2 = System.getLogger("MyNewLogger");
     
    public static void test() {
        LOGGER1.log(System.Logger.Level.ERROR, "This is just an Error Log test.");
        LOGGER1.log(System.Logger.Level.INFO, "Hello World!");
     
        LOGGER2.log(System.Logger.Level.ERROR, "This is just an Error Log test.");
        LOGGER2.log(System.Logger.Level.INFO, "Hello World!");
    }

 

 



 

 

CompletableFuture API Improvements

  • java 8 에서 비동기지원을 위해 CompletableFuture가 도입되었었다. 
    • 비동기 개발시에 불편함(callback-hell). callback 을 통한 비동기 지원의 편이성을 위해 도입.
    • method chaining 으로 콜백 등록, 예외처리 등을 편리하게 개발 가능
  • delay와 timeout에 대한 강화
    • CompletableFuture 완료를 특정 시간만큼 지연시킨다던지, 제한시간을 두도록 할 수 있음
    •   
      CompletableFuture<Object> future = new CompletableFuture<>();
      future.completeAsync(() -> input, CompletableFuture.delayedExecutor(1, TimeUnit.SECONDS));
        
      CompletableFuture<Object> future = new CompletableFuture<>();
      future.orTimeout(1, TimeUnit.SECONDS);
  • defaultExecutor()
    • 비동기 처리를 위한 default Executor 생성.
  • newIncompleteFuture() 
    • 기존의 서브 클래스를 유지한, 새로운 CompletableFuture 를 생성해서 되돌려줌. (주로 CompletableFututure 내부적으로 사용됨)
  • copy() 
    • 새로운 CompletableFututure 를 리턴한다. 
  • minimalCompletionStage()
    • 기존과 같은 동작을 하는 새로운 CompletionStage를 반환하지만, 결과를 가져오려고 하면 UnsupportedOperationException 예외를 던짐.
  • completeAsync ()
  • orTimeout ()
  • completeOnTimeout ()
  • 유틸리티 메소드도 추가됨
    • Executor delayedExecutor(long delay, TimeUnit unit, Executor executor)
    • Executor delayedExecutor(long delay, TimeUnit unit) 
    • <U> CompletionStage<U> completedStage(U value) 
    • <U> CompletionStage<U> failedStage(Throwable ex) 
    • <U> CompletableFuture<U> failedFuture(Throwable ex)

 

 

 

 

 

 

 

 

Java Platform Module System (Jigsaw project)

  • 기존 자바의 라이브러리 문제점
    • classpath내에 있다면 모든 public 클래스는 접근 가능함.
      • public API의 무분별한 사용이 일어남.
    • classpath 내의 의존성 관리가 어려움
      • 필요한 라이브러리가 이미 있는지, 중복된 항목은 얼마나 잇는지 파악하기 어려움
    • 특정 버전에 대한 선택 문제
      • ex.    A클래스는 특정 라이브러리의 v1 을 바라보아야 하고, B클래스는 v2를 바라보아야 하는 경우
    • 성능 문제
      • Hadoop 을 예로 살펴보면.
      • 110 개의 jar가 있음.
      • public 클래스를 로드하기위해 클래스 로더는 110개의 jar를 다 찾아야 한다. 110개의 jar는 클래스간 12,000 개 이상의 interaction이 있다.
      • 모두 뒤지면서 찾으려면 당연히 성능이 안좋다.
  • 자바 9에서는?
    • JDK, JRE, JAR 등을 더 작은 모듈 단위로 나눔.
      • 프로젝트마다 모듈 구성을 위한 설정을 할 수 있음.
    • 모듈간 public 접근에 대한 제한
      • 모든 클래스 단위의 제한을 두는 것이 아니라, 패키지 단위의 접근 컨트롤을 한다.
      • 원하지 않는 로직 내부를 감출 수 있음 → 캡슐화!
      • 모듈간 종속성도 표현 가능
    • 성능 개선
    • 단일 책임 (SRP)
    • 아쉽게도 특정 버젼에 대한 선택문제는 해결되지 않았다.
      • JVM은 클래스 로더에 의해 클래스들을 로드 하는데, 버전에 따른 로드를 하는게 불가능함.

 

  • 모듈은 어떻게??
    • 패키지의 상위 디렉토리에 module-info.java 라는 파일을 생성해 모듈 설정을 해야한다.
      • 해당 모듈의 이름을 설정한다
      • 설정 내부에 접근 가능한 패키지는 export       (export 선언이 되어있지 않은 패키지는 모두 접근 불가능함)
      • 설정에 해당 모듈이 필요로 하는 패키지는 requires 로 필요 패키지를 명시적으로 선언한다.
      • module java.nine.first {
         
            exports com.jess.test;
         
            requires java.base;
            requires jdk.incubator.httpclient;
        }
  • JVM 경량화
    • 임베디드 시스템같이 자원이 제한적인 상황에서 JVM의 경량화가 가능해짐.
    • JDK도 모듈들로 이루어져 있다. 여기서 필요한 모듈만 사용할 수 있다.
    • 새로 추가된 jlink 를 통해 런타임 이미지를 생성할 수 있음.
      • 실행에 필요한 것들을 패키징 하는 것. (JDK 전체를 다운받을 필요가 없다)
      • JRE에서 jlink를 실행.
    Modular Jar
    • 기존 라이브러리로 jar를 매우 많이 사용 중. 
    • 컴파일된 module-info.class를 포함하는 것 빼곤 동일하다
      • 있으면 Modular Jar.
      • 없으면 비모듈 Jar   (기존에 쓰던 일반 Jar) 
    • 하위 버전과 호환된다. (module-info.class가 이전 JVM에서 무시됨)
  • 모듈과 비모듈을 어떻게 구분?  
    • 모듈은 애플리케이션 module-path에 존재하고, 
    • 비모듈은 기존과 동일하게 classpath에 존재한다.
  • cli 로 모듈 빌드하려니 너무 빡셈.. 

 

 





Reactive Streams API 추가


여기 참고









개인적으로 Java 9는 Java 8만큼의 큰 메리트는 없는 것 같았다. 

Module 시스템도 웹 어플리케이션 개발자 입장에서는 큰 관심이 생기지 않았다, 기존의 라이브러리가 가지는 문제에 완전한 해결책은 아닌것 같다라는 생각이 들기도 했고. 

Stream API를 공식 지원하는게 개발자로서 그나마 관심이 가지는 부분이었다. 다만 이마저도 스프링에서 추가된 Reactor 가 내부적으로 Java 9의 Stream API를 사용하고 있지 않고 범용성면에서 훨씬 더 유용하다는 느낌이다. 스프링5를 쓰면 Java 9의 Stream API를 많이 쓸것 같지 않았다. 



'개발 > Java' 카테고리의 다른 글

Java - Generics  (0) 2019.12.04
Java - static  (0) 2017.09.06
Java 8 - Stream  (0) 2017.08.11
Java 8 - Lambda Translation  (0) 2017.08.10
Java 8 - 람다 표현식  (2) 2017.08.10
Comments