Java 9. 주요 변경 내용 (1)
try-with-resources 확장
try-with-resources는 Java 7에서 추가된 기능으로 개발자가 직접 닫아줘야 하는 자원을 try() 안에 선언하면, try 로직이 끝난 후 자동으로 해당 자원을 닫아주는 것입니다. 자동으로 해당 자원을 닫아주기 위해서는 AutoCloseable을 구현하고 있어야 합니다.
- Java 7 이전 코드
Resource resource = new Resource();
try {
// 작업
} finally {
resource.close();
}
- Java 7 이후 코드
try (Resource resource = new Resource()) {
// 작업
}
Java 7의 try-with-resources의 경우, try() 밖에서 만든 자원을 닫을 수는 없습니다.
Resource r1 = new Resource("r1");
try (
r1; // Compile Error !!
Resource r2 = new Resource("r2")
) {
// Logic
}
Java 9에서는 이를 개선하여 try 바깥의 final 변수 or 사실상 final인 변수(한 번 선언된 후 변경이 되지 않은)도 자동으로 자원을 닫아줍니다.
Resource r1 = new Resource("r1");
final Resource r2 = new Resource("r2");
try (r1; r2) {}
@SafeVarargs 어노테이션 확장
@SafeVarargs는 Java 7에서 도입된 어노테이션으로, 제네릭 가변인수(varargs) 메소드의 타입 안전성을 보장하는 데 사용됩니다. Java 9 이전에는 @SafeVarargs 어노테이션을 final 메소드, static 메소드, 생성자에만 사용할 수 있었습니다. Java 9부터는 private 메소드에도 해당 어노테이션을 사용할 수 있게 되었습니다.
가변인수(varargs)란 메소드를 호출할 때 매개변수의 개수를 자유롭게 넣을 수 있는 것으로, 내부적으로 해당 값들이 담겨 있는 배열이 생성됩니다.
public static int sum(int... nums) {
return Arrays.stream(nums).sum();
}
제네릭 함수에서 가변인수를 사용하면 안전하지 않을 수 있다는 경고가 발생합니다. @SafeVarargs를 사용하면 메소드 호출 시 발생하는 경고를 억제할 수 있어, 코드의 가독성을 높이고 불필요한 경고를 제거할 수 있습니다.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Main {
@SafeVarargs
public static <T> List<T> flatten(List<T>... lists) {
List<T> result = new ArrayList<>();
for (List<T> list : lists) {
result.addAll(list);
}
return result;
}
public static void main(String[] args) {
List<Integer> list1 = Arrays.asList(1, 2, 3);
List<Integer> list2 = Arrays.asList(4, 5);
List<Integer> result = flatten(list1, list2);
System.out.println(result);
}
}
익명 inner class + diamond syntax
Java 9부터는 내부 클래스를 익명 클래스로 만들 때 diamond syntax 사용이 가능해졌습니다. diamond syntax란 제네릭 클래스를 인스턴스화 할 때 우항(오른쪽)의 타입을 생략할 수 있는 것입니다.
// 타입을 생략하지 않음
List<Integer> numbers1 = new ArrayList<Integer>();
// 타입을 생략함 (diamond syntax)
List<Integer> numbers2 = new ArrayList<>();
내부 클래스는 외부 참조를 할 수 있는 내부 클래스와 외부 참조를 할 수 없는 내부 클래스가 있습니다. 외부 참조가 있으면 메모리 관리가 어렵고, 직렬화 표준도 없기 때문에 외부 참조가 없는 내부 클래스가 권장됩니다.
public class Main {
// 외부 (Main) 참조를 할 수 있는 내부 클래스
public class ReferenceClass { }
// 외부 (Main) 참조를 할 수 없는 내부 클래스
public static class NoReferenceClass { }
}
다음은 Java 9부터 내부 클래스를 익명 클래스로 만들 때 diamond syntax를 적용한 코드입니다.
public class Main {
public static void main(String[] args) {
InnerClass<Integer> ic = new InnerClass<>(3) { // diamond syntax
};
int _abc = 3;
}
public static class InnerClass<T> {
private final T t;
public InnerClass(T t) {
this.t = t;
}
}
}
인터페이스 + private 메소드
Java 8부터는 인터페이스에 default 메소드를 구현할 수 있게 되었습니다. 그러나 default 메소드를 구현 하다 보니 로직이 겹치는 코드가 발생하기 시작하였습니다. Java 9에서는 중복된 코드를 제거하고 재사용성 향상을 위해 인터페이스에도 private 메소드를 사용 가능하게 되었습니다.
public interface PaymentProcessor {
default void processPayment(double amount) {
if (validateAmount(amount)) {
System.out.println("Processing payment of $" + amount);
sendConfirmation();
} else {
System.out.println("Invalid payment amount");
}
}
default void refundPayment(double amount) {
if (validateAmount(amount)) {
System.out.println("Refunding payment of $" + amount);
sendConfirmation();
} else {
System.out.println("Invalid refund amount");
}
}
// Private helper method
private boolean validateAmount(double amount) {
return amount > 0;
}
// Private helper method
private void sendConfirmation() {
System.out.println("Sending confirmation email...");
}
// Static private method
private static void logTransaction(String type, double amount) {
System.out.println("Logging " + type + " transaction: $" + amount);
}
// Public static method that uses private static method
static void auditPayment(double amount) {
logTransaction("payment", amount);
}
}
underscore 네이밍 불가능
Java 9에서는 언더스코어(_)를 변수 이름으로 사용하는 것이 불가능하게 되었습니다. 이는 언더스코어가 Java 9부터 예약된 키워드로 지정되었기 때문입니다. 해당 변경은 언더스코어를 향후 Java 버전에서 특별한 의미를 가진 키워드로 사용하기 위한 준비 단계입니다.