이것은 문제가되는 코드의 단순화 된 버전입니다. 하나의 제네릭 클래스는 제네릭 형식 매개 변수가있는 다른 클래스를 사용하며 제네릭 형식 중 하나를 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에게 가변 인자 방법과 형식 매개 변수 사용하면 향상된 컴파일러 경고 및 오류를 기술 노트를.