本文介绍了泛型和(超?)类型的令牌是否可以帮助构建类型安全的新闻聚合器?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这个基本的新闻界面

 界面新闻{
String getHeader();
String getText();

和具体类如 SportsNews getStockPrice() getSport()的具体方法, code>等等。新闻旨在分发给

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

问题是如何注册和维护订阅。我尝试的第一种方法是使用中央 Aggregator ,在 Class< T> 对象和 Set< Subscriber< T>> ,但很快这种方法显示不可行。这里是所需的API

  public class Aggregator {

public< N extends News>无效订阅(Subscriber< N>订户){
// TODO以某种方式提取(超级类型令牌)提取N和
//将该项目添加到getSubscribers()获取的集合


public< N extends News> (订阅者< N>订阅者:getSubscribersFor(news.getClass())){
subscriber.onNews(news);
}
}

private< N extends News>设置<订户LT; N>> getSubscribersFor(Class k){
// TODO从Map
}
}


有没有其他选择是安全的? Java可以解决这个问题吗?我在网上帮助您更好地理解问题的实质。



UPDATE

另一种方法是使 Aggregator 本身用实际新闻类型参数化。这样可以,除了这是一个鸡和鸡蛋问题:现在需要找到一种方法来检索聚合器。在Java中,无法表达以下内容:

$ p $ interface新闻{
static Aggregator< CurrentClass> getAggregator();




  • static 方法不能是 abstract

  • 没有办法引用当前类型在类型参数中


解决方案

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

首先,从您的订阅者中添加一个方法来获取该类:

  public接口订户< N extends News> {
void onNews(N news);
Class< N> getSupportedNewsType();
}

然后执行时:

  public class MySubscriber实现Subscriber< MyNews> {

// ...

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






$在你的聚合器中,值不是输入:

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

另外请注意,Guava有一个multimap实现,可以为您创建多个值。只需GoogleGuava Multimap即可找到它。



注册订户:

  public< N extends News> void register(Subscriber< N> subscriber){
//这里使用的方法创建一个新的集合,如果一个集合尚不存在
Set< Subscriber<>> subscribers = getSubscriberSet(subscriber.getSupportedNewsType());
subscribers.add(订户);
}

并发送:

  @SuppressWarnings( 未登记); 
public< N extends News> void dispatch(N news){
Set< Subscriber<>> subs = subscribersByClass.get(news.getClass());
if(subs == null)
return; (Subscriber<> sub:subs){
((Subscriber< N> sub).onNews(news))的


}
}

注意这里的剧组。由于 register 方法和 Subscriber 接口之间泛型的性质,这是安​​全的,只要没有人提供一些荒谬的错误,像原始类型如实现了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;

您的私人方法并且字段不需要是类型安全的。由于Java的泛型是通过擦除来实现的,所以它不会造成任何问题,所以这里的所有集合都只是一组对象。试图让他们安全的类型只会导致讨厌的,不必要的演员,这些演员不会影响其正确性。



确实重要的是你的 public 方法是类型安全的。在 Subscriber Aggregator 中的公共方法中声明泛型的唯一方法是通过raw类型,就像我上面所说的那样。简而言之,只要没有不安全的转换或原始数据,每个订阅者传递给注册的保证 接受您注册它的类型。打字。




使用番石榴



或者,看看Guava的 EventBus 。这对于你正在尝试做的事情会更容易,IMO。
$ b

Guava的 EventBus 类使用注释 - 驱动事件派发,而不是接口驱动。这很简单。您将不再拥有订阅者界面。相反,您的实现将如下所示:

  public class MySubscriber {
// ...

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



@Subscribe 注释指示Guava的 EventBus 它应该记住稍后调度的方法。然后注册它并分派事件,使用 EventBus isntance:

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

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

public void dispatch(新闻新闻){
eventBus.dispatch(news);






$ b这会自动找到接受 news 对象,并为您进行调度。你甚至可以在同一个类中多次订阅:


  public class MySubscriber {
// ...

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

@订阅
public void anEntirelyDifferentMethod {
// Handle news
}
}

或同一订阅者中的多种类型:

  public class MySubscriber {
// ...

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

@订阅
public void handleNews(YourNews news){
//处理新闻
}
}

最后, EventBus 尊重层级结构,所以如果您有一个扩展 MyNews 的类,比如 MyExtendedNews ,然后调度 MyExtendedNew s 事件也会传递给那些关心 MyNews 事件的人。接口也一样。通过这种方式,您甚至可以创建全球用户:

  public class GlobalSubscriber {
// ...

@订阅
public void handleAllTheThings(News news){
// Handle news
}
}


I have this basic News interface

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

and concrete classes like SportsNews and FinancialNews to provide specific methods like getStockPrice(), getSport() and so on. News are intended to be dispatched to a

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

The problem is how to register and maintain subscriptions. The first approach I tried was using a central Aggregator, keeping a map between Class<T> objects and Set<Subscriber<T>>, but soon this approach revealed unviable. Here is the desired 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
    }
}

Is there any alternative to be type safe? Can Java solve this problem at all? I put this little demo online to help you better understand what the problem really is.

UPDATE

An alternative would be to make Aggregator itself parameterized with the actual news type. This would be ok, except that it's a chicken and egg problem: now one needs to find a way to retrieve the aggregator. In Java there's no way to express the following

interface News {
    static Aggregator<CurrentClass> getAggregator();
}

  • static method can't be abstract
  • there's no way to reference the current type in a type argument

解决方案

Here's what I would do. If you can use Guava (a Google library written and used by Google), I recommend scrolling down and looking at the other solution first.

Vanilla Java

First, start by adding a method to get the class from your subscribers:

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

Then when implementing:

public class MySubscriber implements Subscriber<MyNews> {

    // ...

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

In your aggregator, include a map where the keys and values aren't typed:

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

Also note that Guava has a multimap implementation that will do this key to multiple values stuff for you. Just Google "Guava Multimap" and you'll find it.

To register a subscriber:

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);
}

And to dispatch:

@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);
    }
}

Notice the cast here. This will be safe because of the nature of the generics between the register method and the Subscriber interface, provided no one does something ridiculously wrong, like raw-typing such as implements Subscriber (no generic argument). The SuppressWarnings annotation suppresses warnings about this cast from the compiler.

And your private method to retrieve subscribers:

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 methods and fields do not need to be type safe. It won't cause any problems anyway since Java's generics are implemented via erasure, so all of the sets here will be just a set of objects anyway. Trying to make them type safe will only lead to nasty, unnecessary casts that have no bearing on its correctness.

What does matter is that your public methods are type safe. The way the generics are declared in Subscriber and the public methods on Aggregator, the only way to break it is via raw types, like I stated above. In short, every Subscriber passed to register is guaranteed to accept the types that you're registering it for as long as there's no unsafe casts or raw typing.


Using Guava

Alternatively, you can take a look at Guava's EventBus. This would be easier, IMO, for what you're trying to do.

Guava's EventBus class uses annotation-driven event dispatching instead of interface-driven. It's really simple. You won't have a Subscriber interface anymore. Instead, your implementation will look like this:

public class MySubscriber {
    // ...

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

The @Subscribe annotation signals to Guava's EventBus that it should remember that method later for dispatching. Then to register it and dispatch events, use an EventBus isntance:

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

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

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

This will automatically find the methods that accept the news object and do the dispatching for you. You can even subscribe more than once in the same class:

public class MySubscriber {
    // ...

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

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

Or for multiple types within the same subscriber:

public class MySubscriber {
    // ...

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

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

Lastly, EventBus respects hierarchical structures, so if you have a class that extends MyNews, such as MyExtendedNews, then dispatching MyExtendedNews events will also be passed to those that care about MyNews events. Same goes for interfaces. In this way, you can even create a global subscriber:

public class GlobalSubscriber {
    // ...

    @Subscribe
    public void handleAllTheThings(News news) {
        // Handle news
    }
}

这篇关于泛型和(超?)类型的令牌是否可以帮助构建类型安全的新闻聚合器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-26 23:51