我有几个接口:
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;}
where EndpointImpl implements Endpoint<FetchableImpl>
.
我的直觉是,参数应该有一些东西来指定它是一个处理特定类型Fetchable的端点。那么,为什么编译器只需要一个直接的Endpoint
,即使接口方法需要Endpoint<T>
?
你的接口方法声明需要一个能够处理任何类型的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;
}
}
或者,如果您只是希望Endpoint<T>
中的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;
}
}
简而言之,第一个有效是因为原始类型,而后两个失败是因为擦除后方法签名中的冲突。
对于更长的解释,请记住您的Fetcher::fetch
方法是<T extends Fetchable> T fetch(Endpoint<T>)
,因此Fetcher
的实现者必须实现该方法。考虑这个问题的一个好方法是Liskov替代原则,它基本上是说"如果你的静态类型是超类,那么你有哪个子类都不重要,它们都应该像超类所说的那样工作。"
让我们看看你的第二个两个声明是如何处理的,想象某人有一个Fetcher
,并像这样调用它:
Endpoint<IntFetchable> intEndpoint = whatever();
IntFetchable i = fetcher.fetch(intEndpoint); // T is inferred to be IntFetchable
你可以看到,为了工作,fetch
方法不能取EndpointImpl
或Endpoint<FetchableImpl>
——它确实需要取Endpoint<T>
。
您还可以在方法签名中完全忽略泛型,并使重写为原始类型(即类型擦除类型)。这就是您对第一个覆盖(FetchableImpl fetch(Endpoint)
)所做的,但是原始类型失去了类型安全性,并且在它们周围有一些其他的问题,所以我不推荐它。
如果您希望为每种端点指定特定的获取器,您应该使用泛型声明并将其放在Fetcher
接口上:
public interface Fetcher<T> {
T fetch(Endpoint<T> endpoint);
}
现在你可以有一个FetcherImpl implements Fetcher<EndpointImpl>
.