6

powiedzmy mamy klasę i przeciążenia funkcji:rozdzielczości przeciążenia odniesieniami metoda i specjalnościach interfejsu funkcja dla prymitywnych typów

public class Main { 
    static final class A { 
    } 

    public static String g(ToIntFunction<? extends A> f) { 
     return null; 
    } 

    public static String g(ToDoubleFunction<? extends A> f) { 
     return null; 
    } 
} 

i chcę zadzwonić g odniesieniem metoda funkcji typu A - > int:

public class Main { 
    static final class A { 
    } 

    public static String g(ToIntFunction<? extends A> f) { 
     return null; 
    } 

    public static String g(ToDoubleFunction<? extends A> f) { 
     return null; 
    } 

    private static int toInt(A x) { 
     return 2; 
    } 

    public static void main(String[] args) { 
     ToIntFunction<? extends A> f1 = Main::toInt; 
     ToDoubleFunction<? extends A> f2 = Main::toInt; 

     g(Main::toInt); 
    } 
} 

Działa z javac, ale nie z eclipse ecj. Przesłałem raport o błędzie do ecj, ale nie jestem pewien, czy jest to błąd typu ecj lub javac, i spróbowałem zastosować algorytm rozwiązywania przeciążenia, aby to zrozumieć. Mam wrażenie, że kod powinien zostać zaakceptowany, ponieważ intuicyjnie uzasadnione jest, że ToIntFunction lepiej pasuje do toInt niż ToDoubleFunction. Jednak moje czytanie JLS polega na tym, że należy go odrzucić, ponieważ nie ma usprawiedliwienia dla bycia bardziej szczegółowym.

Muszę przyznać, że jestem trochę zagubiony w specyfikacji JLS i doceniłbym jakąś pomoc. Najpierw chciałam obliczyć typ Main::double2int, więc przyjrzałem się 15.13.2. Type of a Method Reference. Nie określa typ, ale określa, kiedy typ jest zgodne w różnych kontekstach

Wyrażenie odniesienia sposób jest zgodny w kontekście przypisania, kontekście wywołania albo odlewanie związku z typem cel T jeśli T jest funkcjonalny typu interfejsu (§9.8), a ekspresja jest przystający podstawie rodzaju funkcji typu docelowego ziemia pochodzącego z T.

rodzaje mielone są ToIntFunction<A> i ToDoubleFunction<A>. toInt zwraca wartość int, która jest zgodna z liczbą podwójną, więc mógłbym wywnioskować, że odwołanie do metody jest kompatybilne z ToIntFunction<? extends A> i ToDoubleFunction<? extends A> w kontekście wywołania. Można to zweryfikować, przypisując nazwę metody zarówno do ToIntFunction<? extends A>, jak ii ToDoubleFunction<? extends A>, która jest akceptowana w funkcji głównej.

Potem spojrzałem w rozdzielczości przeciążenia i znalazł 15.12.2.5. Choosing the Most Specific Method który ma szczególny przypadek o referencje metoda zdecydować, które z tych dwóch przeciążeń dla ToIntFunction lub ToDoubleFunction jest bardziej specyficzny dla parametru Main::toInt deklaracji kompilacji A -> int.

funkcjonalny typu interfejs S jest bardziej szczegółowy niż typu interfejsu funkcjonalnego T do wyrażenia E, jeżeli T nie jest podtypem S, a jedna z poniższych sytuacji (gdzie U1 ... UK i R1 są rodzaje parametrów oraz rodzaj zwrotny typu funkcji do chwytania S i V1 ... Vk i R2 są rodzaje parametrów oraz rodzaj typu funkcji T) zwracają:

...

Jeśli e jest dokładnym wyrażeniem referencyjnym metody (§15.13.1), następnie i) dla all i (1 ≤ i ≤ k), Ui jest takie samo jak Vi, i ii) jedno z następujących jest prawdziwe:

R2 jest nieważny.

R1 <: R2.

R1 jest typem pierwotnym, R2 jest typem odniesienia, a deklaracja czasu kompilacji dla odwołania do metody ma typ zwracany, który jest typem pierwotnym .

R1 jest typem odniesienia, R2 jest typem pierwotnym, a deklaracja czasu kompilacji dla odwołania do metody ma typ zwracany, który jest typem odniesienia .

Pierwszy warunek oczywiście nie pasuje, ponieważ R1 i R2 nie są nieważne.

dwa interfejsy ToIntFunction i ToDoubleFunction różni się tylko ich rodzaje, które obie są proste typy double i int. W przypadku typów pierwotnych klauzula "R1 <: R2" jest zdefiniowana w 4.10.1 zgodnie z rozmiarem typów. Nie ma relacji między double i int, więc ten przypadek nie określa, który typ jest bardziej szczegółowy.

Dwa ostatnie punkty również nie pasują, ponieważ żaden z dwóch funkcjonalnych interfejsów nie ma zwracanej wartości typu referencyjnego.

Wydaje się, że nie ma reguły dla przypadku, gdy dwa funkcjonalne interfejsy zwracają prymitywy, a kod powinien zostać odrzucony jako niejednoznaczny. Jednak javac akceptuje kod i oczekiwałbym, że to zrobi. Zastanawiam się więc, czy jest to brakujący punkt w JLS.

Odpowiedz

3

Do podstawowych typów, klauzula „R1 <: R2” jest zdefiniowany w 4.10.1 według wielkości typów. Nie ma związku między podwójnym i wewnętrznym, więc ten przypadek nie określa, który typ jest bardziej szczegółowy.

Tak nie jest; double jest w rzeczywistości nadtypem int.

Paragraph 4.10:

W supertypes od typu uzyskuje się poprzez refleksyjne i przechodnich zamknięcia nad supertypem bezpośredniej relacji, napisany S >₁ T, który jest zdefiniowany przez zasad podanych w dalszej części tego rozdziału. Piszemy, że S :> T do wskazuje, że relacja supertype zawiera się między S i T.

Paragraph 4.10.1:

The następujących zasad określić bezpośredni związek między supertypem prymitywnych typów:

double >₁ float

float >₁ long

long >₁ int

supertypem związek będący zwrotny i przechodnia zamknięcie bezpośredni supertypem związku oznacza, że ​​z (double >₁ float) ∧ (float >₁ long) ∧ (long >₁ int) następujące double :> int.

+0

Przyjemny połów. I bardzo proste ... Więc wniosek będzie taki, że ToIntFunction lepiej pasuje niż ToDoubleFunction, a kod jest zgodny. W takim przypadku jest to błąd typu ecj. – Jens