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

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(使用前将#替换为@)

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

  • 如何让 BlazeDS 忽略属性?

    我有一个 java 类 它有一个带有 getter 和 setter 的字段 以及第二对 getter 和 setter 它们以另一种方式访问 该字段 public class NullAbleId private static final
  • Spring应用中Eureka健康检查的问题

    我正在开发一个基于 Spring 的应用程序 其中包含多个微服务 我的一个微服务充当尤里卡服务器 到目前为止一切正常 在我所有其他微服务中 用 EnableEurekaClient 我想启用这样的健康检查 应用程序 yml eureka c
  • 如何通过 javaconfig 使用 SchedulerFactoryBean.schedulerContextAsMap

    我使用 Spring 4 0 并将项目从 xml 移至 java config 除了访问 Service scheduleService 带注释的类来自QuartzJobBean executeInternal 我必须让它工作的 xml 位
  • 在内存中使用 byte[] 创建 zip 文件。 Zip 文件总是损坏

    我创建的 zip 文件有问题 我正在使用 Java 7 我尝试从字节数组创建一个 zip 文件 其中包含两个或多个 Excel 文件 应用程序始终完成 没有任何异常 所以 我以为一切都好 当我尝试打开 zip 文件后 Windows 7 出
  • .properties 中的通配符

    是否存在任何方法 我可以将通配符添加到属性文件中 并且具有所有含义 例如a b c d lalalala 或为所有以结尾的内容设置一个正则表达式a b c anything 普通的 Java 属性文件无法处理这个问题 不 请记住 它实际上是
  • 如何使用assertEquals 和 Epsilon 在 JUnit 中断言两个双精度数?

    不推荐使用双打的assertEquals 我发现应该使用带有Epsilon的形式 这是因为双打不可能100 严格 但无论如何我需要比较两个双打 预期结果和实际结果 但我不知道该怎么做 目前我的测试如下 Test public void te
  • 如何获取之前的URL?

    我需要调用我的网络应用程序的 URL 例如 如果有一个从 stackoverflow com 到我的网站 foo com 的链接 我需要 Web 应用程序 托管 bean 中的 stackoverflow 链接 感谢所有帮助 谢谢 并不总是
  • 在 Jar 文件中运行 ANT build.xml 文件

    我需要使用存储在 jar 文件中的 build xml 文件运行 ANT 构建 该 jar 文件在类路径中可用 是否可以在不分解 jar 文件并将 build xml 保存到本地目录的情况下做到这一点 如果是的话我该怎么办呢 Update
  • 谷歌应用程序引擎会话

    什么是java应用程序引擎 默认会话超时 如果我们将会话超时设置为非常非常长的时间 会不会产生不良影响 因为谷歌应用程序引擎会话默认情况下仅存储在数据存储中 就像facebook一样 每次访问该页面时 会话仍然永远存在 默认会话超时设置为
  • 在接口中使用默认方法是否违反接口隔离原则?

    我正在学习 SOLID 原则 ISP 指出 客户端不应被迫依赖于他们所使用的接口 不使用 在接口中使用默认方法是否违反了这个原则 我见过类似的问题 但我在这里发布了一个示例 以便更清楚地了解我的示例是否违反了 ISP 假设我有这个例子 pu
  • 没有 Spring 的自定义 Prometheus 指标

    我需要为 Web 应用程序提供自定义指标 问题是我不能使用 Spring 但我必须使用 jax rs 端点 要求非常简单 想象一下 您有一个包含键值对的映射 其中键是指标名称 值是一个简单的整数 它是一个计数器 代码会是这样的 public
  • 在我的 Spring Boot 示例中无法打开版本 3 中的 Swagger UI

    我在 Spring Boot 示例中打开 swagger ui 时遇到问题 当我访问 localhost 8080 swagger ui 或 localhost 8080 root api name swagger ui 时出现这种错误 S
  • 为什么 Java 8 不允许非公共默认方法?

    让我们举个例子 public interface Testerface default public String example return Hello public class Tester implements Testerface
  • 如何使用mockito模拟构建器

    我有一个建造者 class Builder private String name private String address public Builder setName String name this name name retur
  • 从 scala 的 Type 获取 ParameterizedType?

    有用的是 scala 的 Universe typeOf 保留了类的类型参数 import scala reflect runtime universe case class X T TypeTag val t typeOf T e g S
  • ASP.NET Click() 事件在第二次回发时不会触发

    我有一个 ASP NET Web 表单 我第一次提交表单时 会引发 提交按钮单击 事件 表单返回到浏览器时可能会出现验证错误 或者可以选择使用新值再次提交表单 当再次提交表单时 提交按钮单击 事件永远不会触发 Page Load 触发 但按
  • CamcorderProfile.videoCodec 返回错误值

    根据docs https developer android com reference android media CamcorderProfile html 您可以使用CamcorderProfile获取设备默认视频编解码格式 然后将其
  • 双枢轴快速排序和快速排序有什么区别?

    我以前从未见过双枢轴快速排序 是快速排序的升级版吗 双枢轴快速排序和快速排序有什么区别 我在 Java 文档中找到了这个 排序算法是双枢轴快速排序 作者 弗拉基米尔 雅罗斯拉夫斯基 乔恩 本特利和约书亚 布洛赫 这个算法 在许多数据集上提供
  • Spring Boot 无法更新 azure cosmos db(MongoDb) 上的分片集合

    我的数据库中存在一个集合 documentDev 其分片键为 dNumber 样本文件 id 12831221wadaee23 dNumber 115 processed false 如果我尝试使用以下命令通过任何查询工具更新此文档 db
  • 我的班级应该订阅自己的公共活动吗?

    我正在使用 C 3 0 遵循标准事件模式我有 public event EventHandler

随机推荐