[java] “varargs 매개 변수에 대해 일반 T 배열이 만들어집니다”컴파일러 경고를 해결할 수 있습니까?

이것은 문제가되는 코드의 단순화 된 버전입니다. 하나의 제네릭 클래스는 제네릭 형식 매개 변수가있는 다른 클래스를 사용하며 제네릭 형식 중 하나를 varargs 매개 변수가있는 메서드에 전달해야합니다.

class Assembler<X, Y> {
    void assemble(X container, Y... args) { ... }
}

class Component<T> {
    void useAssembler(T something) {

        Assembler<String, T> assembler = new Assembler<String, T>();

        //generates warning:
        // Type safety : A generic array of T is
        // created for a varargs parameter
        assembler.assemble("hello", something);
    }

}

이 경고가 발생하지 않고 일반 매개 변수를 varargs 메서드에 전달하는 올바른 방법이 있습니까?

물론 같은

assembler.assemble("hello", new T[] { something });

일반 배열을 만들 수 없으므로 작동하지 않습니다.



답변

를 추가하는 것 외에는 @SuppressWarnings("unchecked")그렇게 생각하지 않습니다.

버그 보고서 는 더 많은 정보를 가지고 있지만 일반적인 유형의 배열을 좋아하지 않는 컴파일러로 요약됩니다.


답변

Tom Hawtin은 이것을 주석으로 지적했지만 더 명확하게 말하면 예 : (잠재적으로 많은 호출 사이트가 아닌) 선언 사이트에서이를 해결할 수 있습니다 .JDK7로 전환하십시오.

Joseph Darcy의 블로그 게시물 에서 볼 수 있듯이 Java 7에 대한 약간의 점진적 언어 개선을 선택하기위한 Project Coin 연습은 Bob Lee의 제안 을 받아 들였습니다 @SuppressWarnings("varargs"). 안전한.

이것은 이 커밋 으로 OpenJDK에서 구현되었습니다 .

이것은 프로젝트에 유용하거나 유용하지 않을 수 있습니다 (많은 사람들이 JVM의 시험판 불안정 버전으로 전환하는 것을 좋아하지 않을 것입니다!) )가 유용하다는 것을 알게 될 것입니다.


답변

유창한 인터페이스를 사용한 경우 빌더 패턴을 사용해 볼 수 있습니다. varargs만큼 간결하지는 않지만 형식이 안전합니다.

정적 제네릭 형식의 메서드는 빌더를 사용할 때 형식 안전성을 유지하면서 보일러 플레이트의 일부를 제거 할 수 있습니다.

빌더

public class ArgBuilder<T> implements Iterable<T> {

    private final List<T> args = new ArrayList<T>();

    public ArgBuilder<T> and(T arg) {
        args.add(arg);
        return this;
    }

    @Override
    public Iterator<T> iterator() {
        return args.iterator();
    }

    public static <T> ArgBuilder<T> with(T firstArgument) {
        return new ArgBuilder<T>().and(firstArgument);
    }
}

그것을 사용하여

import static com.example.ArgBuilder.*;

public class VarargsTest {

    public static void main(String[] args) {
        doSomething(new ArgBuilder<String>().and("foo").and("bar").and("baz"));
        // or
        doSomething(with("foo").and("bar").and("baz"));
    }

    static void doSomething(Iterable<String> args) {
        for (String arg : args) {
            System.out.println(arg);
        }
    }
}


답변

vararg 메서드 호출에서 Object에 매개 변수를 명시 적으로 캐스팅하면 @SuppressWarnings를 사용하지 않고도 컴파일러가 만족할 것입니다.

public static <T> List<T> list( final T... items )
{
    return Arrays.asList( items );
}

// This will produce a warning.
list( "1", 2, new BigDecimal( "3.5" ) )

// This will not produce a warning.
list( (Object) "1", (Object) 2, (Object) new BigDecimal( "3.5" ) )

// This will not produce a warning either. Casting just the first parameter to 
// Object appears to be sufficient.
list( (Object) "1", 2, new BigDecimal( "3.5" ) )

여기서 문제는 컴파일러가 어떤 구체적인 유형의 배열을 만들지 알아야한다는 것입니다. 메소드가 제네릭이 아닌 경우 컴파일러는 메소드의 유형 정보를 사용할 수 있습니다. 이 메소드가 일반적인 경우, 호출시 사용 된 매개 변수를 기반으로 배열 유형을 파악하려고 시도합니다. 매개 변수 유형이 균질 한 경우 해당 작업이 쉽습니다. 그것들이 다양하다면, 컴파일러는 제 생각에 너무 영리하려고 노력하고 공용체 타입의 제네릭 배열을 만듭니다. 그런 다음 그것에 대해 경고해야합니다. 더 간단한 해결책은 type을 더 좁힐 수 없을 때 Object []를 만드는 것입니다. 위의 해결책은 바로 그것을 강제합니다.

이를 더 잘 이해하려면 다음 list2 메소드와 비교하여 위의 list 메소드를 호출하십시오.

public static List<Object> list2( final Object... items )
{
    return Arrays.asList( items );
}


답변

Java 7부터 @SafeVarargs 를 메소드에 추가 할 수 있으며 클라이언트 코드에 주석을 달 필요가 없습니다.

class Assembler<X, Y> {

    @SafeVarargs
    final void assemble(X container, Y... args) {
        //has to be final...
    }
}


답변

메소드를 오버로드 할 수 있습니다. 이렇게해도 문제가 해결되지는 않지만 경고 수를 최소화합니다 (예, 해킹입니다).

class Assembler<X, Y> {
  void assemble(X container, Y a1) { ... }
  void assemble(X container, Y a1, Y a2) { ... }
  void assemble(X container, Y a1, Y a2, Y a3) { ... }
  void assemble(X container, Y a1, Y a2, Y a3, Y a4) { ... }
  void assemble(X container, Y... args) { ... }
}


답변

해결하는 것은 매우 쉬운 문제입니다 : Use List<T>!

참조 유형의 배열은 피해야합니다.

Java (1.7)의 현재 버전 @SafeVargs에서는 호출자로부터 경고를 제거하는 메소드를 표시 할 수 있습니다 . 그래도 조심하고 레거시 어레이 없이도 더 나아질 수 있습니다.

또한 참조 비 Reifiable에게 가변 인자 방법과 형식 매개 변수 사용하면 향상된 컴파일러 경고 및 오류를 기술 노트를.