2012-01-11 2 views
2

У меня есть следующее определение:Java родовое "захват # п-о" компиляции ошибка

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

Это [тип стирания] (http://docs.oracle.com/javase/tutorial/java/generics/erasure.html) в лучшем виде. – Finbarr

+0

ну, я вроде знаю, почему сейчас. данный MessageResponseHandler messageHandler, он должен быть определенным типом MessageBody, но не общим. Но как я могу обойти это? тип будет отличаться каждый раз. – Wudong

+1

@Finbarr это не имеет никакого отношения к стиранию. Типы просто необоснованны. –

ответ

8

Вы говорите, что DeviceClientHelper имеет messageHandler для некоторого подкласса MessageBody. И у 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); 
     } 
    } 
} 

(это, скорее всего, производят предупреждение о бесконтрольной гипсе)

Или еще лучше:

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), и все же вы хотите, чтобы он был валидирован дженериками, я боюсь, что вам не повезло; для этого потребуется, чтобы дженерики проверялись по адресу runtime вместо время компиляции, а Java не поддерживает его из-за стирания типа дженериков.

+0

Ты потерял меня там. Предполагаете ли вы, что SomeHandler расширяет MessageBody? Woudln't, которые уже не в назначении обработчика? – Miquel

+0

Нет, почему? Назначение указывает, что может быть назначен обработчик для ЛЮБОГО типа, расширяющего MessageBody. Обработчик удовлетворяет этому - M1 - это расширение, расширяющее MessageBody. Таким образом, M1 выполняет условие «? Extends MessageBody» для переменной обработчика. – Seramme

0

Это связано с поведением wildcard (?) в дженериках. Короче говоря, переменная/параметр, набранный с помощью шаблона, не может быть назначена.

В вашем коде звонок this.messageHandler.responsed(msg) пытается передать msg в параметр, тип которого ? extends MessageBody. Не имеет значения, что фактический параметр, который вы pssing, также подстановочным шрифтом: пока фактический параметр является подстановочным шрифтом, вы не можете передавать ему значения.

Смежные вопросы