从接口覆盖通用 return 类型
Overriding generic return type from interface
我有几个接口:
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
的 class,为什么编译器使用此方法声明:
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>
?
您的接口方法声明需要一个能够处理任何类型的方法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
相同,您可以保持接口方法声明不变,并在您的实施中使用相同的声明 class:
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 substitution principle,它基本上表示 "if your static type is of SuperClass, then it shouldn't matter which SubClass you have, they should all just work as SuperClass says they do."
让我们看看你的后两个声明是如何处理的,假设有人有一个 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 专用于每种端点,您应该采用通用声明并将其放在 Fetcher
接口上:
public interface Fetcher<T> {
T fetch(Endpoint<T> endpoint);
}
现在你可以拥有 FetcherImpl implements Fetcher<EndpointImpl>
.
我有几个接口:
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
的 class,为什么编译器使用此方法声明:
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>
?
您的接口方法声明需要一个能够处理任何类型的方法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
相同,您可以保持接口方法声明不变,并在您的实施中使用相同的声明 class:
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 substitution principle,它基本上表示 "if your static type is of SuperClass, then it shouldn't matter which SubClass you have, they should all just work as SuperClass says they do."
让我们看看你的后两个声明是如何处理的,假设有人有一个 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 专用于每种端点,您应该采用通用声明并将其放在 Fetcher
接口上:
public interface Fetcher<T> {
T fetch(Endpoint<T> endpoint);
}
现在你可以拥有 FetcherImpl implements Fetcher<EndpointImpl>
.