CodeGym/Java Blog/무작위의/제네릭으로 작업할 때 varargs 사용
John Squirrels
레벨 41
San Francisco

제네릭으로 작업할 때 varargs 사용

무작위의 그룹에 게시되었습니다
회원
안녕! 오늘 수업에서는 계속해서 제네릭에 대해 공부하겠습니다. 공교롭게도 이것은 큰 주제이지만 피할 수는 없습니다. 언어의 매우 중요한 부분 입니다 . 수정 가능한 유형 . 수정 가능한 유형은 런타임에 정보를 완전히 사용할 수 있는 유형입니다. 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>(), // This is okay
               "Leonardo da Vinci",
               "Vasco de Gama"
       );

       // but here we get a warning
       addAll(new ArrayList<Pair<String, String>>(),
               new Pair<String, String>("Leonardo", "da Vinci"),
               new Pair<String, String>("Vasco", "de Gama")
       );
   }
}
이 메서드는 a 와 임의 개수의 객체를 addAll()입력으로 받은 다음 이러한 모든 객체를 목록에 추가합니다. 메서드 에서 메서드를 두 번 호출합니다 . 첫 번째 경우에는 두 개의 일반 문자열을 . 여기에 모든 것이 정돈되어 있습니다. 두 번째 경우에는 에 두 개의 객체를 추가합니다 . 그러나 여기서 우리는 예기치 않게 다음과 같은 경고를 받습니다. List<E>Emain()addAll()ListPair<String, String>List
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")
        }
   );
}
배열 String은 괜찮습니다. 그러나 Pair<String, String>배열은 그렇지 않습니다. 문제는 그것이 Pair<String, String>수정 불가능한 유형이라는 것입니다. 컴파일하는 동안 형식 인수(<String, String>)에 대한 모든 정보가 지워집니다. 재정의할 수 없는 유형의 배열을 만드는 것은 Java에서 허용되지 않습니다 . Pair<String, String> 배열을 수동으로 만들려고 하면 이것을 볼 수 있습니다.
public static void main(String[] args) {

   // Compilation error 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 사용 - 3힙 오염은 다음과 같은 상황에서 발생할 수 있습니다.
import java.util.ArrayList;
import java.util.List;

public class Main {

   static List<String> polluteHeap() {
       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 = polluteHeap();

       System.out.println(stringsWithHeapPollution.get(0));
   }
}
콘솔 출력:
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
간단히 말해서 힙 오염은 유형의 개체가 A힙에 있어야 하지만 B유형 안전과 관련된 오류로 인해 유형의 개체가 힙에 있는 경우입니다. 우리의 예에서 이것은 정확히 일어나는 일입니다. 먼저 원시 변수를 생성 하고 여기에 numbers일반 컬렉션( )을 할당했습니다 . 그런 다음 컬렉션에 ArrayList<Number>번호를 추가했습니다 .1
List<String> strings = numbers;
이 줄에서 컴파일러는 " Unchecked assignment... " 경고 를 발행하여 가능한 오류를 경고하려 했지만 무시했습니다. List<String>type 의 제네릭 컬렉션을 가리키는 type 의 제네릭 변수로 끝납니다 ArrayList<Number>. 분명히 이 상황은 문제를 일으킬 수 있습니다! 그리고 그렇게 됩니다. 새 변수를 사용하여 컬렉션에 문자열을 추가합니다. 이제 힙 오염이 발생했습니다. 숫자를 추가한 다음 매개변수화된 컬렉션에 문자열을 추가했습니다. 컴파일러가 우리에게 경고했지만 우리는 그 경고를 무시했습니다. 결과적으로 ClassCastException프로그램이 실행되는 동안에만 얻을 수 있습니다. 그렇다면 이것이 varargs와 어떤 관련이 있습니까? 제네릭과 함께 varargs를 사용하면 쉽게 힙 오염이 발생할 수 있습니다. 다음은 간단한 예입니다.
import java.util.Arrays;
import java.util.List;

public class Main {

   static void polluteHeap(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");

       polluteHeap(cars1, cars2);
   }
}
무슨 일이야? 유형 삭제로 인해 가변 길이 인수
List<String>...stringsLists
목록의 배열이 됩니다 List[]. 이 때문에 Object[] array메서드의 첫 번째 줄에 있는 변수에 쉽게 할당할 수 있습니다. 목록에 있는 객체의 유형이 지워졌습니다! 그리고 이제 우리는 Object[]자바의 모든 객체가 Object! 처음에는 문자열 목록의 배열만 있습니다. 그러나 유형 삭제와 가변 인수 사용 덕분에 숫자 목록을 쉽게 추가할 수 있습니다. 결과적으로 서로 다른 유형의 객체를 혼합하여 힙을 오염시킵니다. ClassCastException배열에서 문자열을 읽으려고 하면 결과가 또 달라집니다 . 콘솔 출력:
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
간단한 메커니즘인 varargs를 사용하면 이러한 예기치 않은 결과가 발생할 수 있습니다. :) 이것으로 오늘 수업은 끝납니다. 몇 가지 과제를 해결하는 것을 잊지 마시고, 시간과 에너지가 있다면 추가 독서를 공부하십시오. " Effective Java "는 스스로 읽지 않습니다! :) 다음 시간까지!
코멘트
  • 인기
  • 신규
  • 이전
코멘트를 남기려면 로그인 해야 합니다
이 페이지에는 아직 코멘트가 없습니다