2016-01-26 10 views
5

Antwort läuft darauf hinaus, eine similar questionWarum kann der Java-Compiler die Vererbung nicht richtig ableiten?

-Java does not support lower bounds on parameterized methods, because such a feature is "not useful enough" finden Sie die folgenden Ausschnitt Gegeben:

package demo; 

public class Demo { 
    interface Foo { void foo(); } 
    interface Bar { void bar(); } 
    interface FooBar { 
     <R extends Foo & Bar> R foobar(); 

     static FooBar create() { return new TypicalJavaFooBar(); } 
    } 

    private static final class TypicalJavaFooBar implements Foo, Bar, FooBar { 
     public void bar() { System.out.println("foo"); } 
     public void foo() { System.out.println("bar"); } 

     public <R extends Foo & Bar> R foobar() { 
      return (R) this; 
     } 
    } 

    public static void main(String[] args) { 
     FooBar x = FooBar.create(); 
     Foo foo = x.foobar(); 
     Bar bar = x.foobar(); 
     x.foobar().foo(); 
     x.foobar().bar(); 
    } 
} 

Ohne die explizite Umwandlung zu R in TypicalJavaFooBar#foobar Compiler mit dem folgenden Fehler fehlschlägt

Error:(13, 20) java: incompatible types: demo.Demo.TypicalJavaFooBar cannot be converted to R

Meine Frage ist warum? Für mich scheint es, dass der Compiler genügend Informationen haben sollte, da TypicalJavaFooBar klar definiert ist, sowohl Foo als auch Bar zu implementieren; Warum ist das nicht genug, um die Foo & Bar Einschränkung zu erfüllen?

UPDATE

Das Hauptziel dieser Übung ist es, den folgenden Auftrag zu definieren: Aufruf der Methode foobar auf einer Instanz eines FooBar garantiert etwas zurückzukehren, die sowohl Foo und Bar implementiert.

+0

@horatius Das hat nichts mit dieser Frage zu tun. – chrylis

+0

In 'TypicalJavaFooBar' können Sie den Rückgabetyp der' foobar' Methode in 'TypicalJavaFooBar' ändern, wodurch die Besetzung ebenfalls entfällt. Während dies Ihre Frage nicht beantwortet, würde ich dies wahrscheinlich in dieser Situation tun.Obwohl ich die Komposition generell über der Vererbung bevorzuge, ist es unwahrscheinlich, dass ich auf diese Situation stoße. :-) – Waldheinz

+0

@Andrey, leider ist dein "Hauptziel" in Javas Typsystem eigentlich nicht möglich. Sie können etwas zurückgeben, das garantiert eine bestimmte Schnittstelle implementiert, aber Sie können nicht "etwas, das diese beiden Schnittstellen implementiert" ausdrücken. –

Antwort

6

Der Typparameter R ist an die Methode durch den aufrufenden Code gebunden und könnte theoretisch Baz implements Foo, Bar; siehe zB Collections.emptySet(), dessen Typparameter vom Aufrufer bestimmt wird und von einem Typzeugen beeinflusst werden kann.

Um zu tun, was Sie anscheinend versuchen, müssten Sie den Typ Parameter auf die Schnittstelle FooBar verschieben und TypicalJavaFooBar implements Foo, Bar, FooBar<TypicalJavaFooBar> haben.

1

Ihre R kann jeder Art sein, die ein Foo & Bar ist so können Sie

class MyFooBar implements Foo, Bar { ... 


FooBar x = new TypicalJavaFooBar(); 
MyFooBar mfb = x.foobar(); // this compiles now with the cast. 
0

Nein schreiben, kann der Compiler nicht die Art von R Inferenz und Ihre Besetzung ist falsch. Um zu sehen, warum gerade Ihre Klasse wie folgt erweitern:

static class C extends TypicalJavaFooBar { 

    void eat() {} 

    @Override 
    public void bar() {} 

    @Override 
    public void foo() {} 
} 

Dann Ihre Cast ermöglicht dies:

FooBar x = new TypicalJavaFooBar(); 
C c = x.foobar(); 
c.eat(); 

, die in einem Runtime Ergebnisse:

Exception in thread "main" java.lang.ClassCastException: demo.Demo$TypicalJavaFooBar cannot be cast to demo.Demo$C 

Aus diesem Grund - - Die Entwickler des Compilers würden das sicherlich nicht zulassen. Es ist etwas falsch im Code, daher die Notwendigkeit der Besetzung.

+0

Ich aktualisierte die Antwort, ich war nur Ich versuche, das Demo kurz zu halten ... ehrlich gesagt finde ich nicht-finale, öffentliche Klassen oder Abschlussklassen mit öffentlichen Konstrukteuren seit dem Erscheinen von Java 8 eine horrende Praxis. Außerdem vermisst du den Punkt. Der Punkt hier ist, den folgenden Vertrag explizit zu machen: Aufruf Methode "Foobar" auf einer Instanz eines "FooBar" ist garantiert, dass * etwas *, das sowohl "Foo" und "Bar" implementiert. – Andrey

+1

Wenn Sie nicht-finale, öffentliche Klassen für entsetzlich halten, werden Sie andere Features der Sprache nicht mögen, wie die Notwendigkeit von Cast, um das Risiko von ClassCastException zur Laufzeit zu zeigen. – smarquis

+0

statische Factory-Methoden in Schnittstellen = nicht mit öffentlichen Klassen. Außerdem würde Foo & Bar * als * untere * Grenze definiert, dh: "", ich sehe keinen triftigen Grund, warum die Angabe von unteren Grenzen nicht unterstützt wird ... – Andrey

0

Wenn Sie dies in Java verwenden könnte

if (obj instanceof (Foo & Bar)) 

dann würden Sie nicht die Besetzung machen gezwungen werden.

+0

Notwendigkeit der Besetzung ist hier, um das Risiko von java.lang.ClassCastException zur Laufzeit zu zeigen. – smarquis

+1

Könnte es sein..IDK ... aber für mich gibt es keinen logischen Unterschied zwischen 'if (obj instanceof (Foo & Bar))' und 'if ((obj instanceof Foo) & (Obj instanceof Bar))', aber das erste kompiliert jedoch nicht –

Verwandte Themen