Java通用“捕获#n-of”编译错误

时间:2012-01-11 21:08:40

标签: java eclipse generics

我有以下定义:

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>)

什么是&#34; catpure&#34;意味着在错误消息中?

3 个答案:

答案 0 :(得分:8)

您说DeviceClientHelpermessageHandler的某些子类有MessageBodysomeMethod还为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 :(得分: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);
        }
    }
} 

(这很可能会产生关于未经检查的演员的警告)

或者更好:

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);
        }
    }
} 

如果你的DeviceClientHelper应该包含处理不同类型消息的处理程序(例如,它最初包含M1的处理程序,但是你为M2分配了一个处理程序),你仍然希望它由泛型验证,我我怕你运气不好这需要在运行时而不是编译时间检查泛型,并且由于泛型类型擦除,Java不支持它。

答案 2 :(得分:0)

这是由于泛型中的wildcard (?)行为造成的。长话短说,用通配符键入的变量/参数不能分配给。

在您的代码中,调用this.messageHandler.responsed(msg)会尝试将msg传递给类型为? extends MessageBody的参数。您正在使用的实际参数也是通配符类型并不重要:只要实际参数是通配符类型,您就无法将值传递给它。