java – HashMap中的消费者映射类

前端之家收集整理的这篇文章主要介绍了java – HashMap中的消费者映射类前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我想创建一个IdentityHashMap< Class< T>,Consumer< T>>.基本上,我想用一种方法来映射一个类型,说明如何处理这种类型.

我想动态地能够用对象X说,执行Y.我能做到

private IdentityHashMap<Class<?>,Consumer<?>> interceptor = new IdentityHashMap<>();

但它很糟糕,因为我在使用它时必须在lamba中投射对象.

例:

interceptor.put(Train.class,train -> {
    System.out.println(((Train)train).getSpeed());
});

我想做的是

private <T> IdentityHashMap<Class<T>,Consumer<T>> interceptor = new IdentityHashMap<>();

但似乎不允许这样做.有没有办法做到这一点 ?使用此类型的方法映射类型的最佳解决方法是什么?

解决方法

这基本上就像 type-safe heterogeneous container described by Joshua Bloch一样,除了你不能使用Class来强制转换结果.

奇怪的是,我找不到SO上存在的一个很好的例子,所以这里有一个:

package mcve;
import java.util.*;
import java.util.function.*;

class ClassToConsumerMap {
    private final Map<Class<?>,Consumer<?>> map =
        new HashMap<>();

    @SuppressWarnings("unchecked")
    public <T> Consumer<? super T> put(Class<T> key,Consumer<? super T> c) {
        return (Consumer<? super T>) map.put(key,c);
    }

    @SuppressWarnings("unchecked")
    public <T> Consumer<? super T> get(Class<T> key) {
        return (Consumer<? super T>) map.get(key);
    }
}

这是类型安全的,因为键和值之间的关系是由put方法的签名强制执行的.

关于Java泛型的局限性的一个令人讨厌的事情是,这些容器中的一个不能为通用值类型编写,因为没有办法做到例如:

class ClassToGenericValueMap<V> {
    ...
    public <T> V<T> put(Class<T> key,V<T> val) {...}
    public <T> V<T> get(Class<T> key) {...}
}

其他说明:

>我会使用常规的HashMap或LinkedHashMap. HashMap得到了更好的维护,并且具有许多IdentityHashMap所没有的优化.
>如果需要使用泛型类型,例如Consumer< List< String>>,则需要使用类似Guava TypeToken的键作为键,因为Class只能表示类型的擦除.
>当你需要Map< Class< T>,T>时,Guava有一个ClassToInstanceMap.

有时人们希望通过类到消费者的地图做这样的事情:

public <T> void accept(T obj) {
   Consumer<? super T> c = get(obj.getClass());
   if (c != null)
       c.accept(obj);
}

也就是说,给定任何对象,找到绑定到该对象类的映射中的使用者,并将该对象传递给使用者的accept方法.

但是,该示例不会编译,因为getClass()实际上被指定为返回Class<? extends | T |>,其中| T |表示T的擦除(参见JLS §4.3.2.)在上面的例子中,T的擦除是Object,所以obj.getClass()返回一个普通的Class<?>.

这个问题可以通过capturing helper method解决

public void accept(Object obj) {
    accept(obj.getClass(),obj);
}
private <T> void accept(Class<T> key,Object obj) {
    Consumer<? super T> c = get(key);
    if (c != null)
        c.accept(key.cast(obj));
}

此外,如果您想要一个返回任何适用消费者的get的修改版本,您可以使用以下内容

public <T> Consumer<? super T> findApplicable(Class<T> key) {
    Consumer<? super T> c = get(key);
    if (c == null) {
        for (Map.Entry<Class<?>,Consumer<?>> e : map.entrySet()) {
            if (e.getKey().isAssignableFrom(key)) {
                @SuppressWarnings("unchecked")
                Consumer<? super T> value =
                    (Consumer<? super T>) e.getValue();
                c = value;
                break;
            }
        }
    }
    return c;
}

这让我们可以将普通超类型消费者放在地图中,如下所示:

ctcm.put(Object.class,System.out::println);

然后使用子类类检索:

Consumer<? super String> c = ctcm.findApplicable(String.class);
c.accept("hello world");

这是一个稍微更一般的例子,这次使用的是UnaryOperator并且没有有界通配符:

package mcve;
import java.util.*;
import java.util.function.*;

public class ClassToUnaryOpMap {
    private final Map<Class<?>,UnaryOperator<?>> map =
        new HashMap<>();

    @SuppressWarnings("unchecked")
    public <T> UnaryOperator<T> put(Class<T> key,UnaryOperator<T> op) {
        return (UnaryOperator<T>) map.put(key,op);
    }

    @SuppressWarnings("unchecked")
    public <T> UnaryOperator<T> get(Class<T> key) {
        return (UnaryOperator<T>) map.get(key);
    }
}

The ?super bounded wildcard in the first example is specific to consumers,我认为没有通配符的例子可能更容易阅读.

猜你在找的Java相关文章