在Java Collection API中使用接口而不是具体的数据结构

时间:2017-01-07 00:39:49

标签: java collections

我目前正在阅读Horstmann的“不耐烦的核心Java”(我推荐它,喜欢简洁的风格),我很难理解与集合API相关的练习之一。练习如下:

  

我鼓励您使用接口而不是具体的数据结构,例如,a   Map<String, Set<Integer>>代替Map<String, Set<Integer>> toc = new HashMap<>(); toc.put("element1", IntStream.of(1, 2, 3).boxed().collect(Collectors.toSet())); toc.put("element2", IntStream.of (3, 4, 7).boxed().collect(Collectors.toSet())); toc.forEach( (k, v) -> { System.out.print(k + " "); v.forEach(val -> System.out.print(val + " ")); System.out.println(); } ); } 。不幸的是,这个建议只是到目前为止。为什么不能   您使用CONTAINS来表示目录?   (提示:你会如何初始化它?)你可以用什么类型?

以下代码编译并正常工作,即使接口用于变量声明。我错过了什么?

SCAN

2 个答案:

答案 0 :(得分:1)

Map这样的接口是继承它的所有接口的超类型以及实现它的所有类。因此,TreeMap继承自Map,因为您始终可以为变量分配任何属于子类型的引用,因此为TreeMap分配Map引用是完全可以接受的。变量。这称为扩展参考转换 https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.1.5 “扩展引用转换在运行时从不需要特殊操作,因此决不会在运行时抛出异常。它们只是将引用视为具有某种其他类型,在编译时可以证明是正确的。”

所以,是的,您当然可以使用Map<String, Set<Integer>>来表示域模型中的某些内容,但是您无法直接实例化接口;您必须实例化实现它的具体类型(类)。这正是您声明时所做的 Map<String, Set<Integer>> toc = new HashMap<>();

作为这个原则的延伸,你可以轻松写出来 AbstractMap<String, Set<Integer>> toc = new HashMap<>(); 因为AbstractMap也是HashMap的超类型。

通常,您希望为变量声明最宽的类型,该变量可以包含在逻辑中有效的最大可能的子类型引用集。如果你需要一个有序的地图,那么'地图'太宽了;它不会强制排序。您必须将变量声明为TreeMap,或更好,SortedMap

通常界面是最广泛的适用类型,但如果不是,则必须考虑它。

编辑:根据评论提到SortedMap

答案 1 :(得分:0)

我与本书的作者有联系,他同意这个问题不清楚,并且引导读者使用通配符类型。有问题的练习改为:

  

假设您有一个类型为Map<String, Set<Integer>>的方法参数,并且有人使用HashMap<String, HashSet<Integer>>调用您的方法。怎么了?您可以使用哪种参数类型?

答案是在这种情况下应该使用通配符类型:Map<String, ? extends Set<Integer>>