development

생성자 참조-제네릭 배열이 생성 될 때 경고 없음

big-blog 2021. 1. 8. 22:46
반응형

생성자 참조-제네릭 배열이 생성 될 때 경고 없음


Java에서는 제네릭 유형의 배열을 직접 생성 할 수 없습니다.

Test<String>[] t1 = new Test<String>[10]; // Compile-time error

그러나 원시 유형을 사용하여이를 수행 할 수 있습니다.

Test<String>[] t2 = new Test[10]; // Compile warning "unchecked"

Java 8에서는 생성자 참조를 사용할 수도 있습니다.

interface ArrayCreator<T> {
    T create(int n);
}

ArrayCreator<Test<String>[]> ac = Test[]::new; // No warning
Test<String>[] t3 = ac.create(10);

마지막 경우 컴파일러가 경고를 표시하지 않는 이유는 무엇입니까? 여전히 원시 유형을 사용하여 배열을 생성합니다.


귀하의 질문이 정당합니다. 요컨대, 메소드 참조는 실제로 원시 유형을 사용하고 (또는 원시 유형을 사용해야 함) 제네릭 배열 생성이 금지 된 이유는 메소드 참조를 사용할 때 여전히 적용되므로 자동으로 생성하는 함수를 생성 할 수 있습니다. 일반 배열은 언어 디자인의 의도를 분명히 위반합니다.

제네릭 배열 생성이 금지 된 이유는 제네릭 이전 시대에서 유래 한 배열 유형 상속이 제네릭 유형 시스템과 호환되지 않기 때문입니다. 즉 다음과 같이 작성할 수 있습니다.

IntFunction<List<String>[]> af = List[]::new; // should generate warning
List<String>[] array = af.apply(10);
Object[] objArray = array;
objArray[0] = Arrays.asList(42);
List<String> list = array[0]; // heap pollution

여기에서 일부 답변과 달리 컴파일러는 일반 요소 유형을 추론하기 위해 표현식에 대한 유형 유추를 수행 하지 않는다는 점을 강조해야합니다 . 일반 배열 생성이 여전히 금지되어 있음을 쉽게 증명할 수 있습니다.List[]::newList<String>

IntFunction<List<String>[]> af = List<String>[]::new; // does not compile

List<String>[]::new는 불법 이므로 List[]::new경고없이 받아 들여지면 사실상 불법이라고 추론하여 이상 할 것 List<String>[]::new입니다.

JLS §15.13 은 다음과 같이 명시합니다.

메서드 참조 식의 형식이 ArrayType :: new 인 경우 ArrayType 은 수정 가능한 형식 (§4.7)을 나타내야합니다. 그렇지 않으면 컴파일 타임 오류가 발생합니다.

이것은 이미 그 의미 List<String>[]::new하기 때문에, 불법 List<String>reifiable없는 반면, List<?>[]::new합법적으로, List<?>reifiable이며, List[]::new우리가 고려하는 경우 법적 List원시 타입 은 AS, 원시 타입 List reifiable입니다.

그리고 §15.13.1은

메서드 참조 식의 형식이 ArrayType :: new 인 경우 단일 명목 메서드가 고려됩니다. 이 메서드에는 유형의 단일 매개 변수 int가 있고 ArrayType을 반환하며 throws절이 없습니다 . 경우 N = 1 , 이것이 유일한 가능성 적용 방법; 그렇지 않으면 잠재적으로 적용 가능한 방법이 없습니다.

즉, List[]::new위 표현식 의 동작 은 다음과 같이 작성한 것과 동일합니다.

    IntFunction<List<String>[]> af = MyClass::create;
private static List[] create(int i) {
    return new List[i];
}

방법 create이 단지 개념적 이라는 점을 제외하고 . 그리고 실제로이 명시 적 방법 선언으로 만가 원시 형 상기 경고 create방법,하지만 선택하지 않은 변환에 대한 경고 List[]List<String>[]방법 참조에서이. 따라서 List[]::new원시 유형을 사용하는 메소드가 개념적 일 뿐인 경우, 즉 소스 코드에 존재하지 않는 경우 컴파일러에서 어떤 일이 발생하는지 이해할 수 있습니다.

그러나 확인되지 않은 경고가 없다는 것은 JLS §5.1.9, 확인되지 않은 변환 의 명백한 위반입니다 .

하자 G가있는 제네릭 형식 선언의 이름을 n 개의 유형 매개 변수를.

체크 변환 원료 클래스 또는 인터페이스 유형 (§4.8)의 G형태 중 어느 한 파라미터 유형이 G<T₁,...,Tₙ>.

원시 배열 유형 G[]ᵏ에서 양식의 모든 배열 유형으로 확인되지 않은 변환이 있습니다 G<T₁,...,Tₙ>[]ᵏ. (표기법 []ᵏk 차원 의 배열 유형을 나타냅니다 .)

확인되지 않은 변환을 사용하면 모든 유형 인수 ᵢ (1 ≤ in )이 제한되지 않은 와일드 카드 (§4.5.1)이거나 확인되지 않은 경고가 주석 (§9.6.4.5)에 의해 억제되지 않는 한 컴파일시 확인 되지 않은 경고가 발생합니다.TSuppressWarnings

So, a conversion of List[] to List<?>[] is legal, as List is parameterized with an unbounded wildcard, but the conversion from List[] to List<String>[] must produce an unchecked warning, which is crucial here, as the use of List[]::new does not produce the raw type warning that appears with an explicit creation method. The absence of raw type warnings seems not to be a violation (as far as I understood §4.8) and it wouldn’t be a problem, if javac created the required unchecked warning.


The best that I can come up with is that the JLS specifies that a method reference to the constructor of a generic type infers the generic parameters: "If a method or constructor is generic, the appropriate type arguments may either be inferred or provided explicitly." Later it gives ArrayList::new as an example and describes it as "inferred type arguments for generic class," thus establishing that ArrayList::new (and not ArrayList<>::new) is the syntax that infers arguments.

Given a class:

public static class Test<T> {
    public Test() {}
}

this gives a warning:

Test<String> = new Test(); // No <String>

but this doesn't:

Supplier<Test<String>> = Test::new; // No <String> but no warning

because Test::new implicitly infers the generic arguments.

So I assume that a method reference to an array constructor works the same way.


It still uses raw type to create the array, right?

Java generics are just a compile-time illusion, so the raw type will of course be used at runtime to create the array.

Why doesn't the compiler display the warning in the last case?

Yes, the unchecked cast from Test[] to Test<String>[] is still happening; it's just happening behind the scenes in an anonymous context.

Test<String>[] t3 = ((IntFunction<Test<String>[]>) Test[]::new).apply(10);

Since the anonymous method is doing the dirty work, the unchecked cast effectively disappears from the managed code.

ReferenceURL : https://stackoverflow.com/questions/42346895/constructor-reference-no-warning-when-generics-array-is-created

반응형