为什么我们在实例化它们之前首先将子类型声明为它们的超类型?

时间:2012-01-06 16:38:24

标签: java oop

阅读其他人的代码,我见过很多:

List<E> ints = new ArrayList<E>();
Map<K, V> map = new HashMap<K, V>();

我的问题是:以这种方式实例化它们的意义/优势是什么,而不是:

ArrayList<E> ints = new ArrayList<E>();
HashMap<K, V> map = new HashMap<K, V>();

奇怪的是,我从未见过像:

CharSequence s = new String("String");

OutputStream out = new PrintStream(OutputStream);


  

重复(问题的第一部分):

     

When/why to use/define an interface

     

Use interface or type for variable definition in java?

     

When should I use an interface in java?

     

why are interfaces created instead of their implementations for every class

     

What's the difference between these two java variable declarations?

12 个答案:

答案 0 :(得分:24)

快速回答?使用接口和超类可以提高代码的可移植性和可维护性,主要是隐藏实现细节。采取以下假设的例子:

class Account {
    private Collection<Transaction> transactions;

    public Account() {
        super();
        transactions = new ArrayList<Transaction>(4);
    }

    public Collection<Transaction> getTransactions() {
        return transactions;
    }
}

我已经宣布了一个帐户合同,该合同规定发布到帐户的交易可以作为集合检索。我的代码的调用者不必关心我的方法实际返回什么样的集合,也不应该。如果需要的话,这可以让我更改内部实现,而不会影响(也就是打破)未知数量的客户端。所以,如果我发现我需要在我的事务中强加某种独特性,我可以将上面显示的实现从ArrayList更改为HashSet,对使用我的类的任何人都没有负面影响

public Account() {
    super();
    transactions = new HashSet<Transaction>(4);
}

就你的第二个问题而言,我可以说你在任何有意义的地方都使用了可移植性和封装的原则。在那里没有可怕的CharSequence实现,而String是迄今为止最常用的。所以你不会看到很多开发人员在他们的代码中声明CharSequence变量。

答案 1 :(得分:11)

使用接口的主要优点是以后可以更改实现(类),而无需更改多于创建实例的单行并进行分配。

答案 2 :(得分:7)

有关

List<E> ints = new ArrayList<E>();
Map<K, V> map = new HashMap<K, V>();

ListMap是接口,因此可以将实现这些接口的任何类分配给这些引用。

ArrayList是实现LinkedList接口的几个类之一(另一个是List)。

Map相同。 HashMapLinkedHashMapTreeMap都实现了Map。

这是一般原则 为接口编程而不是为实现编程 。因此,编程任务变得更容易。您可以动态更改引用的行为。

如果你写

ArrayList<E> ints = new ArrayList<E>();
HashMap<K, V> map = new HashMap<K, V>();

intsmap将永远只有ArrayListHashMap

答案 3 :(得分:4)

您的设计原则是program to the interface and not to the implementation

这样,您可以稍后向同一界面提供新的实现。

从以上链接Eric Gamma解释:

  

这个原则实际上是关于依赖关系,必须在大型应用程序中谨慎管理。在类上添加依赖项很容易。这太简单了;只需添加一个import语句,Eclipse等现代Java开发工具甚至可以为您编写此语句。有趣的是,反过来并不容易,摆脱不必要的依赖可能是真正的重构工作,甚至更糟,阻止你在另一个上下文中重用代码。因此,在引入依赖关系时,您必须睁大眼睛。这个原则告诉我们,依赖于接口通常是有益的。

这里,终端interface不仅指Java工件,而且指的是给定对象的公共接口,它基本上由它拥有的方法组成,因此,它可以是一个Java接口(如{你的例子中的{1}}或具体的超类。

因此,在您的示例中,如果您想要使用List,则会更难,因为只有列表已经足够,类型已经被声明为LinkedList

当然,如果您需要来自给定实现的特定方法,则必须声明该类型。

我希望这会有所帮助。

答案 4 :(得分:4)

@Bhushan回答了原因。 回答你的困惑为什么没有人使用

CharSequence s = new String("String");

OutputStream out = new PrintStream(OutputStream);

CharSequence只包含几种常用方法。实现此接口的其他类主要是缓冲区,只有String是不可变的。 CharSequence为char数组支持的类定义了公共api,此接口不会优化equals和hashCode方法的常规协定(参见javadoc)。

OutputStream是用于写入数据的低级API。因为PrintStream 添加了更方便的编写方法 - 更高级别的抽象,所以它在OutputStream上使用。

答案 5 :(得分:3)

这样做是为了确保以后在使用变量时(或任何使用类的人)不依赖于所选实现的特定方法(ArrayList,HashMap等)

答案 6 :(得分:3)

背后的原因不是技术性的,而是你必须在代码行之间阅读的内容:ListMap示例说:“我只对基本的列表/地图内容感兴趣,基本上你可以在这里使用任何东西。“一个极端的例子是

Iterable<Foo> items = new ArrayList<Foo>();

当你真的只想为每件事做一些事情时。

作为一个额外的好处,这使得稍后将代码重构为不需要具体类型的常见实用程序类/方法更容易一些。或者您想为每种集合多次编写算法代码?

另一方面,String示例并不常见,因为a)String是Java中的特殊类 - 每个"foo"文字自动为String并且更快或者稍后你给一些只接受String的方法,而b)CharSequence实际上是 ahh minimal。它甚至不支持BMP以外的Unicode,它错过了String的大多数查询/操作方法。

答案 7 :(得分:2)

将类型声明为类实现的Interface这种(好)风格很重要,因为它迫使我们使用仅在Interface中定义的方法。

因此,当我们需要更改我们的类实现时(即我们发现我们的ArraySet优于标准HashSet),我们保证如果我们更改类,我们的代码将起作用,因为这两个类都实现了严格执行的Interface

答案 8 :(得分:1)

String开始考虑String更容易。从WhateverList开始考虑List更容易(也更有益)。

奖金会多次讨论,但简而言之,您只需将问题分开:当您需要CharSequence时,即可使用它。您不太可能仅需要ArrayList 通常,任何列表都可以。

答案 9 :(得分:1)

当您在某个时候决定使用不同的实现时,请说:

List<E> ints = new LinkedList<E>();

而不是

List<E> ints = new ArrayList<E>();

此更改需要仅在一个地方

有适当的平衡:

通常您使用的类型为您提供最合适的保证。显然,List也是Collection,也是Iterable。但是一个集合没有给你一个订单,而一个iterable没有“添加”方法。

使用ArrayList作为变量类型也是合理的,当你想更明确地说明对象位置需要快速随机访问时 - 在LinkedList中,“get(100)”是一个慢得多。 (如果Java有这样的接口会很好,但我认为没有。通过使用ArrayList,你不允许将数组作为列表。)

答案 10 :(得分:1)

 List<E> ints = new ArrayList<E>();

如果您编写的代码只处理List,那么它适用于任何实现List的类(例如LinkedList等)。但是,如果您的代码直接处理ArrayList,那么它仅限于ArrayList

CharSequence s = new String("String");

手动实例化String对象并不好。您应该使用字符串文字。我只是猜测你没有看到CharSequence可能的原因,因为它很新,而且字符串是不可变的。

答案 11 :(得分:0)

根据Gang of Four,这是对接口的编程,而不是实现。这将有助于阻止代码依赖于仅添加到特定实现的方法,并且如果由于某种原因而变得必要,则使得更容易更改以使用不同的实现,例如,性能