Java

[Java 9] 언어적 변경 : try-with-resources, @SafeVarargs, inner class + diamond Syntax, Interface + private method

확장된 try-with-resources 

  • 자바 7부터 생긴 try-with-resources 구문을 통해 생성한 자원을 정리하지 않는 실수를 막을 수 있고, 코드가 간단해짐
// AS-IS (Java 7 미만)
Resource resource = new Resource();
try {
  ...
} finally {
  resource.close(); // finally 구문으로 인한 코드 복잡도 증가 + 실수로 빼먹을 수 있음
}

// TO-BE (Java 7)
try (Resource resource = new Resource()) { // 자동으로 리소스 close
  ...
}

 

  • 그러나 자바 7의 try-with-resources 구문은 try 외부에서 선언한 자원에 대해서는 적용이 불가했음
  • 자바 9는 try 외부에서 선언한 final 자원 or 생성 후 변경이 없는 자원에 대해서 적용 가능
// AS-IS (Java 7)
Resource resource = new Resource();
try (resource) { // 컴파일 에러
  ...
}

// TO-BE (Java 9)
final Resource r1 = new Resource(); // final
Resource r2 = new Resource(); // 선언 이후 변경 X
try (r1; r2) { // OK
   ...
}

 

 

 

 

@SafeVarargs를 private 메서드에 적용 가능

  • 가변인자가 제네릭과 함께 쓰이면 컴파일 타임에 잡지 못하는 힙 오염(Heap Pollution)으로 인한 런타임 오류의 위험이 있다.
  • 다음 예시와 같이 List<String>[] 형태의 가변인자를 Object[] 에 넣고, Object 원소로 Integer 값을 넣어도 컴파일 타임에 오류가 발생하지 않는다.  
public static void flatten(List<String> ... lists) {
    Object[] = lists; // List<String>[]
    Object[0] = 1; // Integer (힙 오염, 컴파일 오류 X, 런타임 오류 발생)
    ...
}

 

  • 런타임에 힙 오염으로 인한 오류가 발생한다.
  • 이렇듯, 가변인자와 제네릭이 함께 쓰일 때 컴파일러는 기본적으로 경고하는데, 개발자가 해당 메서드가 이러한 경고에서 안전하다고 판단할 때 해당 어노테이션을 붙일 수 있다.
  • 제네릭 가변인자가 안전하기 위해서는 두가지 조건이 필요하다.
    • 가변인자 배열에 아무것도 저장하지 않는다. (메서드 내 변경 불가)
    • 가변인자 배열이 외부에 노출되지 않는다. (참조를 통한 변경 불가)
  • 해당 조건은 재정의 가능한 메서드에 대해서는 성립하지 않으므로 재정의 불가한 메서드에 대해서만 @SaveVarargs를 붙일 수 있다.
  • 자바 8에서는 static, final 메서드에 대해서만 해당 어노테이션을 붙이는 게 가능했다.
  • 자바 9부터는 private 메서드에 대해서도 해당 어노테이션을 붙일 수 있다.

 

 

 

 

inner class + diamond Syntax

  • 자바 9 이전에는 제네릭 클래스인 inner class의 생성 시 우항에 반드시 해당 타입을 명시해주어야 했다.
  • 자바 9 부터는 제네릭 클래스인 inner class 생성 시 우항에 타입을 생략할 수 있는 diamond syntax 적용이 가능하다.
public class Outer {
    public static class InnerClass<T> {
        public InnerClass() {
          
        }
    }
}

// AS-IS (Java 9 미만)
InnerClass<Integer> inner = new InnerClass<Integer>(); // 우항에 타입 명시 필수

// TO-BE (Java 9)
InnerClass<Integer> inner = new InnterClass<>(); // Diamond Syntax 적용 가능

 

 

 

Interface + private method

  • 자바 9 이전에는 인터페이스에 대해 default method는 가능했으나, private method를 정의할 수 없었다. (public만 가능)
  • 이는 코드의 중복 / 캡슐화의 어려움을 야기한다.
    • 두개의 default method가 동일한 코드를 가지게 됨
    • 공통 코드를 메서드로 뺐을 때, 두 default method에 필요한 일부 코드임에도 외부에 public으로 불필요하게 노출되어 캡슐화 어려움
// case 1. 코드 중복
public interface TestInterface {
    
    default void defaultMethod1() {
        int common = 10;
        System.out.println("common logic" + common); // 공통로직 (중복)
        
        System.out.println("default method 1");
    }

    default void defaultMethod2() {
        int common = 10;
        System.out.println("common logic" + common); // 공통로직 (중복)
        
        System.out.println("default method 2");
    }
}

// case 2. 중복 코드 메서드 추출 시 불필요한 외부 노출
public interface TestInterface {

    default void defaultMethod1() {
        commonLogic();

        System.out.println("default method 1");
    }

    default void defaultMethod2() {
        commonLogic();

        System.out.println("default method 2");
    }

    default void commonLogic() { // default method에만 필요한 일부 코드임에도 불필요하게 public 노출
        int common = 10;
        System.out.println("common logic" + common);
    }
}
  • 자바 9 이후부터는 인터페이스에 private method의 정의가 가능해 코드의 중복제거, 재사용성 증대, 캡슐화 증대를 가져올 수 있다.
public interface TestInterface {

    default void defaultMethod1() {
        commonLogic();

        System.out.println("default method 1");
    }

    default void defaultMethod2() {
        commonLogic();

        System.out.println("default method 2");
    }

    private void commonLogic() { // private 가능
        int common = 10;
        System.out.println("common logic" + common);
    }
}

 

 

 


Reference

인프런 - 자바 9부터 자바 21까지