2012-01-11 72 views
2

我有如下定義:Java的泛型「捕捉到第N-的」編譯錯誤

public interface MessageResponseHandler<T extends MessageBody> { 
    public void responsed(Message<T> msg); 
} 

public class DeviceClientHelper { 
    public MessageResponseHandler<? extends MessageBody> messageHandler; 

    setHandler(MessageResponseHandler<? extends MessageBody> h){ 
     this.messageHandler = h; 
    } 

    public someMethod(Object message){ 
     Message<? extends MessageBody> msg = (Message<? extends MessageBody>) message; 
     if (this.messageHandler != null) { 
      this.messageHandler.responsed(msg); 
     } 
    } 
} 

我無法爲什麼在的someMethod()方法計算出的

this.messageHandler.responsed(msg); 
調用

會在eclipse中給我一個有線編譯器錯誤。例如:

the method responsed(Message<capture#3-of ? extends MessageBody>) in 
the type MessageResponseHandler<capture#3-of ? extends MessageBody> is 
not applicable for the arguments (Message<capture#4-of ? extends 
MessageBody>) 

無論如何,錯誤信息中的「catpure」意味着什麼?

+4

這是[type erasure](http://docs.oracle.com/javase/tutorial/java/generics/erasure.html)最好的版本。 – Finbarr 2012-01-11 21:12:48

+0

嗯,我有點知道爲什麼現在。給定MessageResponseHandler <?擴展MessageBody> messageHandler,它必須是某種類型的MessageBody,但不是一般的。但是我怎麼能解決這個問題?類型每次都會有所不同。 – Wudong 2012-01-11 21:17:10

+1

@Finbarr這與刪除無關。類型只是不完善。 – 2012-01-11 21:26:38

回答

8

你的意思是DeviceClientHelper對於MessageBody的某些子類有一個messageHandler。並且someMethod也有一條消息,用於MessageBody的某些子類。但是,沒有什麼要求它們是相同的的子類MessageBody,因此responsed的呼叫無效。

爲了讓他們使用相同的子類,使DeviceClientHelper通用對MessageBody一個特定的子類,像這樣:

interface MessageResponseHandler<T extends MessageBody> { 
    public void responsed(Message<T> msg); 
} 

public class DeviceClientHelper<T extends MessageBody> { 
    public MessageResponseHandler<T> messageHandler; 

    void setHandler(MessageResponseHandler<T> h){ 
     this.messageHandler = h; 
    } 

    public void someMethod(Object message){ 
     Message<T> msg = (Message<T>) message; 
     if (this.messageHandler != null) { 
      this.messageHandler.responsed(msg); 
     } 
    } 
} 

但是,你MessageResponseHandler接口可能不需要關心MessageBody類。這取決於它如何實際使用,但這樣的事情可能會更好地工作:

interface MessageResponseHandler { 
    public void responsed(Message<? extends MessageBody> msg); 
} 

然後你就可以從messageHandler字段中刪除泛型類型,並保持原有的someMethod實施。

1

這是使用泛型時常見的陷阱;儘管你的messageHandler和msg對象看起來是一樣的,但它們確實不是(或者說,它們不一定非要)。試想一下:

class M1 extends MessageBody { ... } 
class M2 extends MessageBody { ... } 

MessageHandler<? extends MessageBody> handler = new MessageHandler<M1>(); 
/* So far so good */ 
Message<? extends MessageBody> message = new M2(); 
/* Still OK */ 
handler.responded(message); 
/* Huh... you don't want to allow this, right? */ 

如果你的處理器是唯一能夠處理一個特定那種消息,那麼你應該DeviceClientHelper只收到此具體信息種類太多了吧?這樣的事情呢?

public class DeviceClientHelper<T extends MessageBody> { 
    public MessageResponseHandler<T> messageHandler; 

    setHandler(MessageResponseHandler<T> h){ 
     this.messageHandler = h; 
    } 

    public someMethod(Object message){ 
     Message<T> msg = (Message<T>) message; 
     if (this.messageHandler != null) { 
      this.messageHandler.responsed(msg); 
     } 
    } 
} 

(這將極有可能產生約選中鑄警告)

或者更好的是:如果你的DeviceClientHelper應該包含處理程序處理不同類型的信息(例如

public class DeviceClientHelper<T extends MessageBody> { 
    public MessageResponseHandler<T> messageHandler; 

    setHandler(MessageResponseHandler<T> h){ 
     this.messageHandler = h; 
    } 

    public someMethod(T message){ 
     if (this.messageHandler != null) { 
      this.messageHandler.responsed(message); 
     } 
    } 
} 

,它最初包含一個M1的處理程序,但是然後你爲M2分配一個處理程序),並且仍然希望它可以通過泛型進行驗證,恐怕你運氣不佳;這將需要在運行時而不是編譯時間檢查泛型,並且由於泛型的類型擦除,Java不支持它。

+0

你在那裏迷失了我。你是否假設SomeHandler擴展了MessageBody?那不是已經在處理器任務中失敗了嗎? – Miquel 2012-01-11 21:22:34

+0

不,爲什麼要這樣?該分配指定可以分配任何類型的擴展MessageBody的處理程序。 Handler 滿足這個 - M1是一個擴展MessageBody的類型。因此,M1滿足處理程序變量的「擴展MessageBody」條件。 – Seramme 2012-01-11 21:27:11

0

這是由於泛型中的wildcard (?)行爲。長話短說,用通配符鍵入的變量/參數不能分配給。

在您的代碼中,呼叫this.messageHandler.responsed(msg)嘗試將msg傳遞給類型爲? extends MessageBody的參數。不要緊,你說的實際參數也是通配符類型的:只要實際參數是通配符類型的,你就不能傳遞值。