簡體   English   中英

從接口覆蓋通用返回類型

[英]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的類,為什么編譯器使用此方法聲明:

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<T>時,為什么編譯器也只需要一個直線Endpoint

接口方法聲明需要一個能夠處理任何類型的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;
    }
}

另外,如果你只是想TEndpoint<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替換原理 ,該原理基本上說:“如果您的靜態類型是SuperClass,那么無論您擁有哪個SubClass都不重要,它們都應該像SuperClass所說的那樣工作。”

讓我們通過想象有人擁有Fetcher並按如下方式調用它來查看后兩個聲明的Fetcher

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

如您所見,要使其正常工作, fetch方法不能采用EndpointImplEndpoint<FetchableImpl> -它確實需要采用Endpoint<T>

您也可以在方法簽名中完全不考慮泛型,而使覆蓋類型為原始類型(即,類型擦除類型)。 這就是您使用第一個重寫( FetchableImpl fetch(Endpoint)FetchableImpl fetch(Endpoint) ,但是原始類型失去類型安全性,並且周圍還有一些其他陷阱,因此我不建議這樣做。

如果要使Fetcher專用於每種端點,則應采用通用聲明並將其放在Fetcher接口上:

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

現在您可以擁有一個FetcherImpl implements Fetcher<EndpointImpl>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM