2016-06-09 2 views
2

Ich bin auf etwas gestoßen, das mich beim Schreiben von Code stört. Ich habe die beiden Beispiele im folgenden Codebeispiel zusammengestellt.Inkompatible Typen: Klasse <Bar> kann nicht in Klasse <CAP#1> konvertiert werden, wobei CAP # 1 eine Variable vom Typ Fresh ist

Die Zeile cls1 verwendet einen Lambda-Ausdruck, kompiliert jedoch nicht, während die Zeile cls2 eine Methodenreferenzen verwendet und kompiliert. Ich weiß, dass ich keine Probleme habe, wenn ich nicht-generische Objekte verwende, aber hier verwende ich Generika und insbesondere Wildcards.

import java.lang.annotation.*; 
import java.util.Optional; 

public class MCVE { 

    static class Foo {} 
    static class Bar extends Foo {} 

    @Retention(RetentionPolicy.RUNTIME) 
    @Target(ElementType.TYPE) 
    static @interface Baz { Class<? extends Foo> value(); } 

    @Baz(Bar.class) 
    static class Quz {} 

    // Lambda expression - doesn't compile 
    Class<? extends Foo> cls1 = Optional.ofNullable(Quz.class.getAnnotation(Baz.class)) 
     .map(baz -> baz.value()) 
     .orElse(Bar.class); 

    // Method reference - compiles 
    Class<? extends Foo> cls2 = Optional.ofNullable(Quz.class.getAnnotation(Baz.class)) 
     .map(Baz::value) 
     .orElse(Bar.class); 

} 

In der Funktionalität machen beide Linien das gleiche. Ich verstehe einfach nicht, was unter dem Dach passiert, dass die Verwendung eines Lambda-Ausdrucks fehlschlägt, während eine Methodenreferenz kein Problem hat.

Für alle, die fragen werden, erhielten die Fehler während des folgenden Zusammenstellung:

MCVE.java:25: error: incompatible types: Class<Bar> cannot be converted to Class<CAP#1> 
     .orElse(Bar.class); 
       ^
    where CAP#1 is a fresh type-variable: 
    CAP#1 extends Foo from capture of ? extends Foo 
Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output 
1 error 

Dies ist eher lakonisch und bietet keine extrem nützliche Information.

Bitte beachten Sie auch, dass ich meine Nachforschungen gemacht habe und dass dieser Fehler nicht derselbe ist wie in den "Ähnliche Fragen", die Sie auf der rechten Seite sehen können. Der interessante Teil hier ist "Fresh-Type Variable".

+0

http://stackoverflow.com/questions/20543966/incompatible-types-and-fresh-type -Variable – Hosseini

+1

Im Allgemeinen: Javas Typsystem ist in diesen Tagen so ziemlich eine Black Box, und es gibt nicht viele allgemeine Regeln darüber, wann explizite Typen notwendig und unnötig sind. –

+0

@ Hosseini Vielen Dank für diesen Link. Es ist viel näher an dem, was ich erlebt habe, als alles, was ich in "Ähnliche Fragen" gesehen habe, es handelt sich um eine Aufgabe - was hier nicht relevant ist, denke ich - Es geht nicht um die Frage, warum es mit einem Lambda nicht OK ist. aber OK mit einer Methodenreferenz. –

Antwort

1

Stellen Sie sich vor Sie haben drei Klassen:

static class Foo {} 
static class Bar extends Foo {} 
static class Dem extends Foo {} 

Compiler aus Lambda-Ausdruck finden:

var x1 = Optional.ofNullable(Quz.class.getAnnotation(Baz.class)); 
// typeof x1 = Optional<Baz> 

var x2 = x1.map(baz -> baz.value()) 
// typeof x2 = Optional<Class<T>>, where <T extends Foo> - this is the black magic you suffer with 
// E.g. it can be 
// a) Optional<Class<T=Foo>> or 
// b) Optional<Class<T=Bar>> or 
// c) Optional<Class<T=Dem>> 

var x3 = x2.orElse(Bar.class); 
// In case (a) and (b) - this code should work, in case (c) it should fail. 
// Without additional explicit hint (conversion) compiler reports about this issue. 

Wenn Sie Methode Referenz verwenden - Compiler ignoriert beschrieben Typinferenz und verwendet ursprünglichen Erklärung Baz-Typ, so

.map(Baz::value) // is identical to 
.map(baz -> (Class<? extends Foo>) baz.value()) 
+0

Etwas nervt mich mit dieser Erklärung, weshalb ich noch nicht akzeptiert habe. Es ist absolut kein Unterschied zwischen deinen 'Bar'- und' Dem'-Klassen, doch du sagst, dass es anders ist: "Im Fall (a) und (b) sollte dieser Code funktionieren, falls (c) er fehlschlagen sollte. " Das ist so widersprüchlich, ich komme einfach nicht dazu um es richtig zu verstehen. Vielleicht hast du irgendwo einen Tippfehler? –

+0

Wenn T = Der haben Sie typeof x2 = Optional >, so dass Sie orElse (Bar.class) nicht anwenden können - Der und Bar sind inkompatibel. Sie können das Objekt nicht in Bar umwandeln und umgekehrt. Dies ist nur ein theoretisches Beispiel, um Compiler-Bedenken aufzuzeigen. – ursa

+0

Okay, deine Absicht ist viel klarer und ich verstehe, was du meintest. Danke für alles :) –

Verwandte Themen