Hi yoahn 개발블로그

자바 9에서 추가된 기능들 본문

프로그래밍 언어/Java

자바 9에서 추가된 기능들

hi._.0seon 2024. 10. 9. 18:26
반응형

제 블로그는 제가 공부한 내용들을 기록하고 나중에 기억이 안날때를 대비해 제가 보기 위한 용도로 작성하고 있습니다.

그럼에도 불구하고 구독해주신분들이 계셔서 감사,,

 

1. 확장된 try-with-resources

개발자가 직접 닫아줘야했던 자원을 try( ) 안에 선언하여 try 로직이 끝날때 자동으로 해당 자원을 닫는 기능이다.

이때 자원은 AutoCloseable 구현하고 있어야 함

try (Resource resource = new Resource()) {
    // job
}

자바 8까지는 try() 바깥에서 선언한 변수에 대해서는 try-with-resources 구문을 사용할 수 없었지만 자바9 부터 final 혹은 초기화한 이후 값이 변경되지 않은 변수인 경우에 try() 안에 변수 이름을 한번 더 쓰는 것으로 사용할 수 있다.

 

2. @SafeVarargs 어노테이션 + private 메소드

자바9 이전에는 private 메소드와 함께 사용할 수 없었는데, 메소드를 호출할 때 매개변수의 개수를 자유롭게 넣을 수 있고 해당 값들이 담겨있는 배열이 생성된다.

 

public class Main {
	@SafeVarargs
	public static <T> List<T> flatten(List<T> ...lists) {
		Object[] obj = lists;
		obj[0] = Arrays.asList("Hello, World!");
		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, 6);
		List<Integer> result = flatten(list1, list2);
		System.out.println(result);
	}
}

이 코드가 나는 제네릭인데 T 가 Integer 타입으로 들어가서 String 타입의 리스트 추가는 런타임오류로 잡힐거라 생각했는데, 예상 외로 동작하고 문자열과 숫자가 모두 들어간 배열이 출력되서 충격이었다.

 

이런 식으로 코드를 작성할 수 있다보니 어노테이션 없이 해당 코드를 실행하면 안전하지 않은 코드를 실행한다는 warning 이 출력된다.

이때 문제가 생기지 않는 코드를 작성했다면 해당 warn 메시지가 뜨지 않도록 @SafeVarags 어노테이션을 사용할 수 있다.

 

자바9부터 private 함수에도 사용할 수 있게 되었다.

 

3. 익명 inner class + diamond syntax

내부 클래스를 익명 클래스로 만들 때, diamond syntax 를 사용할 수 있게 되었다는 점이다.

 

diamond syntax : 자바 7에서 소개된 기능

제네릭 클래스를 인스턴스화할 때 우항의 타입 생략

List<Integer> numbers = new ArrayList<>();

 

내부 클래스는 클래스 안에 위치한 클래스로, 외부 참조가 있는 내부 클래스와 외부참조가 없는 클래스로 분리 가능

public class Main {
    // 외부 참조를 할 수 있는 내부 클래스
    public class ReferenceClass {}
    
    // 외부 참조를 할 수 없는 내부 클래스
    public static class NoReferenceClass {}
}

 

자바 9부터 내부 클래스를 익명 클래스로 만들 때, diamond syntax 사용이 가능해졌다.

public class Main {
    public static void main(String[] args) {
        InnerClass<Integer> ic = new InnerClass<>(3) {};
    }
    
    public static class InnerClass<T> {
        private final T t;
        
        public InnerClass(T t) {
            this.t = t;
        }
    }
}

4. 인터페이스에서 private 메소드를 사용할 수 있도록 변경

기존 인터페이스를 수정하게되면 하위 호환성이 깨지게 되는 문제가 있었다. 그래서 비슷한 이름의 다른 인터페이스를 만들고, 이 차이를 묻는 사람들이 생기게 된다.

자바 8에서 default 메소드 구현이 가능해지면서 여러 default 메소드들끼리 공통된 코드로 존재하게 되었고, 자바9에서는 private 메서드 구현을 가능하게 하여 공통된 코드를 분리할 수 있게 되었다.

 

5. under score 네이밍 불가

int _ = 1;

_ 만으로 변수, 함수 이름을 지을 수 없게 변경됨

 

6. Collection 기능 추가

6-1. Collection 을 만드는 방법 추가

List.of() 정적 팩토리 메소드가 생겼다.

List<Integer> oldList = Arrays.asList(1, 2);
List<Integer> newList = List.of(1, 2);

Set<Integer> oldSet = new HashSet<>(Arrays.asList(1,2));
Set<Integer> newSet = Set.of(1,2);

Map<String, Integer> newMap = Map.of("A", 1, "B", 2);
Map.ofEntries(entry("A", 1), entry("B", 2));

 

정적 팩토리 메소드 of 를 이용해 만들어진 Collection 은 불변이기 때문에 값을 추가하거나 제거할 수 없다. (UnsupportedOperationException 발생)

 

과거 불변 컬렉션을 만들기 위해 사용했던 Collections.unmodifiableList 와는 구현체가 다름

Collections.unmodifiableList -> 구현체: UnmodifiableRandomAccessList, UnmodifiableList

List.of -> 구현체: ImmutableCollections.ListN 이 구현체로 사용된다.

 

정적 팩토리 메소드를 사용하면 좀더 메모리 효율적인 코드 작성이 가능하다.

 

7. Optional, Stream 기능 추가

Optional 기능

ifPresentOrElse(action, emptyAction)

기존에 있던 ifPresent(action) 의 기능이 더 확대된 것

두 함수를 받아 값이 존재하면 앞의 함수를, 없으면 뒤의 함수를 실행

 

or(supplier)

Optional 에서 체이닝을 통해 값이 존재하지 않을 경우 다른 Optional 로 변환할 수 있도록 or API 가 등장

getOptionalNum().or(() -> Optional.of(3));

stream()

Optional 을 stream 으로 변환하는 함수

값이 존재하면, 해당 값 한개를 가진 stream 이 되고, 없으면 빈 스트림이 됌

 

Stream 기능

takeWhile(predicate)

filter(predicate) 와 기능이 비슷하지만, predicate 결과가 false 가 나오는 순간 뒤의 데이터는 모두 버린다.

 

dropWhile(predicate)

takeWhile(predicate) 과 비슷하지만 반대이다. predicate 이 false 가 나오는 순간 뒤의 데이터는 모두 살린다.

 

ofNullable(t)

정적 팩토리 메소드

null 인 경우 빈 스트림, 아니면 해당 값을 가진 Stream 반환

 

iterate

기존 iterate 기능 개선

기존

Stream.iterate(0, i -> i + 2)
	.limit(5)
    .forEach(System.out::println);

기존 함수는 Stream 을 만들기 위해 초기값, 변화 값 두가지 매개변수를 넣었고, 유한 스트림을 위해 Limit() 함수 필요

 

개선

Stream.iterate(0, i -> i < 10, i -> i + 2)
	.forEach(System.out::println);

유한 스트림을 위한 종료 조건을 매개변수로 넣을 수 있게 됨

limit() 을 사용하지 않아도 된다.

8. CompletableFuture API 기능 추가

비동기 프로그래밍에 사용되는 CompletableFuture

 

orTimeout()

이 메소드를 이용하면 CompletableFuture 에서 실행하고 있는 작업에 대한 타임아웃을 걸 수 있다.

public static void main(String[] args) throws Exception {
    // 10 초 기다렸다 '작업 완료' 출력하는 Runnable
    Runnable sleep = () -> {
        try {
            Thread.sleep(10_000L);
            System.out.println(System.currentTimeMillis() + " - 작업 완료");
        } catch(InterruptedException e) {}
    };
    
    System.out.println(System.currentTimeMillis() + " - 작업 실행");
    // sleep 작업 실행하고 1초 넘으면 timeout
    CompletableFuture<Void> future = CompletableFuture.runAsync(sleep).orTimeout(1, TimeUnit.SECONDS);
    
    // 작업이 완료될 때까지 메인 스레드를 blocking
    future.get();
}

위 예제를 실행시키면 작업 실행 출력 이후 timeout exception 발생하고, CompletableFuture 는 예외와 함께 종료된다.

 

비슷한 함수는 completeOnTimeout() 이 있다. 이 함수는 orTimeout 과 사용법이 같지만, 타임아웃이 되면, 에러와 함께 CompletableFuture 가 종료되는 대신 매개변수로 받은 값을 반환하는 CompletableFuture 를 만들어 낸다.

 

 

반응형
Comments