从接口重写泛型返回类型



我有几个接口:

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方法不能取EndpointImplEndpoint<FetchableImpl>——它确实需要取Endpoint<T>

您还可以在方法签名中完全忽略泛型,并使重写为原始类型(即类型擦除类型)。这就是您对第一个覆盖(FetchableImpl fetch(Endpoint))所做的,但是原始类型失去了类型安全性,并且在它们周围有一些其他的问题,所以我不推荐它。

如果您希望为每种端点指定特定的获取器,您应该使用泛型声明并将其放在Fetcher接口上:

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

现在你可以有一个FetcherImpl implements Fetcher<EndpointImpl> .

最新更新