【问题标题】:Java generics wildcard for different subtypes clarificationJava泛型通配符用于不同子类型的说明
【发布时间】:2020-07-28 07:05:22
【问题描述】:

我想避免在我的代码中显式强制转换,这是我到现在为止所做的,但是编译器给了我一些错误,我不明白如何修复它没有强制转换

我有一个带有监听器映射的类,以及一些通知监听器的方法:

public class MessageDispatcher {
    
     private final Map<String, List<ControllerMessageWatcher<? extends ControllerMessage>>> watchers;
     
     public void registerMessageWatcher(Class<? extends ControllerMessage> classToWatch, final ControllerMessageWatcher<?> controllerMessageWatcher) {
        this.watchers.computeIfAbsent(classToWatch.getSimpleName(), k -> new ArrayList<>()).add(controllerMessageWatcher);
    }

     public synchronized void receive(StatusMessage statusMessage) {
         List<ControllerMessageWatcher<?>> watchers = this.watchers.get(statusMessage.getClass().getSimpleName());
         if (watchers != null)
             watchers.forEach(controllerMessageWatcher -> controllerMessageWatcher.newMessage(statusMessage)); 
               // Required type: capture of ?      Provided:  StatusMessage <-- error here!   ^^^^
     }

     public synchronized void receive(UpdateDBMessage updateDBMessage) {
        List<ControllerMessageWatcher<?>> watchers = this.watchers.get(updateDBMessage.getClass().getSimpleName());
        if (watchers != null)
            watchers.forEach(controllerMessageWatcher -> controllerMessageWatcher.newMessage(updateDBMessage));
               // Required type: capture of ?      Provided:  UpdateDBMessage <-- error here!   ^^^^
     }
}

这是监听器的接口:

public interface ControllerMessageWatcher<T extends ControllerMessage> {

    void newMessage(T message);
}

这是我希望避免强制转换的监听器的实现:

public class Status implements ControllerMessageWatcher<StatusMessage> {
    @Override
    public void newMessage(StatusMessage message) {
        ...
    }

}

除了 receive 方法之外的所有工作,它会给出错误(或者如果我删除通配符,则会出现未经检查的警告)。

我想要实现的是避免在每个 newMessage 方法中显式强制转换(如果我删除所有泛型部分,我必须这样做)。

当我从MessageDispatcher.receive() 方法(我现在拥有的)调用newMessage 时避免错误/警告(请参阅该方法中的 cmets)

这是ControllerMessageStatusMessageUpdateDBMessage

public abstract class ControllerMessage {

    private String messageId;

//    ...  getter setter
}
public class StatusMessage extends ControllerMessage{

    private String aField;

//    ...  getter setter
}
public class UpdateDBMessage extends ControllerMessage{

    private String aField;

//    ...  getter setter
}

【问题讨论】:

  • 您的问题可读性不强,并且缺少某些部分。请提供编译器为您提供的错误,提供您想要避免的确切内容(代码的哪一行)以及您想要达到的目标结果。另外,ControllerMessage 是什么? AckMessage?
  • @GiorgiTsiklauri 我已经编辑了问题,ControllerMessage 只是具有公共字段的每条消息的抽象类
  • 请提供该类,我们也看不到StatusMessage 是什么。当我们必须阅读和解析整个代码时,很难回答这个问题。如果您对某个特定行只有一个特定问题,请提供该问题,并附上清晰的错误日志。
  • 我已经添加了请求的类,错误在代码中的两行注释中指定。
  • 查看我在下面写的答案。我希望它会让你清楚。如果仍有疑问,请在 cmets 中提问。

标签: java generics wildcard


【解决方案1】:

我认为您的设计从根本上被破坏了,因为您使用了反射。

public void registerMessageWatcher(
    Class<? extends ControllerMessage> classToWatch, 
    final ControllerMessageWatcher<?> controllerMessageWatcher
)

这一步,在您的 API 级别,您使编译器无法管理您的继承。

一个小的改进是使类型一致。

public <T extends ControllerMessage> void registerMessageWatcher(
    Class<T> classToWatch, 
    final ControllerMessageWatcher<T> controllerMessageWatcher
)

现在我们正在跟踪我们的班级类型,无论好坏,但我们仍然需要通过种姓来将班级保留在地图中。

private final Map<Class<?>, List<ControllerMessageWatcher<?>>> watchers;

List<ControllerMessageWatcher<T>> getWatchers( Class<T> clazz ){
    return (List<ControllerMessageWatcher<T>>)watchers.get(clazz);
}

为了避免演员,我会做

public interface ControllerMessageWatcher<T extends ControllerMessage> {
    void newMessage(ControllerMessage message);
}

我不知道你在用你的观察者做什么来区分类型。这样你就可以拥有了。

List<ControllerMessageWatcher<? extends ControllerMessage>> watchers;
watchers.forEach( w -> w.newMessage( message ) );

然后用接口替换抽象类。

interface ControllerMessage{
    String getMessageId();
    String getAField();
}

现在由特定的观察者来获取相关信息。您还可以为不相关的字段设置默认方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多