2015-08-07 20 views
8

Ho diverse interfacce:Override tipo restituito generico dall'interfaccia

public interface Endpoint<T extends Fetchable> {  
    public Class<T> getFetchableType(); 
} 

public interface Fetchable { 
    ... fetched data fields 
} 

public interface Fetcher { 
    public <T extends Fetchable> T fetch(Endpoint<T> endpoint); 
} 

Per una classe che implementa Fetcher, perché fa il lavoro del compilatore con questa dichiarazione di metodo:

public FetchableImpl fetch(Endpoint endpoint) { return null;} 

mentre questi sono entrambi errati dichiarazioni:

public FetchableImpl fetch(EndpointImpl endpoint) { return null;} 
--or-- 
public FetchableImpl fetch(Endpoint<FetchableImpl> endpoint) { return null;} 

dove EndpointImpl implements Endpoint<FetchableImpl> .

La mia intuizione sarebbe che il parametro avrebbe qualcosa di designante che è un endpoint che si occupa del tipo specifico di Fetchable. Quindi, perché il compilatore richiede solo una scala Endpoint, anche quando il metodo di interfaccia richiede Endpoint<T>?

risposta

0

vostra dichiarazione metodo di interfaccia richiede un metodo che è in grado di affrontare qualsiasi tipo di EndPoint:

public <T extends Fetchable> T fetch(Endpoint<T> endpoint); 

Ma nel metodo implementazioni a restringere verso il basso per un tipo più specifico di EndPoint.

public FetchableImpl fetch(EndpointImpl endpoint) { return null;} 
public FetchableImpl fetch(Endpoint<FetchableImpl> endpoint) { return null;} 

Quindi, queste non sono implementazioni valide del metodo di interfaccia. Non coprono tutti i casi richiesti dall'interfaccia.

Si potrebbe desiderare di dichiarare una generica Fetcher, e quindi implementare che:

public interface Fetcher<T extends Fetchable> { 
    T fetch(Endpoint<T> endpoint); 
} 

public class FetcherImpl implements Fetcher<FetchableImpl> { 
    public FetchableImpl fetch(Endpoint<FetchableImpl> endpoint) { 
     return null; 
    } 
} 

In alternativa, se si desidera solo il T in Endpoint<T> di essere lo stesso del T che viene restituito dal metodo, è può mantenere la dichiarazione di metodo di interfaccia come sono, e utilizzare la stessa dichiarazione nella classe di applicazione:

public interface Fetcher { 
    <T extends Fetchable> T fetch(Endpoint<T> endpoint); 
} 

public class FetcherImpl implements Fetcher { 
    public <T extends Fetchable> T fetch(Endpoint<T> endpoint) { 
     return null; 
    } 
} 
+0

Quindi, se volessi che il 'T' in 'Endpoint ' sia uguale al 'T' che viene restituito dal metodo, come dovrei dichiararlo nell'interfaccia? – Indigenuity

+0

Lo stesso di voi. E usa la stessa dichiarazione anche nella tua classe di implementazione, inclusi i parametri del tipo. –

+0

Questo non sembra spiegare perché è consentito il primo metodo? Quando l'interfaccia si aspetta di restituire qualsiasi cosa che estenda Fetchable, la classe restituisce effettivamente una classe specifica. – Codebender

0

in breve, i primi lavori perché di tipo prime, mentre le due successive sicuro a causa di conflitti nella firma del metodo dopo la cancellazione.

Per la spiegazione più lunga, ricordare che il metodo Fetcher::fetch è <T extends Fetchable> T fetch(Endpoint<T>) e che gli implementatori di Fetcher devono implementare tale metodo. Un buon modo di pensare a questo è lo Liskov substitution principle, che in pratica dice "se il tuo tipo statico è di SuperClass, allora non dovrebbe importare quale SubClass tu abbia, dovrebbero funzionare come dice SuperClass."

Vediamo come le vostre seconde due dichiarazioni che fare con questo immaginando che qualcuno ha un Fetcher e invoca come tale:

Endpoint<IntFetchable> intEndpoint = whatever(); 
IntFetchable i = fetcher.fetch(intEndpoint); // T is inferred to be IntFetchable 

Come si può vedere, per questo al lavoro, il metodo fetch non può prendere EndpointImpl o Endpoint<FetchableImpl> - ha davvero bisogno di prendere un Endpoint<T>.

È inoltre possibile ignorare del tutto il generico nella firma del metodo e fare in modo che l'override sia di tipo raw (ovvero del tipo cancellato).Questo è quello che hai fatto con il primo dei tuoi overrides (FetchableImpl fetch(Endpoint)), ma i tipi non elaborati perdono la sicurezza del tipo e hanno qualche altro trucco intorno a loro, quindi non lo consiglierei.

Se si desidera fetchers per essere specializzati per ogni tipo di endpoint, si dovrebbe prendere la dichiarazione generica e metterlo sull'interfaccia Fetcher:

public interface Fetcher<T> { 
    T fetch(Endpoint<T> endpoint); 
} 

Ora si può avere un FetcherImpl implements Fetcher<EndpointImpl>.