HOWTO · Java

Java의 다이아몬드 연산자

이 튜토리얼에서는 Java의 다이아몬드 연산자에 대해 설명합니다.

이 페이지의 내용

Java의 다이아몬드 연산자 <>를 사용하면 객체를 만들 때 컴파일러가 제네릭 타입 인수를 추론합니다. 변수 선언에는 타입을 그대로 명시하면서 생성자 호출에서 반복되는 타입 인수는 생략할 수 있습니다. 이 연산자를 사용해도 클래스의 제네릭이 없어지는 것은 아니며, 확인되지 않은 형 변환이 안전해지는 것도 아닙니다.

다이아몬드 연산자의 기능

List<String> names = new ArrayList<>();와 같은 선언에서는 변수에 선언된 타입이 생성자의 타입 인수를 추론할 때 기준이 됩니다. 객체의 타입은 여전히 ArrayList<String>이며, 생성자 이름 뒤에 반복해서 쓰던 <String>만 생략됩니다. 주변 문맥만으로 적절한 타입을 결정할 수 없으면 컴파일러가 의도보다 더 넓은 타입을 추론할 수 있습니다. 의도한 타입이 분명하지 않다면 변수 타입이나 대상 타입을 명시하세요.

다이아몬드 연산자는 제네릭 클래스의 객체를 생성할 때 사용합니다. 제네릭 메서드를 호출하는 특별한 방법은 아닙니다. 메서드 타입 인수는 메서드 호출 규칙에 따라 메서드 인수와 대상 문맥을 바탕으로 추론됩니다.

제네릭 생성자에서 다이아몬드 연산자 사용하기

제네릭 클래스는 타입 매개변수에 해당하는 값을 받는 생성자를 선언할 수 있습니다. 왼쪽에 선언된 타입이 오른쪽 생성자의 타입 추론을 돕습니다.

다음 예제를 실행해 보세요.

이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.

import java.util.ArrayList;
import java.util.List;

public class DiamondConstructorExample {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("Ada");
        names.add("Linus");
        System.out.println(names);
    }
}
[Ada, Linus]

예제의 출력은 [Ada, Linus]입니다. 선언된 List<String>이 ArrayList<> 생성에 필요한 타입 문맥을 컴파일러에 제공합니다. 이 리스트는 계속 문자열을 받으며, 호환되지 않는 타입의 값을 추가하려고 하면 컴파일 시 타입 검사가 이를 거부합니다.

생성자는 클래스의 타입 매개변수에 해당하는 값을 받을 수도 있습니다. 이 경우 선언된 대상 타입 Box<String>을 바탕으로 컴파일러가 new Box<>(...)의 타입 인수를 String으로 추론합니다.

public class GenericConstructorExample {
    static class Box<T> {
        private final T value;

        Box(T value) {
            this.value = value;
        }

        T getValue() {
            return value;
        }
    }

    public static void main(String[] args) {
        Box<String> greeting = new Box<>("Hello, Java");
        System.out.println(greeting.getValue());
    }
}

생성자 매개변수의 타입은 T이므로 대상 타입 String에 따라 인수와 저장되는 값의 타입도 String이 됩니다. 이는 일반적인 제네릭 타입 검사이며, 다이아몬드 연산자가 생략하는 것은 생성자 호출에서 반복되는 타입 인수뿐입니다. 출력의 Hello는 예제에 표시된 영어 문구 그대로 유지됩니다.

익명 클래스에서 다이아몬드 연산자 사용하기

제네릭 클래스의 익명 하위 클래스를 생성할 때도 다이아몬드 구문을 사용할 수 있습니다. 대상 타입과 익명 클래스의 구현은 서로 일치해야 합니다. 다음 예제는 ArrayList<String>을 사용하고 간단한 결과를 보여 주도록 toString을 재정의합니다.

이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.

import java.util.ArrayList;

public class DiamondAnonymousExample {
    public static void main(String[] args) {
        ArrayList<String> names = new ArrayList<>() {
            @Override
            public String toString() {
                return "names=" + size();
            }
        };
        names.add("Ada");
        System.out.println(names);
    }
}
names=1

예제의 출력은 names=1입니다. 추론된 타입 인수는 String입니다. 익명 클래스 본문을 사용해도 다이아몬드 연산자가 원시 타입으로 바뀌거나 제네릭 타입 검사가 없어지지 않습니다.

Java 버전 요구 사항 및 제한 사항

다이아몬드 구문의 지원 여부는 컴파일에 선택한 Java 언어 수준에 따라 달라집니다. 익명 클래스에서 사용할 때는 언어 수준에 따른 추가 제약이 있을 수도 있습니다. 대상으로 삼는 언어 수준과 릴리스에 맞춰 Java 언어 명세와 컴파일러 문서를 확인하세요. IDE에서는 프로젝트의 언어 수준을 확인하고, 빌드 환경에서는 설정된 source 또는 release 옵션을 살펴보세요. <>에서 컴파일 오류가 발생한다면 선택한 언어 수준이 해당 구문을 지원하지 않는 것일 수 있습니다.

다이아몬드 연산자는 원시 타입, 안전하지 않은 형 변환 또는 원시 제네릭 값을 노출하는 API 때문에 발생하는 확인되지 않은 경고를 억제하지 않습니다. 매개변수화된 타입을 사용하거나 필요한 형 변환을 검증해 원인을 해결하세요. <>를 경고 제거 기능으로 취급하지 마세요.

예제 실행 및 확인

각 예제를 공개 클래스 이름과 일치하는 파일에 저장한 다음, 대상으로 삼는 언어 수준에 맞게 설정한 JDK로 컴파일하세요. 리스트 예제는 javac DiamondConstructorExample.java를 실행한 뒤 java DiamondConstructorExample을 실행하면 [Ada, Linus]를 출력합니다. 익명 클래스 예제는 javac DiamondAnonymousExample.java 다음에 java DiamondAnonymousExample을 실행하면 names=1을 출력합니다. 제네릭 생성자 예제는 성공적으로 실행되었으며, java GenericConstructorExample은 Hello, Java를 출력했습니다. 컴파일러가 <>를 거부하면 소스 코드를 바꾸기 전에 설정된 Java 언어 수준을 확인하세요.