我一直看到集合数据结构被实例化为子实例并由其父接口声明。 例如
Set<E> collection1 = new HashSet<E>();
Map<E> collection1 = new HashMap<E>();
这背后的原因是什么?子类将继承所有方法,并且子类中的Java文档中提到的所有方法都被覆盖但具有相同的含义。那么声明它们是父接口的原因是什么。
任何输入都将极大地帮助我提高基础知识。
修改
感谢您的反馈。我明白了。 但在改变
Set<E> collection1 = new HashSet<E>();
到
Set<E> collection1 = new LinkedHashSet<E>();
我们将丢失对原始HashSet的引用,因为我们正在为引用集声明一个新的LinkedHashSet。那么这样做有什么意义呢? 如果我们可以通过任何方式将旧实现转换为新实现而不丢失数据,那么这将是有意义的。
答案 0 :(得分:4)
这称为接口编码。这基本上已经完成,以便您可以稍后更改接口的底层实现,而无需在其他地方更改代码。您可以稍后替换任何具体的实现。
Set<E> collection1 = new LinkedHashSet<E>();
任何期望接口类型的方法或代码都可以提供任何实现类型。
根据 Effective Java 2nd Edition,Item 52:通过接口引用对象
如果存在适当的接口类型,则应使用接口类型声明参数,返回值和字段。如果您养成使用界面类型的习惯,您的程序将更加灵活。如果没有合适的接口,则通过类引用对象是完全合适的。
这是Liskov substitution principle所说的:
可替代性是面向对象编程的一个原则。它指出,在计算机程序中,如果S是T的子类型,则类型T的对象可以用类型S的对象替换(即,类型S的对象可以替换类型为T的对象)而不改变任何该程序的理想属性(正确性,执行任务等)。
有些我认为您的代码不应该依赖于对象的实现细节,只需要它的已发布界面。
答案 1 :(得分:1)
编译器将变量视为抽象类型(例如Set
,这意味着您可以在以后根据需要交换新的具体实现(JDK中的各种Set实现之一或您自己的实现)。
这是一种很好的编程习惯,因为它减少了耦合。