2012-01-11 43 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的参数。不要紧,你说的实际参数也是通配符类型的:只要实际参数是通配符类型的,你就不能传递值。