11
import java.util.function.*; 

class Test { 
    void test(int foo, Consumer<Integer> bar) { } 
    void test(long foo, Consumer<Long> bar) { } 
    void test(float foo, Consumer<Float> bar) { } 
    void test(double foo, Consumer<Double> bar) { } 
} 

Wenn ich kompilieren dies mit javac -Xlint Test.java bekomme ich ein paar Warnungen:Achtung: [Überlastungen] Methode m1 ist möglicherweise nicht eindeutig mit Methode m2

Test.java:4: warning: [overloads] test(int,Consumer<Integer>) in Test is potentially ambiguous with test(long,Consumer<Long>) in Test 
    void test(int foo, Consumer<Integer> bar) { } 
     ^
Test.java:6: warning: [overloads] test(float,Consumer<Float>) in Test is potentially ambiguous with test(double,Consumer<Double>) in Test 
    void test(float foo, Consumer<Float> bar) { } 
     ^
2 warnings 

Wenn ich Consumer zu Supplier die Warnungen ändern verschwinden. Dieses Programm ist Warnung frei:

import java.util.function.*; 

class Test { 
    void test(int foo, Supplier<Integer> bar) { } 
    void test(long foo, Supplier<Long> bar) { } 
    void test(float foo, Supplier<Float> bar) { } 
    void test(double foo, Supplier<Double> bar) { } 
} 

Warum ist das? Was bedeutet diese Warnung? Wie sind diese Methoden zweideutig? Ist es sicher, die Warnung zu unterdrücken?

+0

Was passiert, wenn Sie versuchen, die Warnfunktionen aufzurufen? –

+1

Es scheint, dass die Warnung nur ausgegeben wird, wenn: a. es ist eine funktionale Schnittstelle (d. h. Verbraucher, Lieferant), UND b. Jede der Methoden der Schnittstelle enthält irgendwelche Parameter ... Als Test habe ich meine eigenen Kopien der Verbraucher/Lieferanten-Schnittstellen gemacht und mit ihnen gezwirbelt. Leider weiß ich nicht genug über Java-Funktionalität, um zu wissen, warum eine Warnung dafür erzeugt wird. – Kai

+0

Korrektur: Punkt A ist falsch, es muss keine funktionale Schnittstelle sein, nur irgendeine Schnittstelle (Klasse erzeugt keine Warnung), wobei nur eine (nicht standardmäßige) Methode definiert ist. Außerdem habe ich die Parameter von test (int, Consumer) auf etwas geändert, das fast unmöglich ist, mehrdeutig zu sein wie test (Object, Consumer) vs. test (Map, Consumer) und trotzdem wird die Warnung noch geworfen. Als solches, wenn Sie nicht wirklich etwas verrücktes Programmieren verwenden, denke ich wirklich nicht, dass Sie sich darum kümmern müssen. – Kai

Antwort

16

Diese Warnungen treten aufgrund der lustigen Überschneidung zwischen der Überladungsauflösung, der Zieltypisierung und der Typinferenz auf. Der Compiler denkt etwas voraus und warnt Sie, da die meisten Lambdas ohne explizit deklarierte Typen geschrieben werden. Betrachten wir zum Beispiel diesen Aufruf:

test(1, i -> { }); 

Was ist die Art von i? Der Compiler kann nicht darauf schließen, bis die Überladungsauflösung abgeschlossen ist ... aber der Wert 1 stimmt mit allen vier Überladungen überein. Welche Überladung auch gewählt wird, würde den Zieltyp des zweiten Arguments beeinflussen, was sich wiederum auf den Typ auswirken würde, der für i abgeleitet wird. Es ist wirklich nicht genug Informationen hier für den Compiler, welche Methode zu entscheiden, zu nennen, so dass diese Linie tatsächlich zu einem Fehler bei der Kompilierung:

error: reference to test is ambiguous 
      both method test(float,Consumer<Float>) in Test and 
      method test(double,Consumer<Double>) in Test match 

(Interessanterweise erwähnt die float und double Überlastungen, aber wenn Sie eine davon aus kommentieren, erhalten Sie den gleichen Fehler in Bezug auf die long Überlastung.)

man könnte eine Politik vorstellen, wo der Compiler die Überladungsauflösung vervollständigten die meisten spezifischen Regel verwendet wird, wodurch die Wahl der Überlastung der int arg. Es hätte dann einen bestimmten Zieltyp für das Lambda. Die Compilerdesigner waren der Meinung, dass dies zu subtil war und dass es Fälle geben würde, in denen Programmierer überrascht sein würden, welche Überladung letztendlich aufgerufen wurde. Anstatt Programme auf eine möglicherweise unerwartete Weise zu kompilieren, hielten sie es für sicherer, dies zu einem Fehler zu machen und den Programmierer dazu zu zwingen, es zu disambiguieren.

Der Compiler gibt bei den Methodendeklarationen Warnungen aus, um anzuzeigen, dass der wahrscheinliche Code, den ein Programmierer schreiben würde, um eine dieser Methoden aufzurufen (wie oben gezeigt), zu einem Fehler bei der Kompilierung führt.

den Anruf eindeutig zu machen, würde man stattdessen

test(1, (Integer i) -> { }); 

oder erklärt einen anderen expliziten Typen für die i Parameter zu schreiben. Eine andere Möglichkeit besteht darin, vor dem Lambda einen Cast einzufügen: Dies ist jedoch wohl noch schlimmer. Sie möchten wahrscheinlich nicht, dass Anrufer Ihrer API zu jedem Zeitpunkt mit solchen Dingen ringen müssen.

Diese Warnungen treten für den Fall Supplier nicht auf, da der Typ eines Lieferanten über eine lokale Argumentation bestimmt werden kann, ohne irgendeine Art von Rückschluss.

Sie werden wahrscheinlich die Art, wie Sie diese API zusammenstellen, überdenken wollen.Wenn Sie Methoden mit diesen Argumenttypen wirklich wollen, können Sie die Methoden testInt, testLong usw. umbenennen und Überlastung vermeiden. Beachten Sie, dass die Java SE-APIs dies in ähnlichen Fällen getan haben, z. B. comparingInt, comparingLong und comparingDouble unter Comparator; und auch mapToInt, mapToLong und mapToDouble auf Stream.

+0

Sehr schöne Erklärung! Keine Fragen mehr nach dem Lesen. – TWiStErRob

Verwandte Themen