2017-01-18 3 views
4
Kompilieren

Ich habe dies vereinfachte Beispiel meines echten Code, es kompiliert nicht:Java Generics mit Wildcard nicht

public class Example { 

    public static void main(String[] args) { 
     Callback<BalanceResponse> balanceRequestCaller = new Callback<BalanceResponse>() { 
      @Override 
      public void onResponse(BalanceResponse response) {} 

      @Override 
      public void onFailure(String error, int code) {} 

      @Override 
      public void onFailure(Throwable t) {} 
     }; 

     BalanceRequest breq = new BalanceRequest(); 

     Interceptor interceptor = new Interceptor(balanceRequestCaller); 

     breq.startRequest((Callback<BaseResponse>) interceptor); //compile-time error!!! 
    } 


    public static class Interceptor implements Callback<BaseResponse> { 

     public Interceptor(Callback<? extends BaseResponse> originalCaller) { 
      this.originalCaller = originalCaller; 
     } 

     private Callback<? extends BaseResponse> originalCaller; 

     @Override 
     public void onResponse(BaseResponse response) { 
      //do some interception stuff 

      if (response instanceof BalanceResponse) { 
       ((Callback<BalanceResponse>) originalCaller).onResponse((BalanceResponse) response); 
      } else if (response instanceof SubscriptionResponse) { 
       ((Callback<SubscriptionResponse>) originalCaller).onResponse((SubscriptionResponse) response); 
      } 
     } 

     @Override 
     public void onFailure(String error, int code) {} 

     @Override 
     public void onFailure(Throwable t) {} 
    } 

    public interface Request<T extends BaseResponse> { 
     void startRequest(Callback<T> callback); 
    } 

    public static class BalanceRequest implements Request<BalanceResponse> { 
     @Override 
     public void startRequest(Callback<BalanceResponse> callback) {} 
    } 

    public static class SubscriptionRequest implements Request<SubscriptionResponse> { 
     @Override 
     public void startRequest(Callback<SubscriptionResponse> callback) {} 
    } 

    public static class BaseResponse { 
     public String status; 
    } 

    public static class BalanceResponse extends BaseResponse { 

    } 

    public static class SubscriptionResponse extends BaseResponse { 

    } 

    public interface Callback<T> { 
     void onResponse(T response); 
     void onFailure(String error, int code); 
     void onFailure(Throwable t); 
    } 
} 

Was ich versuche zu tun ist, ein Interceptor zu machen, dass der Rückruf würde abfangen, einige Sachen tun mit der Antwort und dann an den ursprünglichen Rückruf weitergeben.

Ich möchte eine generische Interceptor machen, die jeden Rückruf abfangen könnte, der Callback<? extends BaseResponse> ist.

Durch die Art und Weise der Fehler bei der Kompilierung ich von NetBeans bin immer ist

incompatible types: Callback<BaseResponse> cannot be converted to Callback<BalanceResponse> 

Vorschläge Geben Sie mir, wie könnte ich diese Arbeit machen?

Antwort

3

Sie sollten einen Typ Parameter auf Ihrer Klasse definieren Interceptor Ihre Übersetzungsfehler zu vermeiden und explizite Casts in dem Verfahren onResponse die in der Regel ein Beweis für Design-Problem ist zu vermeiden.

Ihre Interceptor-Klasse mit einem Typ Parameter:

public static class Interceptor<T extends BaseResponse> implements Callback<T> { 

    public Interceptor(Callback<T> originalCaller) { 
     this.originalCaller = originalCaller; 
    } 

    private Callback<T> originalCaller; 

    @Override 
    public void onResponse(T response) { 
     originalCaller.onResponse(response); 
    } 

    ... 
} 

Sie würden es dann instanziiert und als nächstes verwenden:

Interceptor<BalanceResponse> interceptor = new Interceptor<>(balanceRequestCaller); 
breq.startRequest(interceptor); 
1

Ändern Sie die Signatur von startRequest() Signatur

void startRequest(Callback<? super T> callback); 

Diese allo Ws, um einen allgemeineren Rückruf zu akzeptieren, anstatt nur den engsten Typ zu erfordern. Weitere Informationen finden Sie unter What is PECS (Producer Extends Consumer Super)?

EDIT: @teppic's answer ist die bessere Lösung, es sei denn, Sie müssen eine einzige Interceptor Instanz für verschiedene Anforderungstypen wiederverwenden. Wenn dies der Fall ist, können Sie den Textbaustein immer noch auf eine einzige Delegierung reduzieren:

@Override 
public void onResponse(BaseResponse response) { 
    //do some interception stuff 
    ((Callback<BaseResponse>)originalCaller).onResponse(response); 
}