泛型和(超级?)类型标记可以帮助构建类型安全的新闻聚合器吗?

2024-04-21

我有这个基本的News界面

interface News {
    String getHeader();
    String getText();
}

和具体的类,如SportsNews and FinancialNews提供具体方法,例如getStockPrice(), getSport()等等。新闻旨在发送至

interface Subscriber<N extends News> {
    void onNews(N news);
}

问题是如何注册和维护订阅。我尝试的第一种方法是使用中央Aggregator,在之间保留地图Class<T>物体和Set<Subscriber<T>>,但很快这种方法就显露出不可行。这是所需的 API

public class Aggregator {

    public <N extends News> void subscribe(Subscriber<N> subscriber) {
        // TODO somehow (super type token) extract N and 
        // add the item to the set retrieved by getSubscribersFor()
    }

    public <N extends News> void dispatch(N news) {
        for (Subscriber<N> subscriber: getSubscribersFor(news.getClass())) {
            subscriber.onNews(news);
        }
    }

    private <N extends News> Set<Subscriber<N>> getSubscribersFor(Class<N> k) {
        // TODO retrieve the Set for the specified key from the Map
    }
}

有没有其他方法可以保证类型安全? Java能解决这个问题吗?我放这个在线小演示 http://pastebin.com/LVbcw0Nv帮助您更好地了解问题的根源。

UPDATE

另一种选择是Aggregator本身用实际的新闻类型参数化。这没什么问题,只是这是一个先有鸡还是先有蛋的问题:现在需要找到一种方法来检索聚合器。在Java中没有办法表达以下内容

interface News {
    static Aggregator<CurrentClass> getAggregator();
}
  • static方法不能是abstract
  • 没有办法引用当前类型在类型参数中

这就是我要做的。如果您可以使用 Guava(由 Google 编写和使用的 Google 库),我建议您向下滚动并首先查看其他解决方案。

香草爪哇

首先,首先添加一个从订阅者处获取类的方法:

public interface Subscriber<N extends News> {
    void onNews(N news);
    Class<N> getSupportedNewsType();
}

然后在实施时:

public class MySubscriber implements Subscriber<MyNews> {

    // ...

    public Class<MyNews> getSupportedNewsType() {
        return MyNews.class;
    }
}

在聚合器中,包含一个未键入键和值的映射:

private Map<Class<?>, Set<Subscriber<?>> subscribersByClass = ... ;

另请注意,Guava 有一个多重映射实现,可以为您执行此键到多个值的操作。只要谷歌“Guava Multimap”,你就会找到它。

注册订阅者:

public <N extends News> void register(Subscriber<N> subscriber) {
    // The method used here creates a new set and puts it if one doesn't already exist
    Set<Subscriber<?>> subscribers = getSubscriberSet(subscriber.getSupportedNewsType());
    subscribers.add(subscriber);
}

并发送:

@SuppressWarnings("unchecked");
public <N extends News> void dispatch(N news) {
    Set<Subscriber<?>> subs = subscribersByClass.get(news.getClass());
    if (subs == null)
        return;

    for (Subscriber<?> sub : subs) {
        ((Subscriber<N>) sub).onNews(news);
    }
}

注意这里的演员阵容。由于泛型之间的性质,这将是安全的register方法和Subscriber接口,前提是没有人做一些可笑的错误,比如原始类型,例如implements Subscriber(没有通用参数)。这SuppressWarnings注释会禁止编译器发出有关此转换的警告。

以及您检索订阅者的私有方法:

private Set<Subscriber<?>> getSubscriberSet(Class<?> clazz) {
    Set<Subscriber<?>> subs = subscribersByClass.get(news.getClass());
    if (subs == null) {
        subs = new HashSet<Subscriber<?>>();
        subscribersByClass.put(subs);
    }
    return subs;
}

Your private方法和字段不需要类型安全。无论如何,它不会造成任何问题,因为 Java 的泛型是通过擦除实现的,所以这里的所有集合无论如何都只是一组对象。试图使它们类型安全只会导致令人讨厌的、不必要的强制转换,而这与其正确性无关。

What does问题是你的public方法是类型安全的。泛型的声明方式Subscriber和公共方法Aggregator,打破它的唯一方法是通过原始类型,就像我上面所说的那样。简而言之,每一个Subscriber传递给寄存器的是保证只要不存在不安全的强制转换或原始类型,就可以接受您注册的类型。


使用番石榴

或者,您可以看看 Guava 的EventBus。 IMO,对于您想要做的事情来说,这会更容易。

Guava's EventBus类使用注释驱动的事件调度而不是接口驱动。这真的很简单。你不会有一个Subscriber界面不再了。相反,您的实现将如下所示:

public class MySubscriber {
    // ...

    @Subscribe
    public void anyMethodNameYouWant(MyNews news) {
        // Handle news
    }
}

The @SubscribeGuava 的注释信号EventBus它应该记住该方法以便稍后进行调度。然后要注册它并分派事件,请使用EventBus实例:

public class Aggregator {
    private EventBus eventBus = new EventBus();

    public void register(Object obj) {
        eventBus.register(obj);
    }

    public void dispatch(News news) {
        eventBus.dispatch(news);
    }
}

这将自动找到接受的方法news反对并为您进行调度。您甚至可以在同一课程中多次订阅:

public class MySubscriber {
    // ...

    @Subscribe
    public void anyMethodNameYouWant(MyNews news) {
        // Handle news
    }

    @Subscribe
    public void anEntirelyDifferentMethod(MyNews news) {
        // Handle news
    }
}

或者对于同一订阅者内的多种类型:

public class MySubscriber {
    // ...

    @Subscribe
    public void handleNews(MyNews news) {
        // Handle news
    }

    @Subscribe
    public void handleNews(YourNews news) {
        // Handle news
    }
}

Lastly, EventBus尊重层次结构,所以如果你有一个扩展的类MyNews, 例如MyExtendedNews,然后调度MyExtendedNews事件也会传递给关心的人MyNews事件。接口也是如此。通过这种方式,你甚至可以创建一个全局订阅者:

public class GlobalSubscriber {
    // ...

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

泛型和(超级?)类型标记可以帮助构建类型安全的新闻聚合器吗? 的相关文章

随机推荐

  • Spring 表达语言的映射

    我想使用 SpEL 来评估一些谓词 由于值 属性是动态的 因此我没有特定的 bean 类 因此 我们有一个 hashmap 其中 根据应用程序状态 键映射到不同的 POJO bean 例如 Person person new Person
  • Git 推送失败,“非快进更新被拒绝”

    我已经通过 Git Online 编辑了我的 GIT 存储库 当我尝试推送本地代码更改后 出现错误 Git push failed To prevent from losing history non fast forward update
  • Oracle中如何检查所有字段是否唯一?

    Oracle中如何检查所有字段是否唯一 SELECT myColumn COUNT FROM myTable GROUP BY myColumn HAVING COUNT gt 1 如果出现次数大于 1 即它们不是唯一的 这将返回所有 my
  • Java 中的数据验证和扫描器

    我有一个关于数据验证和扫描仪的问题 下面的代码检查用户输入 除了整数之外的任何内容都是不允许的 并且要求用户重新输入一个值 我的问题是 只有当扫描仪打开时 代码才有效在 while 循环内声明 如果在外部声明扫描仪 则程序将无限执行 这是为
  • 如何在.net core项目中使用SignalR Core监听postgresql数据库?

    我正在开发 net core Web 应用程序 我想监听我的 PostgreSQL 数据库 如果桌面上有任何变化 我必须得到它 所以根据我的研究 我必须使用SignalR Core 我用 SignalR 做了一些示例应用程序 例如聊天应用程
  • pod init 获得未初始化的常量 ActiveSupport::XmlMini::IsolatedExecutionState (NameError)

    mac os 成功安装cocoapods 1 11 2 红宝石 v ruby 3 1 1p18 2022 02 18 修订版 53f5fc4236 x86 64 darwin21 宝石 v 3 3 8 但 exec pod init 收到错
  • django 管理中的多态行为

    问题 假设我有三个类A B和C 其中B是A的子类 C是B的子类 A lt B lt C 当我打开管理并列出所有 A 时 我看到所有 A B 和 C 但是 当我点击其中一个链接查看详细信息并编辑该特定 B 时 我只会看到 B 的字段 即使该实
  • 突出显示用户选择的文本片段

    我有一个 div with some text div 我需要突出显示用户选择的文本片段 我已经部分实现了这个 这是我的代码 http jsfiddle net Greatshock fy09jo8w 8 thisRespondHightl
  • 能否在上传 Azure CloudBlockBlob 的同时在其上设置元数据?

    我目前使用以下方式上传 CloudBlockBlob CloudBlockBlob UploadFromStreamAsync 然后我立即在上面设置了一堆用户元数据 问题是 我有一个事件网格事件 该事件在上传 blob 时触发 但事件处理程
  • 如何在多个 HTML 页面上播放背景音频?

    是否有解决方案可以让背景音频 音乐在网站上的多个页面上播放 而无需在每个页面加载时重新启动 该网站当前使用框架集 但我正在寻找替代方案 如果不让整个网站都 AJAX 我认为框架是唯一的方法 如果您需要的话 这里有一个关于制作 ajax 站点
  • JavaScript 多行字符串[重复]

    这个问题在这里已经有答案了 问题是 像在 PHP 中一样将多行字符串存储到变量中的 JavaScript 方法是什么 如果 多行字符串 是指包含换行符的字符串 则可以通过使用转义它们来编写这些字符串 n 对于换行符 var multilin
  • 如何从命令行将 Svelte 文件编译为 JavaScript?

    我是新来的Svelte https svelte dev 快速提问 Svelte 编译器有 CLI 吗 如何访问它 我知道 Svelte 有一个使用 rollup 的预配置设置 但不使用什么 用于构建应用程序 一切都很好 但我只需要 CLI
  • Sublime Text 2 在 Java 开发方面是否胜过 Eclipse [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 是时候使用 Django 1.5 了吗? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • a += b 与 a = a + b 不同[重复]

    这个问题在这里已经有答案了 可能的重复 为什么 在列表上表现异常 https stackoverflow com questions 2347265 why does behave unexpectedly on lists 今天我发现了P
  • C++ 编译器会优化重复的函数调用吗?

    编译器 通常或特别 是否优化重复的函数调用 例如 考虑这种情况 struct foo member type m return type f const returns by value 函数定义在一个翻译单元中 return type f
  • 使用样式化组件设置滑块拇指样式

    我正在尝试使用 React 的样式组件来设置滑块的样式 但我不知道如何设置拇指的样式 我有一个看起来像这样的CSS faderInput webkit slider thumb webkit appearance none width 15
  • Javascript C++ 绑定?

    我有一些 C 代码想要公开给 Web 应用程序的客户端 理想情况下 我想为我的 C 类编写 Javascript 包装对象 以便我可以在客户端使用它们 这以前做过吗 有谁有链接来展示如何实现这一目标 有一个库可以将 C 代码转换为 java
  • 是否可以使用 JNI 在 C 或 C++ 中实现 Java 接口?

    假设一个 Java 库包含一个类 我们称它为Foo 该类包含一个构造函数和两个方法 constructor Foo returns a random int public int bar generates a random int x a
  • 泛型和(超级?)类型标记可以帮助构建类型安全的新闻聚合器吗?

    我有这个基本的News界面 interface News String getHeader String getText 和具体的类 如SportsNews and FinancialNews提供具体方法 例如getStockPrice g