2015-08-07 4 views
8

У меня есть несколько интерфейсов:Перекрытие общего типа возвращаемых из интерфейса

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); 
} 

Для класса, который реализует Fetcher, почему делает работу компилятора с этим методом декларацией:

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

в то время как они оба неправильно декларации:

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

, где EndpointImpl implements Endpoint<FetchableImpl> ,

Моей интуицией было бы то, что у параметра будет что-то обозначающее, что это конечная точка, которая имеет дело с конкретным типом Fetchable. Итак, почему компилятор требует только прямой Endpoint, даже если для метода интерфейса требуется Endpoint<T>?

ответ

0

Ваше объявление метода интерфейса требуется метод, который способен справиться с любым типом EndPoint:

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

Но в ваших реализациях метода вы сузить ее до более конкретного типа EndPoint.

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

Следовательно, это недопустимые реализации интерфейса. Они не охватывают все случаи, требуемые интерфейсом.

Вы можете объявить общую Fetcher, а затем осуществить это:

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; 
    } 
} 

В качестве альтернативы, если вы просто хотите T в Endpoint<T> быть таким же, как T, возвращенного методом, вы может сохранить ваш метод интерфейс декларации, как есть, и использовать такое же заявление в своем реализующий классе:

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

Так что, если бы я хотел, чтобы 'T' в' Endpoint 'был таким же, как' T', который возвращается методом, как бы объявить это в интерфейсе? – Indigenuity

+0

То же, что и у вас. И используйте одно и то же объявление в своем классе реализации, включая параметры типа. –

+0

Это, похоже, не объясняет, почему разрешен первый метод? Когда интерфейс ожидает вернуть все, что расширяет Fetchable, класс фактически возвращает определенный класс. – Codebender

0

Короче говоря, первые работы из-сырьевых видов, в то время как рядом два не в состоянии из-за конфликтов в сигнатуре метода после стирания.

Для более подробного объяснения, помните, что ваш метод Fetcher::fetch равен <T extends Fetchable> T fetch(Endpoint<T>), поэтому разработчики Fetcher должны реализовать этот метод. Хороший способ подумать об этом - это Liskov substitution principle, в котором в основном говорится: «Если ваш статический тип имеет SuperClass, то не имеет значения, какой у вас SubClass, они все должны работать, как говорит SuperClass».

Давайте посмотрим, как ваши вторые два объявления с этим делать, воображая, что кто-то имеет Fetcher и вызывает ее как таковую:

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

Как вы можете видеть, для того, чтобы работать, метод fetch не может возьмите EndpointImpl или Endpoint<FetchableImpl> - ему действительно нужно взять Endpoint<T>.

Вы также можете пренебречь общим типом в вашей сигнатуре метода и иметь переопределение типа raw (то есть типа стираемого типа).Это то, что вы сделали с первым из ваших переопределений (FetchableImpl fetch(Endpoint)), но сырые типы теряют безопасность типа и имеют несколько других ошибок вокруг них, поэтому я бы не рекомендовал его.

Если вы хотите fetchers быть специализированы для каждого вида конечной точки, вы должны принять общую декларацию и поставить его на интерфейс Fetcher:

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

Теперь вы можете иметь FetcherImpl implements Fetcher<EndpointImpl>.

Смежные вопросы