Java SafeVarargs 주석, 표준 또는 모범 사례가 있습니까?


175

최근에 자바 @SafeVarargs주석을 보았습니다 . Java의 가변 기능을 안전하지 않게 만드는 것에 대한 인터넷 검색으로 인해 혼란스러워졌습니다 (힙 중독? 지워진 유형?). 몇 가지 사항을 알고 싶습니다.

  1. 가변성 Java 기능이 안전하지 않은 이유는 무엇입니까 @SafeVarargs(바람직한 예의 형태로 설명하는 것이 바람직 함)?

  2. 왜이 주석이 프로그래머의 재량에 달려 있습니까? 컴파일러가 확인할 수있는 것이 아닌가?

  3. 그의 기능이 실제로 Varag를 안전하게 지키기 위해 준수해야 할 표준이 있습니까? 그렇지 않은 경우이를 보장하기위한 모범 사례는 무엇입니까?


1
JavaDoc 에서 예제와 설명을 보셨습니까 ?
jlordo

2
세 번째 질문은 항상 첫 번째 요소와 다른 요소를 갖는 것 retType myMethod(Arg first, Arg... others)입니다. 을 지정하지 않으면 first빈 배열이 허용되며 인수를 사용하지 않고 동일한 반환 유형을 가진 동일한 이름의 메서드를 가질 수 있습니다. 즉, JVM이 호출해야 할 메서드를 결정하는 데 어려움을 겪을 수 있습니다.
fge

4
@jlordo 내가했지만 varargs 함수 외부에서이 상황을 varargs 함수 외부에서 쉽게 보충 할 수있는 이유를 이해하지 못합니다 (이를 검증하고 예상되는 유형 안전 경고 및 런타임시 오류로 컴파일).
Oren

답변:


244

1) 인터넷과 StackOverflow에는 제네릭 및 varargs의 특정 문제에 대한 많은 예가 있습니다. 기본적으로 유형 매개 변수 유형의 가변 개수의 인수가있는 경우입니다.

<T> void foo(T... args);

Java에서 varargs는 컴파일 타임에 간단한 "다시 쓰기"를 수행하는 구문 설탕입니다. type의 varargs 매개 변수는 type X...의 매개 변수로 변환됩니다 X[]. 이 varargs 메소드를 호출 할 때마다 컴파일러는 varargs 매개 변수에 포함 된 모든 "변수 인수"를 수집하고 다음과 같은 배열을 작성합니다 new X[] { ...(arguments go here)... }.

이것은 varargs 유형이 콘크리트와 같은 경우에 효과적 String...입니다. 와 같은 유형 변수 인 T...경우 T해당 호출에 대한 구체적인 유형으로 알려진 경우에도 작동 합니다. 방법은 위의 클래스의 일부 예를 들어 경우 Foo<T>, 당신은이 Foo<String>참조, 다음 호출 foo우리가 알고 있기 때문에 괜찮을 것에 TString코드에서 그 시점에서.

그러나의 "값" T이 다른 유형 매개 변수 인 경우에는 작동하지 않습니다 . Java에서는 유형 매개 변수 구성 요소 유형 ( new T[] { ... }) 의 배열을 작성할 수 없습니다 . 따라서 Java는 대신 new Object[] { ... }(여기 Object의 상한이 있습니다 T. 상한이 다르면 대신 대신 사용됩니다 Object) 컴파일러 경고를 제공합니다.

그렇다면 무엇을 만드는 new Object[]대신에 new T[]무엇이 잘못 되었나요 ? Java의 배열은 런타임에 구성 요소 유형을 알고 있습니다. 따라서 전달 된 배열 객체는 런타임에 잘못된 구성 요소 유형을 갖습니다.

varargs를 가장 일반적으로 사용하는 경우 요소를 반복하기 만하면 문제가되지 않으므로 (배열의 런타임 유형에 신경 쓰지 않아도 됨) 안전합니다.

@SafeVarargs
final <T> void foo(T... args) {
    for (T x : args) {
        // do stuff with x
    }
}

그러나 전달 된 배열의 런타임 구성 요소 유형에 의존하는 것은 안전하지 않습니다. 안전하지 않고 충돌하는 간단한 예는 다음과 같습니다.

class UnSafeVarargs
{
  static <T> T[] asArray(T... args) {
    return args;
  }

  static <T> T[] arrayOfTwo(T a, T b) {
    return asArray(a, b);
  }

  public static void main(String[] args) {
    String[] bar = arrayOfTwo("hi", "mom");
  }
}

문제는 여기에 우리의 유형에 따라 달라집니다 것입니다 args될 수 T[]로 복귀하기 위해 T[]. 그러나 실제로 런타임에 인수의 유형은의 인스턴스가 아닙니다 T[].

3) 메소드에 type 인수가있는 경우 T...(여기서 T는 모든 유형 매개 변수 임)

  • 안전 : 메소드가 배열의 요소가 인스턴스라는 사실에만 의존하는 경우 T
  • 안전하지 않은 경우 : 어레이가 인스턴스라는 사실에 의존하는 경우 T[]

어레이의 런타임 타입에 의존하는 상황이 포함 형으로 리턴 T[]형의 매개 인자로 전달 T[]이용 어레이 형 점점 .getClass()처럼 배열의 실행 종류에 따라 메소드에 전달 List.toArray()하고 Arrays.copyOf()

2) 위에서 언급 한 구별이 너무 복잡하여 자동으로 쉽게 구별 할 수 없습니다.


7
이제는 모든 것이 의미가 있습니다. 감사. 그래서 당신을 완전히 이해한다는 것을 알기 FOO<T extends Something>위해 varargs 메소드의 T []를 Somthings 의 배열로 반환하는 것이 안전 할 클래스 가 있다고 가정 해 봅시다 .
Oren

30
@SafeVarargs배열 을 사용 하는 유일한 일이 이미 주석이 달린 다른 메소드로 전달되는 경우 (예 : vararg 메소드를 작성하는 경우가 종종 있음)를 주목할 가치가 있습니다 . 를 사용하여 인수를 목록으로 변환하고 Arrays.asList(...)다른 메소드에 전달하기 위해 존재합니다 .이 경우 주석이 @SafeVarargs있기 때문에 항상 주석을 달 수 있습니다 Arrays.asList).
Jules

3
@newacct Java 7에서 이것이 사실인지 모르겠지만 Java 8은 @SafeVarargs메소드가 static또는 final그렇지 않으면 허용 되지 않습니다. 그렇지 않으면 파생 클래스에서 안전하지 않은 것으로 재정의 될 수 있기 때문입니다.
Andy

3
Java 9에서는 private재정의 할 수없는 메소드도 허용됩니다 .
Dave L.

4

모범 사례를 위해 이것을 고려하십시오.

이것이 있다면 :

public <T> void doSomething(A a, B b, T... manyTs) {
    // Your code here
}

이것을 다음과 같이 변경하십시오.

public <T> void doSomething(A a, B b, T... manyTs) {
    doSomething(a, b, Arrays.asList(manyTs));
}

private <T> void doSomething(A a, B b, List<T> manyTs) {
    // Your code here
}

나는 일반적으로 발신자에게 더 편리하게하기 위해 varargs 만 추가한다는 것을 알았습니다. 내부 구현에서을 사용하는 것이 거의 항상 더 편리합니다 List<>. 따라서 피기 백 (piggy-back) Arrays.asList()하고 힙 오염 (Hap Pollution)을 도입 할 수있는 방법이 없도록하기 위해 이것이 바로 제가하는 일입니다.

나는 이것이 당신의 # 3에만 대답한다는 것을 알고 있습니다. newacct는 위의 # 1 및 # 2에 대한 훌륭한 답변을 주었으며, 이것을 의견으로 남길만한 명성은 충분하지 않습니다. :피

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.