在使用新的
Java 8 Stream API的同时,我想知道为什么不:
public interface Map<K,V> extends Function<K,V>
甚至:
public interface Map<K,V>,Predicate<K>
在Map界面上使用默认方法实现相当容易:
@Override default boolean test(K k) { return containsKey(k); } @Override default V apply(K k) { return get(k); }
并且它将允许在地图方法中使用地图:
final MyMagicMap<String,Integer> map = new MyMagicHashMap<>(); map.put("A",1); map.put("B",2); map.put("C",3); map.put("D",4); final Stream<String> strings = Arrays.stream(new String[]{"A","B","C","D"}); final Stream<Integer> remapped = strings.map(map);
或者作为过滤器方法中的谓词.
我发现我的Map的很大一部分用例正是那个构造或类似的一个 – 作为重映射/查找功能.
解决方法
JDK团队当然知道java.util.Map作为数据结构和java.util.function.Function之间的数学关系作为映射函数.毕竟,Function在早期的JDK 8
prototype builds中被命名为Mapper.并且在每个流元素上调用函数的流操作称为Stream.map.
甚至有人可能会将Stream.map重命名为像转换一样的东西,因为转换函数和Map数据结构之间可能会有混淆. (对不起,找不到链接.)这个提议被拒绝,理由是概念上的相似之处(为了这个目的的地图是一般用法).
主要的问题是,如果java.util.Map是java.util.function.Function的子类型,将会获得什么? comments有一些关于子类型是否意味着“is-a”关系的讨论.子类型不是关于对象的“is-a”关系 – 因为我们在谈论接口而不是类 – 但它意味着可替代性.所以如果Map是Function的子类型,那么可以这样做:
Map<K,V> m = ... ; source.stream().map(m).collect(...);
我们马上就面对现在的Function.apply到现有的Map方法的行为.可能唯一合理的是Map.get,如果密钥不存在,则返回null.这些语义坦率地说是一种糟糕的语言.真正的应用程序可能会自己编写自己的方法,提供关键缺失策略,所以似乎没有什么好处可以写
map(m)
代替
map(m::get)
要么
map(x -> m.getOrDefault(x,def))