如何在没有“未经检查”警告的情况下转换为(已知)泛型类型?

2024-01-26

我有这两个接口;

public interface Event {
    default void dispatch() {
        EventBus.getInstance().dispatch(this);
    }
}
public interface EventListener<T extends Event> {
    void handle(T event);
}

如果我正确理解 Java 中的泛型,我实际上是在告诉第二个接口的继承者将 th

然后我想出了下一段代码,其中可以注册侦听器,可以引发事件,并且注册的侦听器将处理任何引发的事件。

public class EventBus {

    /**
     * The singleton EventBus instance.
     */
    private static EventBus instance;

    /**
     * The map of event types and their listeners.
     */
    private final Map<Class<? extends Event>, Set<EventListener<? extends Event>>> listeners = new ConcurrentHashMap<>();

    /**
     * Create a new EventBus instance.
     */
    private EventBus() {

    }

    /**
     * Retrieve the singleton bus instance.
     *
     * @return The event bus instance.
     */
    public static EventBus getInstance() {
        if (instance == null) {
            instance = new EventBus();
        }

        return instance;
    }

    /**
     * Register a new event listener, that listens to the given event type.
     *
     * @param <T>
     * @param type     The type of event that the given listener should react on.
     * @param listener The listener that we want to register.
     */
    public <T extends Event> void registerListener(Class<T> type, EventListener<T> listener) {
        Set<EventListener<? extends Event>> eventListeners = getOrCreateListeners(type);

        eventListeners.add(listener);
    }

    /**
     * Retrieve a set of listeners, either by retrieving an existing list or by creating a new one.
     *
     * @param eventClass The type of event for which to retrieve listeners.
     *
     * @return A set of event listeners that listen for the given event.
     */
    private Set<EventListener<? extends Event>> getOrCreateListeners(Class<? extends Event> eventClass) {
        Set<EventListener<? extends Event>> eventSubscribers = listeners.get(eventClass);

        if (eventSubscribers == null) {
            eventSubscribers = new CopyOnWriteArraySet<>();
            listeners.put(eventClass, eventSubscribers);
        }

        return eventSubscribers;
    }

    /**
     * Dispatch the given event to all registered listeners associated with that type.
     *
     * @param <T>
     * @param event The event that is to be dispatched.
     */
    public <T extends Event> void dispatch(T event) {
        listeners.keySet().stream()
                .filter(type -> type.isAssignableFrom(event.getClass()))
                .flatMap(type -> listeners.get(type).stream())
                .forEach(listener -> {
                    try {
                        ((EventListener<T>) listener).handle(event); //  <-- This is where the compiler warns me...
                    } catch (Exception e) {
                        Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
                    }
                });
    }
}

发出警告的行位于底部附近:((EventListener<T>) listener).handle(event);

事件监听器的代码大致基于这段代码 https://github.com/falkoschumann/java-eventbus/blob/master/src/main/java/de/muspellheim/eventbus/EventBus.java。遗憾的是,该作品没有使用任何泛型。当我为事件和侦听器添加单独的接口时,很多rawtype and 未经检查的代码中出现警告。我开始将一些方法和侦听器映射转变为通用方法。我一直在摆弄问号、Ts 等等,试图弄清楚这一点。我已经从编码中学到了很多关于泛型的知识,但我似乎无法弄清楚这一点。


我认为答案可以在以下任一中找到:a)将侦听器映射通用(以某种方式?):我想告诉编译器,事件的类型EventListener<? extends Event>属于那种类型Class<? extends Event>描述。或者,b) 在发出警告的行上创建“安全”转换。

我通过这样做尝试了第一个选项(以及更多尝试):

/**
 * The map of event types and their listeners.
 */
private final Map<Class<T extends Event>, Set<EventListener<T>>> listeners = new ConcurrentHashMap<>();

但编译器会告诉你,运气不太好。

我还尝试了第二个选项,添加了以下 if 语句(此处还进行了一些尝试):

if (listener instanceof EventListener<T>) {
    ((EventListener<T>) listener).handle(event); //  <-- This is where the compiler warns me...
}

这也不起作用,因为 T 的类型将在运行时被删除......

也许我很接近,但只是没有使用正确的语法。也许我什至不可能将正确的信息传递给编译器或将其保留在运行时。也许我什至没有走在正确的道路上......


到目前为止我已经浏览过类似的问题this https://stackoverflow.com/questions/262367/type-safety-unchecked-cast, this https://codereview.stackexchange.com/questions/3130/how-can-i-avoid-unchecked-cast-warning-in-my-generic-recursive-iterator and this one https://stackoverflow.com/questions/509076/how-do-i-address-unchecked-cast-warnings,遗憾的是没有太多运气。


不幸的是虽然you我知道.filter(type -> type.isAssignableFrom(event.getClass()))过滤掉不合适的类型,编译器不(也不能)知道它,因此会出现警告。

您的侦听器映射具有通用参数,这些参数不允许您保持完整(编译时)类型安全。通配符可以确保这一点。您知道(在编译时)映射包含某些事件的侦听器作为值,但是一旦将侦听器放入其中,就无法再将其取出,而不会失去编译时类型安全性。

Map<Class<? extends Event>, Set<EventListener<? extends Event>>> listeners

那么你有什么选择呢?嗯,你知道它is检查事件类型后运行时类型安全,因此您可能只想标记一个@SuppressWarnings对方法进行注释并继续。毕竟,这只是一个警告,以确保您知道自己在做什么。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在没有“未经检查”警告的情况下转换为(已知)泛型类型? 的相关文章

  • 如何在一行中将字符串数组转换为双精度数组

    我有一个字符串数组 String guaranteedOutput Arrays copyOf values values length String class 所有字符串值都是数字 数据应转换为Double QuestionJava 中
  • 解决错误:日志已在具有多个实例的atomikos中使用

    我仅在使用atomikos的实时服务器上遇到问题 在我的本地服务器上它工作得很好 我在服务器上面临的问题是 init 中出错 日志已在使用中 完整的异常堆栈跟踪 java lang RuntimeException Log already
  • 异步多播委托

    我最近在一个广泛使用事件的项目上做了一些工作 我需要做的事情之一是在多播委托上异步调用多个事件处理程序 我认为诀窍是对 GetInvocableList 中的每个项目调用 BeginInvoke 但似乎那里不存在 BeginInvoke 有
  • java.io.IOException: %1 不是有效的 Win32 应用程序

    我正在尝试对 XML 文档进行数字签名 为此我有两个选择 有一个由爱沙尼亚认证中心为程序员创建的库 还有一个由银行制作的运行 Java 代码的脚本 如果使用官方 认证中心 库 那么一切都会像魅力一样进行一些调整 但是当涉及到银行脚本时 它会
  • java中删除字符串中的特殊字符?

    如何删除字符串中除 之外的特殊字符 现在我用 replaceAll w s 它删除了所有特殊字符 但我想保留 谁能告诉我我该怎么办 Use replaceAll w s 我所做的是将下划线和连字符添加到正则表达式中 我添加了一个 连字符之前
  • Java 页面爬行和解析之 Crawler4j 与 Jsoup

    我想获取页面的内容并提取其中的特定部分 据我所知 此类任务至少有两种解决方案 爬虫4j https github com yasserg crawler4j and Jsoup http jsoup org 它们都能够检索页面的内容并提取其
  • jdbc4.MySQLSyntaxErrorException:数据库中不存在表

    我正在使用 SpringBoot 开发一个网络应用程序 这是我的application properties文件来指定访问数据库的凭据 spring datasource driverClassName com mysql jdbc Dri
  • hibernate总是自己删除表中的所有数据

    您好 我正在开发一个 spring mvc 应用程序 它使用 hibernate 连接到存储文件的 mysql 数据库 我有两个方法 一个方法添加我选择的特定文件路径中的所有文件 另一种方法调用查询以返回从 mysql 存储的文件列表 问题
  • 如何在jsp代码中导入java库?

    我有以下jsp代码 我想添加 java io 等库 我怎样才能做到这一点
  • Microsoft Graph 身份验证 - 委派权限

    我可以使用 Microsoft Graph 访问资源无需用户即可访问 https developer microsoft com en us graph docs concepts auth v2 service 但是 此方法不允许我访问需
  • 使用 apply 方法的泛型类型的 Scala 工厂?

    假设我有以下特征 它定义了一个接口并采用几个类型参数 trait Foo A B implementation details not important 我想使用伴随对象作为该特征的具体实现的工厂 我还想强制用户使用Foo接口而不是子类所
  • 迁移到 java 17 后有关“每个进程的内存映射”和 JVM 崩溃的 GC 警告

    我们正在将 java 8 应用程序迁移到 java 17 并将 GC 从G1GC to ZGC 我们的应用程序作为容器运行 这两个基础映像之间的唯一区别是 java 的版本 例如对于 java 17 版本 FROM ubuntu 20 04
  • Java中接口作为方法参数

    前几天去面试 被问到了这样的问题 问 反转链表 给出以下代码 public class ReverseList interface NodeList int getItem NodeList nextNode void reverse No
  • 如何将文件透明地传输到浏览器?

    受控环境 IE8 IIS 7 ColdFusion 当从 IE 发出指向媒体文件 例如 mp3 mpeg 等 的 GET 请求时 浏览器将启动关联的应用程序 Window Media Player 我猜测 IIS 提供文件的方式允许应用程序
  • 归并排序中的递归:两次递归调用

    private void mergesort int low int high line 1 if low lt high line 2 int middle low high 2 line 3 mergesort low middle l
  • 制作java包

    我的 Java 类组织变得有点混乱 所以我要回顾一下我在 Java 学习中跳过的东西 类路径 我无法安静地将心爱的类编译到我为它们创建的包中 这是我的文件夹层次结构 com david Greet java greeter SayHello
  • 尝试使用 Ruby Java Bridge (RJB) gem 时出现错误“无法创建 Java VM”

    我正在尝试实现 Ruby Java Bridge RJB gem 来与 JVM 通信 以便我可以运行 Open NLP gem 我在 Windows 8 上安装并运行了 Java 所有迹象 至少我所知道的 都表明 Java 已安装并可运行
  • 使用 SAX 进行 XML 解析 |如何处理特殊字符?

    我们有一个 JAVA 应用程序 可以从 SAP 系统中提取数据 解析数据并呈现给用户 使用 SAP JCo 连接器提取数据 最近我们抛出了一个异常 org xml sax SAXParseException 字符引用 是无效的 XML 字符
  • 当单元格内的 JComboBox 中有 ItemEvent 时,如何获取 CellRow

    我有一个 JTable 其中有一列包含 JComboBox 我有一个附加到 JComboBox 的 ItemListener 它会根据任何更改进行操作 但是 ItemListener 没有获取更改的 ComboBox 所在行的方法 当组合框
  • 将2-3-4树转换为红黑树

    我正在尝试将 2 3 4 树转换为 java 中的红黑树 但我无法弄清楚它 我将这两个基本类编写如下 以使问题简单明了 但不知道从这里到哪里去 public class TwoThreeFour

随机推荐