2013-06-12 23 views
5

ho paio di interfacce forniteAssegnare una sottoclasse di una classe generica a una classe super-di questa classe

public interface Finder<S extends Stack<T>,T extends Item> { 
    public S find(S s, int a); 
} 

public interface Stack<T extends Item> { 
    Stack<T> getCopy(); 
} 

e una classe che implementa il primo:

Se non posso cambiare qualsiasi interfaccia quale sarebbe la migliore linea d'azione mantenendo l'implementazione il più generica possibile?

EDIT Qualche altro codice che non riesco a rompere istanzia SimpleFinder<ClassA,ClassB> quindi dovrei avere due tipi generici nella realizzazione pure.

+0

La migliore linea d'azione per ottenere cosa? Perché non riesci a modificare le definizioni delle interfacce? – Bobulous

+0

prova il cast bruto. ci sono modi per risolverlo (parametro di tipo ricorsivo) ma non ne vale la pena. – ZhongYu

+0

@Arkanon perché non è il mio a cambiare ...: | e il cast brutale è piuttosto brutto, vero? – SadStudent

risposta

3

Il problema è che ovviamente Stack<T> non è S extends Stack<T>. Java è fortemente digitato e non ti consente di fare cose del genere.

È possibile eseguire il cast su Stack<T>, nel qual caso verrà comunque visualizzato un avviso relativo alla conversione non selezionata. Ciò significa che questa conversione è non sicura.

public class SimpleFinder<S extends Stack<T>, T extends Item> implements Finder<S, T> { 

    @Override 
    public S find(S s, int a) { 
     Stack<T> stack = s.getCopy(); 
     return (S) stack; 
    } 

} 

o semplicemente utilizzare Stack<T> invece di S extends Stack<T>, che è la mia raccomandazione:

public class SimpleFinder<T extends Item> implements Finder<Stack<T>, T> { 

    @Override 
    public Stack<T> find(Stack<T> s, int a) { 
     Stack<T> stack = s.getCopy(); 
     return stack; 
    } 

} 
+0

Grazie! e sì, sono ben consapevole del motivo per cui non funziona Non ero sicuro di come risolvere questo senza "perdere" la generalità: \ la ragione per cui non posso fare la seconda soluzione è che c'è un altro codice che non è il mio che crea un'istanza SimpleFinder e non riesco a romperlo ... – SadStudent

+0

Potrei chiedere due argomenti e inviare ', T>' come suggerivi all'interfaccia ma poi 'S' sarebbe ridondante e questo è un po 'brutto troppo, non è vero? – SadStudent

+2

L'opzione di cast deselezionata non è sicura: raccomanderei di rimuoverla dalla risposta o aggiungere almeno una dichiarazione di non responsabilità più significativa. –

0
public class SimpleFinder<S extends Stack<T>,T extends Item> implements Finder<S,T>{ 
    public S find(S s, int a){ 
     Stack<T> stack = ....; 
     ... 
     stack = s.getCopy(); 
     ..... 
     return (S) stack; 
    } 
} 

dovrebbe funzionare. Tieni presente che lo stack deve essere un Stack<T> e non S per corrispondere al tipo di ritorno getCopy(). Mi aspetto che il tipo S sia Ok, poiché estende Stack<T>, ma implementandolo è il comportamento che sto osservando.

+0

Che non compila ... – m0skit0

+0

Mi dispiace, ho appena modificato, spero che ora sia ok. –

+0

No ancora non viene compilato: "Manca il tipo di ritorno per il metodo". Questo tipo di ritorno non è valido (fino a Java 7). – m0skit0

0

tuo tipo S è un sottotipo di Stack<T> ma il metodo copia è upcasting ad un Stack<T> che può essere qualsiasi sottotipo di Stack<T>. Dovete trasmettere il risultato della copia a S

2

Dal momento che non è possibile modificare le interfacce, non hai scelta, ma per fare getto bruta.

In una discussione più generale, ciò di cui abbiamo bisogno qui è "self-type", vogliamo dire che un'invocazione del metodo foo.bar() dovrebbe restituire il tipo statico di foo. Di solito il tipo di auto è ricercato per API fluenti in cui il metodo deve restituire foo stesso. Nel tuo caso vuoi restituire un nuovo oggetto.

In Java non c'è una risposta soddisfacente per il tipo di auto. Un trucco è attraverso il parametro di tipo autoreferenziale come Foo<T extends Foo<T>>, tuttavia è molto brutto, e non può davvero far rispettare che qualsiasi sottotipo Bar deve essere un Foo<Bar>. E il trucco non ti aiuterà affatto nel tuo caso.

Un altro trucco potrebbe funzionare

public interface Stack<T extends Item> { 
    <X extends Stack<T>> X getCopy(); 
} 

qui, il chiamante fornisce il tipo di ritorno esatto.

 S stack = ....; 
    ... 
    stack = s.getCopy(); 
    // compiles, because X is inferred to be S 

Questo trucco consente di semplificare i siti di chiamata.Tuttavia, i cast brutali esistono ancora, nascosti nelle implementazioni di getCopy(). Questo trucco è pericoloso e il chiamante deve sapere cosa sta facendo. Personalmente non lo farei; è meglio che il chiamante della forza faccia il cast.

1

Come discusso nei commenti, il vostro disegno richiede che il metodo getCopy restituire il "tipo di auto" - cioè, un'implementazione BlueStack<T> ci si aspetterebbe per restituire un BlueStack<T> dalla sua getCopy, e RedStack<T> dovrebbe restituire un RedStack<T> ecc

Sfortunatamente, non c'è modo di esprimere il "self-type" in Java. Come zhong.j.yu points out, un parametro di tipo ricorsivo si avvicina, per esempio:

//not recommended! 
public interface Stack<S extends Stack<S, T>, T extends Item> { 
    S getCopy(); 
} 

Ma come zhong.j.yu menzioni questo è poco intuitivo e sarebbe ancora non riescono a impedire che un BlueStack<T> da "mentire" e di ritorno a RedStack<T> dal suo getCopy.

Invece, consiglio una riprogettazione. Prova a disaccoppiare la responsabilità di copiare pile dal tipo Stack. Per esempio:

public interface StackCopier<S extends Stack<T>, T extends Item> { 

    S copy(S original); 
} 

Se implementazioni di StackCopier bisogno di accedere ai membri privati ​​delle rispettive Stack s, si consideri che li rende classi nidificate, ad esempio:

class BlueStack<T extends Item> implements Stack<T> { 

    ... 

    static class Copier<T extends Item> implements StackCopier<BlueStack<T>, T> { 

     @Override 
     public BlueStack<T> copy(BlueStack<T> original) { 

      ... 
     } 
    } 

Naturalmente SimpleFinder avrebbe bisogno di essere cambiato in o avere un campo StackCopier<S, T> o prendere uno come un nuovo parametro di find:

private final StackCopier<S, T> copier = ...; 

public S find(S stack, int a) { 

    S stackCopy = copier.copy(stack); 

    ... 

    return stackCopy; 
} 
+0

, anche se ho pensato che è necessaria una riprogettazione, come ho detto nella domanda, non posso cambiare l'interfaccia solo per implementarla, quindi credo di essere bloccata da un brutto cast, ma penso che la tua soluzione sia molto bella ... – SadStudent

Problemi correlati