2015-11-12 10 views
5

Zacznijmy od 3 interfejsów. To, co robią, nie jest ważne. Zwróć uwagę, że samochód jest sparametryzowany(), natomiast Foo i Bar nie.Parametry powiązania w metodzie ogólnej kończą się niepowodzeniem, gdy działa równoważny interfejs ogólny, dlaczego?

interface Foo        {void testFoo();} 
interface Bar        {void testBar();} 
interface Car<A>       {A testCar();} 

chcę „kompozytowych” tych interfejsów, a to działa dobrze, jeśli jawnie tworzenia kompozytów tak:

interface FooBar  extends Foo,Bar  {} 
interface FooCar<A>  extends Foo,Car<A> {} 

Jednak ja dużo wolę w sposób dorozumiany poprzez interfejsy kompozytowe ograniczone deklaracje typów różnych metod. Na przykład:

public <T extends Foo & Bar>  T implicitFooBar() {return null;} 
public <X, T extends Foo & Car<X>> T implicitFooCar() {return null;} 

działa:implictFooBar() metoda zwraca typ T, który implementuje oba interfejsy i Foo Bar (kompozyt, jeśli będzie). Połączenie tego sposobu kompiluje i można uniknąć konieczności wyraźnie stwierdzenie interfejs fooBar:

// implicit composition of Foo and Bar, GOOD 
FooBar implicitFooBar = implicitFooBar(); 
implicitFooBar.testFoo(); 
implicitFooBar.testBar(); 

niepowodzeniem: jednak wywołanie implicitFooCar() z kompilacją. Komunikat o błędzie jest „Metoda implicitFooCar() w typie GenericsTest nie dotyczy argumentów()” (I zawinięte moje kodu testu w klasie o nazwie GenericsTest.) Tylko

// implicit composition of Foo and Car<X>, FAIL! 
//Compiler says "The method implicitFooCar() in the type GenericsTest is not applicable for the arguments()" 
FooCar<Number> implicitFooCar = implicitFooCar(); //compile error on method call 
implicitFooCar.testFoo();       
Number n2 = implicitFooCar.testCar(); 

Błąd kompilatora pokazach gdy deklaracja typu jest złożona i sparametryzowana. Na przykład, zarówno tych kompilacji i można nazwać po prostu w porządku:

public <X>      Car<X> justCar()  {return null;} 
public <X, T extends Car<X>> T  implicitCar() {return null;} 

pytanie ...

podejrzewam to ma coś wspólnego z typem skasowaniem, ale chciałbym zrozumieć szczegóły co tu się dzieje. Czytałem samouczki Oracle Generics, ale nie widzę jakiej kombinacji reguł metoda implicitFooCar() narusza, podczas gdy implicitFooBar() i implicitCar() są w porządku. Szukam wyjaśnienia w stylu akademickim, a nie tylko pracy.

Bonus

Co ciekawe, następujący wariant wywołanie implicitFooCar() metoda działa (brak błędów kompilatora.) Wskazuje na to, dlaczego druga wersja nie działa, ale mam jeszcze połączyć te kropki .

//variant... GOOD... but why? 
implicitFooCar = this.<Number,FooCar<Number>>implicitFooCar(); 

Code Test (całość)

Jeśli chcesz grać z kodem, to jest tutaj jako jednej klasy.

public class GenericsTest { 

    public static interface Foo        {void testFoo();} 
    public static interface Bar        {void testBar();} 
    public static interface Car<A>       {A testCar();} 

    public static interface FooBar  extends Foo,Bar  {} 
    public static interface FooCar<A> extends Foo,Car<A> {} 



    public <X>       Car<X>  justCar()   {return null;} 

    public        FooBar  explicitFooBar() {return null;} 
    public <T extends Foo & Bar>  T   implicitFooBar() {return null;} 

    public <X>       FooCar<X> explicitFooCar() {return null;} 
    public <X, T extends Foo & Car<X>> T   implicitFooCar() {return null;} 

    public <X, T extends Car<X>>  T   implicitCar()  {return null;} 


    public void test() { 
     justCar().testCar(); 

     // explicit composition of Foo and Bar, GOOD 
     FooBar explicitFooBar = explicitFooBar(); 
     explicitFooBar.testFoo(); 
     explicitFooBar.testBar(); 

     // explicit composition of Foo and Car<X>, GOOD 
     FooCar<Number> explicitFooCar = explicitFooCar(); 
     explicitFooCar.testFoo(); 
     Number n1 = explicitFooCar.testCar(); 

     // implicit composition of Foo and Bar, GOOD 
     FooBar implicitFooBar = implicitFooBar(); 
     implicitFooBar.testFoo(); 
     implicitFooBar.testBar(); 

     // implicit composition of Foo and Car<X>, FAIL! 
     //Compiler says "The method implicitFooCar() in the type GenericsTest is not applicable for the arguments()" 
     FooCar<Number> implicitFooCar = implicitFooCar(); //compile error on method call 
     implicitFooCar.testFoo();       
     Number n2 = implicitFooCar.testCar(); 
     //variant... GOOD... but why? 
     implicitFooCar = this.<Number,FooCar<Number>>implicitFooCar(); 

     // implicit (no composition) Car<X>, GOOD 
     Car<Number> implicitCar = implicitCar(); 
     Number n3 = implicitCar.testCar(); 

    } 

} 

UPDATE

To kompiluje z javac wersji 1.8.0_60 (i _45 za komentarze), ale Eclipse (wersja 4.4.2.M20150204-1700) wbudowane w kompilator ETS zgłasza błąd, o którym mowa powyżej. Dodałem znacznik zaćmienia do tego pytania, ponieważ może to być problem EJC.

+1

Twój przykładowy kod u dołu kompiluje się dla mnie (Java 1.8.0_45). Jaka jest twoja wersja Java? – rgettman

+1

Widzę, co próbujesz zrobić, ale to nie zadziała. W szczególności podpis, który napisałeś dla 'implicitFooBar', twierdzi, że dla każdego typu, który implementuje' Foo' i 'Bar', może zwrócić ten typ. To zadziała tylko, jeśli zwrócisz 'null'; nie zadziała, jeśli spróbujesz zwrócić rzeczywisty obiekt. System typu Java nie może wyrazić tego, co próbujesz zrobić. –

+0

@LouisWasserman Dla tła ... Pracuję nad płynnym API. Wyraźne interfejsy zawierają listę dostępnych akcji w punktach w składni API, a niejawne interfejsy są wymagane, gdy wiele zestawów akcji jest możliwych w składni. Dobra uwaga dotycząca implementacji, która obecnie zwraca wartość null. Nie zacząłem kodować implementacji (po prostu tłumaczenie specyfikacji API do tej pory) i prawdopodobnie będę potrzebował tych pośrednich interfejsów w tym momencie. – allenru

Odpowiedz

2

Po pierwsze, aby bezpośrednio odpowiedzieć na moje pytanie ...Możliwe jest zadeklarowanie metody z powiązanym parametryzowanym typem, który łączy dwa interfejsy, z których jeden jest sparametryzowany.

implicitFooCar() metoda z moim przykładzie powiodło się z powodu błędu w kompilatora Eclipse ETS stosowanego w Luna, wersja 4.4 Eclipse. Ten sam kod skompilowany przy użyciu kompilatora javac (v 1.8.0_60) oraz w następnej wersji Eclipse, Mars (4.5).

Po drugie, moja chęć uniknięcia wyraźnych deklaracji tego, co uważałem za tymczasowy lub pośredni interfejs złożony, była krótkowzroczna. Louis Wasserman pointed out, że metoda w pewnym momencie będzie musiała zwrócić obiekt, który jest zgodny z tą złożoną specyfikacją, a zatem będę potrzebował wyraźnej wersji w tym punkcie.

Powiązane problemy