2011-01-07 8 views
8

Dato un'interfaccia genericaJava generici: Illegal di riferimento in avanti

interface Foo<A, B> { } 

Voglio scrivere un'implementazione che richiede una di essere una sottoclasse di B. Quindi voglio fare

class Bar<A, B super A> implements Foo<A, B> { } 
// --> Syntax error 

o

class Bar<A extends B, B> implements Foo<A, B> { } 
// --> illegal forward reference 

Ma l'unica soluzione che sembra funzionare è questo:

class Bar<B, A extends B> implements Foo<A, B> { } 

che è un po 'brutto, perché inverte l'ordine dei parametri generici.
Esistono soluzioni o soluzioni alternative a questo problema?

+1

Io non la penso così :( –

+0

generici Java sono un'implementazione di modelli C++ caramelle rivestite, per una buona ragione - retrocompatibilità con la base di codice esistente.Non ti aspetti di essere felice di loro in ogni momento.Non prendi in giro generici divertenti e allegri! – DwB

+0

@dwb: Huh? I generici Java si comportano * in modo completamente diverso * dai modelli C++. la specializzazione è completamente mancante da Java] (http://stackoverflow.com/questions/3988180/java-generi cs-template-specialization-possible-overriding-template-types-with) (anche se sembra che alcune ricerche teoriche siano state fatte in quella direzione. E Java non supporta neanche i parametri generici non di tipo. –

risposta

5

Poiché ciò non è possibile in Java, provare a pensare a Bar<B, A extends B> in modo diverso.

Quando si dichiara una variabile per Bar, si specifica prima la classe padre e poi la classe figlio. È così che funziona Bar. Non pensarlo come indietro - pensalo come avanti. Il genitore dovrebbe essere naturalmente specificato prima del bambino. Questa relazione aggiuntiva che hai aggiunto è ciò che guida l'ordine dei parametri, non l'interfaccia sottostante.

+0

Apprezzo che non c'è modo di farlo, ma dire al PO che ci sta pensando in modo sbagliato e che implica che probabilmente "[facendogli male la testa]" mi sembra una forma terribile. -1 per la risposta meno utile che abbia mai visto qui su SO. –

+3

@Platinum: Seriamente? Trovo le molte, molte risposte sbagliate qui che sono errate da persone ignoranti come corrette e upvoted per essere meno utili. Ad ogni modo, dato che l'ordine dei parametri di tipo che l'OP vuole non è possibile, penso che si tratti della risposta più utile che potrebbe essere fornita in quanto va oltre il semplice "No" che avrei potuto dare. – ColinD

+3

@Platinum - Certamente non ho letto la risposta in modo negativo mentre la leggi (anche se vedo come l'ultima frase _could_ venga presa nel modo sbagliato). No, dove ha detto che l'OP sta pensando "sbagliato" o usa un'altra parola con una possibile connotazione negativa. L'uso di "backwards" è sinonimo di "reverse" dell'OP. A volte ciò che rende le cose difficili e non intuitive non ha un "modello mentale" adatto con cui lavorare. Non vedo nulla di sbagliato nell'offrire un modo diverso di guardare le cose come una "risposta". –

1

Dopo aver visto questa domanda, ho passato un po 'a provare alcune tecniche diverse che pensavo potessero funzionare. Ad esempio, la creazione di un'interfaccia generica ISuper<B,A extends B> e il successivo utilizzo di Bar<A,B> implements ISuper<B,A> (e una tecnica simile con una sottoclasse e un'estensione anziché le implementazioni), ma ciò comporta un errore di tipo, Bar.java:1: type parameter A is not within its bound. Allo stesso modo, ho provato a creare un metodo private <A extends B> Bar<A,B> foo() { return this; }; e chiamarlo dal costruttore, ma questo si traduce semplicemente nel divertente messaggio di errore Bar.java:2: incompatible types found : Bar<A,B> required: Bar<A,B>

Quindi, penso che, sfortunatamente, la risposta sia no. Ovviamente, non è la risposta che speravi, ma sembra che la risposta corretta sia che questo non è possibile.

+0

Ancora, ho trovato il tuo post stimolante. Grazie per il tuo tempo! – Cephalopod

1

È stato già rilevato che non esiste né una soluzione né una soluzione piacevole. Ecco cosa ho finalmente fatto. Funziona solo per il mio caso speciale, ma puoi prenderlo come ispirazione se incontri problemi simili. (Ciò spiega anche il motivo per cui mi sono imbattuto in questo problema)

Prima di tutto, v'è questa classe (che mostra solo l'interfaccia in questione):

class Pipe<Input, Output> { 

    boolean hasNext(); 

    Input getNext(); 

    void setNext(Output o); 

} 

L'interfaccia Foo è in realtà

interface Processor<Input, Output> { 

    process(Pipe<Input, Output> p); 

} 

e la classe Bar dovrebbe funzionare in questo modo

class JustCopyIt<Input, Output> implements Processor<Input, Output> { 

    process(Pipe<Input, Output> p) { 
     while (p.hasNext()) p.setNext(p.getNext()); 
    } 

} 

Il modo più semplice sarebbe quello di trasmettere valori come questo: p.setNext((Output) p.getNext()). Ma questo è male in quanto consentirebbe di creare un'istanza di JustCopyIt<Integer, String>. Chiamare questo oggetto fallirebbe misteriosamente ad un certo punto, ma non nel punto in cui viene fatto l'errore effettivo.

Fare anche class JustCopyIt<Type> implements Processor<Type, Type> non funzionerebbe anche qui, perché quindi non sono in grado di elaborare un Pipe<String, Object>.

Così che alla fine ho fatto è stato per modificare l'interfaccia a questo:

interface Processor<Input, Output> { 

    process(Pipe<? extends Input, ? super Output> p); 

} 

In questo modo, un JustCopyIt<List> è in grado di elaborare un Pipe<ArrayList, Collection>.

Mentre questa tecnica sembra essere l'unica soluzione valida, è ancora male perché 1) funziona solo per questo caso speciale, 2) mi ha richiesto di cambiare l'interfaccia (che non è sempre possibile) e 3) ha fatto il codice degli altri processori brutto.

Edit:
lettura Keiths risposta ancora una volta mi ha ispirato per un'altra soluzione:.

public abstract class Bar<A, B> implements Foo<A, B> { 

    public static <B, A extends B> Bar<A, B> newInstance() { 
     return new BarImpl<B, A>(); 
    } 

    private static class BarImpl<B, A extends B> extends Bar<A, B> { 
     // code goes here 
    } 

} 

// clean code without visible reversed parameters 
Bar<Integer, Object> bar1 = Bar.newInstance(); 
Bar<Object, Integer> bar2 = Bar.newInstance(); // <- compile error 
+0

Mi piace. È un bel lavoro. Sono sicuro che a un certo punto qualcun altro leggerà quel codice e lo troverà confuso, ma se usato dall'esterno, è un netto miglioramento. –