Java Generics: 유형 삭제 및 와일드카드

13951 단어 corejava
제네릭 유형을 지정하면 Java가 컴파일 타임에 유형 검사를 수행할 수 있습니다. 그러나 코드에서 제네릭을 사용할 때 컴파일 타임에 발생하는 "Type Erasure"로 인해 제네릭 형식 매개 변수가 Object 형식으로 변환됩니다. 이렇게 하면 제네릭 형식 매개 변수가 Object 메서드를 제외한 다른 메서드를 호출할 수 없습니다.
Object 클래스에 있는 메소드가 아닌 다른 메소드를 호출하려면 어떻게 해야 합니까? 이 문서에서는 "와일드카드", "경계"및 "유형 삭제"가 무엇인지 설명합니다. 그리고 제네릭을 사용할 때 유연성을 높이기 위해 와일드카드를 사용하는 방법.

유형 삭제



Wikipedia는 유형 삭제를 다음과 같이 정의합니다.

Type erasure is the load-time process by which explicit type annotations are removed from a program, before it is executed at run-time.



이 예제에서는 제네릭을 사용하므로 여러 유형에 사용할 수 있습니다.

class Container<T> {
    private T contents;

    public Container(T contents) {
        this.contents = contents;
    }

    public T getContents() {
        return contents;
    }

    public static void main(String[] args) {
        Container<String> container = new Container<>("Hello!");
        String contents = container.getContents();
        System.out.println(contents);// Hello!
    }
}

Container<String> container = new Container<>(“Hello!”);를 입력할 때 유형TString로 대체한다고 생각할 수 있습니다. 그러나 컴파일 타임에 Java 컴파일러는 코드에서 일반 유형 매개변수T를 제거하고 이를 Object 유형으로 대체합니다.

class Container {
    private Object contents;

    public Container(Object contents) {
        this.contents = contents;
    }

    public Object getContents() {
        return contents;
    }

    public static void main(String[] args) {
        Container container = new Container("Hello!");
        String contents = (String) container.getContents();
        System.out.println(contents);
    }
}

String contents = container.getContents();String contents = (String) container.getContents();로 어떻게 변환되는지 주목하세요. 보시다시피 컴파일러는 getContents() 메서드를 호출할 때 캐스트를 삽입했습니다.

와일드카드 제네릭 유형


?로 표시되는 와일드카드는 "모든 유형"을 정의하려는 경우에 유용합니다.

무제한 와일드 카드



제네릭 제약 조건 중 하나는 예를 들어 List<Integer> 인수를 List<Object> 매개변수에 전달할 수 없다는 것입니다. 그렇게 하면 컴파일 타임 오류가 발생합니다. Java가 사용자로부터 사용자를 보호하기 때문에 실제로는 좋은 일입니다!

public static void processList(List<Object> list) {
    for (Object o : list)
    System.out.print(o);
}

public static void main(String[] args) {
    List<Integer> numbers = List.of(1, 2);
    processList(numbers); // compile-time error
}


이 예제는 List<Integer>List<Object> 의 하위 유형이 아니기 때문에 작동하지 않습니다. 예상할 수 있듯이 List<Object> 매개변수의 processList 유형은 List<?>로 대체되어야 합니다(읽기: 알 수 없는 유형 목록).

이제 모든 종류의 List를 processList 메서드에 전달할 수 있습니다.

public static void processList(List<?> list) {
    for (Object o : list)
        System.out.print(o);
}

public static void main(String[] args) {
    List<Integer> list = List.of(1, 2);
    processList(list);// 12

    System.out.println();
    List<String> strings = List.of("Hello", "World!");
    processList(strings);// HelloWorld!
}


경계 와일드카드



제한된 와일드카드를 사용하면 컴파일러에서 형식에 대한 제한을 적용할 수 있습니다. 이 예에서는 제한된 와일드카드의 성능 및 안전성 이점을 보여줍니다.

public static void transformList(List<? extends CharSequence> list) {
    List<Integer> charsCount = list.stream()
            .map(CharSequence::length)
            .collect(Collectors.toList());
    System.out.println(charsCount);
}

public static void main(String[] args) {
    List<String> stringList = List.of("Hello", "World", "!");
    transformList(stringList);// [5, 5, 1]
    List<StringBuilder> stringBuilders = List.of(new StringBuilder("Java"), new StringBuilder("Rocks"));
    transformList(stringBuilders);// [4, 5]
}

transformList가 하는 일은 List를 취하여 List<Integer>로 변환하여 각 요소의 길이를 표현하는 것입니다.
transformList의 매개변수는 List<? extends CharSequence>이며, 이는 이 메서드가 CharSequence , List<String> 와 같이 List<StringBuilder> 와 같이 CharSequence 의 하위 유형이 있는 모든 목록을 허용할 수 있음을 의미합니다. 이 메서드의 인수로 전달됩니다.

마무리



이 게시물에서 Type Erasure의 개념을 명확히 하고 와일드카드와 경계를 소개했습니다. 그러나 제네릭에서 배울 것이 훨씬 더 많습니다. 궁금하고 더 알고 싶다면 이 종합Java Generics FAQ을 적극 추천한다.

이 게시물이 유용하다고 생각했다면 지금 무엇을 해야 하는지 알고 있습니다. 박수 버튼을 누르고 피드에 대한 더 많은 기사와 튜토리얼을 보려면 저를 팔로우하세요.

좋은 웹페이지 즐겨찾기