JavaRush /Java Blog /Random-KO /제네릭 작업 시 varargs 사용

제네릭 작업 시 varargs 사용

Random-KO 그룹에 게시되었습니다
안녕하세요! 오늘 수업에서는 계속해서 제네릭을 공부하겠습니다. 이것은 큰 주제이지만 갈 곳이 없습니다. 이것은 언어에서 매우 중요한 부분입니다 :) 제네릭에 대한 Oracle 문서를 연구하거나 인터넷에서 가이드를 읽을 때 다음 용어를 접하게 될 것입니다. 수정 불가능 유형수정 가능 유형 . '재확인 가능'이란 어떤 단어인가요? 영어로 모든 것이 좋다고 하더라도, 영어를 접했을 가능성은 거의 없습니다. 번역해 봅시다! 제네릭 작업 시 varargs 사용 - 2
*Google 감사합니다. 많은 도움이 되었습니다 -_-*
재구성 가능 유형은 런타임 시 정보를 완전히 사용할 수 있는 유형입니다. Java 언어에서는 기본 유형, 원시 유형 및 제네릭이 아닌 유형이 여기에 포함됩니다. 대조적으로, 재구성 불가능한 유형은 런타임 시 정보가 지워지고 사용할 수 없게 되는 유형입니다. 이것은 단지 제네릭입니다 - List<String> , List<Integer> 등.

그런데, varargs가 무엇인지 기억하시나요 ?

잊어버린 경우를 대비해 이는 가변 길이 인수입니다. 메소드에 전달될 수 있는 인수의 수를 정확히 알 수 없는 상황에서 유용합니다. 예를 들어 계산기 클래스가 있고 여기에 메서드가 있다고 가정합니다 sum. sum()2개의 숫자, 3, 5개 또는 원하는 만큼의 숫자를 메소드에 전달할 수 있습니다. sum()가능한 모든 옵션을 고려하기 위해 매번 메소드를 오버로드하는 것은 매우 이상할 것입니다 . 대신에 우리는 이렇게 할 수 있습니다:
public class SimpleCalculator {

   public static int sum(int...numbers) {

       int result = 0;

       for(int i : numbers) {

           result += i;
       }

       return result;
   }

   public static void main(String[] args) {

       System.out.println(sum(1,2,3,4,5));
       System.out.println(sum(2,9));
   }
}
콘솔 출력:

15
11
따라서 varargs제네릭과 함께 사용하면 몇 가지 중요한 기능이 있습니다. 이 코드를 살펴보겠습니다.
import javafx.util.Pair;
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static <E> void addAll(List<E> list, E... array) {

       for (E element : array) {
           list.add(element);
       }
   }

   public static void main(String[] args) {
       addAll(new ArrayList<String>(),  //  здесь все нормально
               "Leonardo da Vinci",
               "Vasco de Gama"
       );

       // а здесь мы получаем предупреждение
       addAll(new ArrayList<Pair<String, String>>(),
               new Pair<String, String>("Leonardo", "da Vinci"),
               new Pair<String, String>("Vasco", "de Gama")
       );
   }
}
이 메서드는 목록 과 여러 개체를 addAll()입력으로 사용 하고 이러한 모든 개체를 목록에 추가합니다. 메소드에서 우리는 메소드를 두 번 호출합니다 . 처음으로 두 개의 일반 줄을 추가합니다. 여기는 모든 것이 괜찮습니다. 두 번째로 두 개의 개체를 추가합니다 . 그리고 여기서 갑자기 경고가 나타납니다. List<E>Emain()addAll()ListListPair<String, String>

Unchecked generics array creation for varargs parameter
무슨 뜻이에요? 경고를 받는 이유는 무엇이며 경고와 어떤 관련이 있습니까 array? Array- 이것은 배열이고 우리 코드에는 배열이 없습니다! 두 번째부터 시작하겠습니다. 컴파일러가 가변 길이 인수(varargs)를 배열로 변환하기 때문에 경고에는 배열이 언급되어 있습니다. 즉, 우리 방법의 시그니처는 다음과 같습니다 addAll().
public static <E> void addAll(List<E> list, E... array)
실제로는 다음과 같습니다.
public static <E> void addAll(List<E> list, E[] array)
즉, 메서드에서 main()컴파일러는 코드를 다음과 같이 변환합니다.
public static void main(String[] args) {
   addAll(new ArrayList<String>(),
      new String[] {
        "Leonardo da Vinci",
        "Vasco de Gama"
      }
   );
   addAll(new ArrayList<Pair<String,String>>(),
        new Pair<String,String>[] {
            new Pair<String,String>("Leonardo","da Vinci"),
            new Pair<String,String>("Vasco","de Gama")
        }
   );
}
array 에서는 모든 것이 괜찮습니다 String. 그러나 배열의 경우 Pair<String, String>- 아니요. 사실 Pair<String, String>이는 수정 불가능한 유형입니다. 컴파일하는 동안 매개변수 유형(<String, String>)에 대한 모든 정보가 삭제됩니다. 재구성 불가능한 유형에서 배열을 생성하는 것은 Java에서 허용되지 않습니다 . pair<String, String> 배열을 수동으로 생성하려고 하면 이를 확인할 수 있습니다.
public static void main(String[] args) {

   //  ошибка компиляции! Generic array creation
  Pair<String, String>[] array = new Pair<String, String>[10];
}
그 이유는 명백합니다 - 유형 안전성입니다. 기억하시겠지만, 배열을 생성할 때 이 배열이 저장할 객체(또는 기본 요소)를 지정해야 합니다.
int array[] = new int[10];
이전 강의 중 하나에서 유형 삭제 메커니즘을 자세히 조사했습니다. 따라서 이 경우 유형을 삭제한 결과 객체에 Pair쌍이 저장되어 있다는 정보가 손실되었습니다 <String, String>. 배열을 만드는 것은 안전하지 않습니다. 및 제네릭 과 함께 메서드를 사용할 때는 varargs유형 삭제와 작동 방식을 정확히 기억해야 합니다. 작성한 코드에 대해 절대적으로 확신하고 문제가 발생하지 않을 것이라는 점을 알고 있다면 varargs주석을 사용하여 코드와 관련된 경고를 비활성화할 수 있습니다.@SafeVarargs
@SafeVarargs
public static <E> void addAll(List<E> list, E... array) {

   for (E element : array) {
       list.add(element);
   }
}
이 주석을 메서드에 추가하면 이전에 발생한 경고가 표시되지 않습니다. 제네릭을 함께 사용할 때 발생할 수 있는 또 다른 문제는 varargs힙 오염입니다. 제네릭 작업 시 varargs 사용 - 4다음과 같은 상황에서 오염이 발생할 수 있습니다.
import java.util.ArrayList;
import java.util.List;

public class Main {

   static List<String> makeHeapPollution() {
       List numbers = new ArrayList<Number>();
       numbers.add(1);
       List<String> strings = numbers;
       strings.add("");
       return strings;
   }

   public static void main(String[] args) {

       List<String> stringsWithHeapPollution = makeHeapPollution();

       System.out.println(stringsWithHeapPollution.get(0));
   }
}
콘솔 출력:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
간단히 말하면, 힙 오염은 유형 1의 객체가 힙에 있어야 하지만 유형 안전 오류로 인해 А유형의 객체가 힙에 있게 되는 상황입니다. B우리의 예에서는 이런 일이 발생합니다. 먼저 Raw 변수를 생성 numbers하고 이를 일반 컬렉션에 할당했습니다 ArrayList<Number>. 그 후에 우리는 거기에 숫자를 추가했습니다 1.
List<String> strings = numbers;
이 줄에서 컴파일러는 " 검사되지 않은 할당... "이라는 경고를 발행하여 가능한 오류에 대해 경고하려고 했지만 우리는 이를 무시했습니다. 결과적으로 유형의 List<String>일반 컬렉션을 가리키는 유형 의 일반 변수가 있습니다 ArrayList<Number>. 이러한 상황은 분명히 문제를 일으킬 수 있습니다! 이것이 일어나는 일입니다. 새 변수를 사용하여 컬렉션에 문자열을 추가합니다. 힙이 오염되었습니다. 먼저 숫자를 추가한 다음 입력된 컬렉션에 문자열을 추가했습니다. 컴파일러는 우리에게 경고했지만 우리는 그 경고를 무시하고 ClassCastException프로그램이 실행되는 동안에만 결과를 받았습니다. 그것 은 그것 과 무슨 관련 이 있나요 varargs? 제네릭과 함께 사용하면 varargs쉽게 힙 오염이 발생할 수 있습니다. 간단한 예는 다음과 같습니다.
import java.util.Arrays;
import java.util.List;

public class Main {

   static void makeHeapPollution(List<String>... stringsLists) {
       Object[] array = stringsLists;
       List<Integer> numbersList = Arrays.asList(66,22,44,12);

       array[0] = numbersList;
       String str = stringsLists[0].get(0);
   }

   public static void main(String[] args) {

       List<String> cars1 = Arrays.asList("Ford", "Fiat", "Kia");
       List<String> cars2 = Arrays.asList("Ferrari", "Bugatti", "Zaporozhets");

       makeHeapPollution(cars1, cars2);
   }
}
여기서 무슨 일이 일어나고 있는 걸까요? 유형 삭제로 인해 매개변수 시트(편의상 "목록" 대신 "시트"라고 부르겠습니다)는 다음과 같습니다.
List<String>...stringsLists
- 알 수 없는 유형의 시트 배열로 변환됩니다 List[](컴파일 결과 varargs가 일반 배열로 변환된다는 점을 잊지 마십시오). 이 때문에 메소드의 첫 번째 줄에서 변수에 쉽게 할당할 수 있습니다 Object[] array. 유형이 시트에서 지워졌습니다! 이제 우리는 Object[]무엇이든 추가할 수 있는 유형의 변수를 갖게 되었습니다. Java의 모든 객체는 Object! 지금은 스트링 시트 배열만 있습니다. 그러나 유형을 사용 varargs하고 삭제함으로써 우리는 쉽게 숫자 시트를 추가할 수 있습니다. 결과적으로 우리는 서로 다른 유형의 객체를 혼합하여 힙을 오염시킵니다. ClassCastException배열에서 문자열을 읽으려고 할 때 결과는 동일한 예외가 됩니다 . 콘솔 출력:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
겉으로는 단순해 보이는 메커니즘을 사용함으로써 발생할 수 있는 예상치 못한 결과입니다. varargs:) 그리고 오늘 강의는 이것으로 끝납니다. 몇 가지 문제를 해결하는 것을 잊지 마세요. 시간과 에너지가 남아 있다면 추가 문헌을 공부하세요. " Effective Java "는 자체적으로 읽혀지지 않습니다! :) 또 봐요!
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION