在java中使用接口或类型进行变量定义?

ArrayList aList = new ArrayList(); List aList = new ArrayList(); 

这两者之间有什么区别,哪个更好用,为什么?

List是接口,其中ArrayList是类,并且该类实现List接口。

我更喜欢第二种forms,它更通用,即如果你不使用特定于ArrayList你可以将它的类型声明为接口List类型。 使用第二种forms,可以更容易地将实现从ArrayList更改为实现List接口的其他类。

编辑:由于许多SO用户评论这两个表单可以是任何接受ListArrrayList 。 但是,当我声明方法时,我更喜欢接口:

  void showAll(List  sl) ... 

并使用:

  void showAllAS(ArrayList  sl) ... 

只有当我的方法使用特定于ArrayList ,例如ensureCapacity()

响应信息我们应该使用类型List而不仅仅是List非常好(当然如果我们不使用古代Java)。

List是一个接口,而ArrayList是该接口的一个实现。

第二个更好,因为这意味着您可以稍后更改ArrayList以用于List的另一个实现,而无需更改应用程序的其余部分。 出于性能原因,或者由于您已选择/将要选择的List实现的行为的其他方面,您可能希望这样做。

自Java 1.5以来,两者都被弃用。

它应该是:

 List list = new ArrayList(); // or whatever data type you are using in your list 

请阅读Joshua Bloch的Effective Java ,特别是这两个项目:

  • 23:不要在新代码中使用原始类型(甚至可以在线获取 )
  • 52:通过接口引用对象

顺便说一下,如果你使用Guava ,你有一个工厂方法来构造一个ArrayList,所以你不必重复type参数:

 List list = Lists.newArraylist(); 

在大多数情况下,我更喜欢第二种,因为它表示你没有使用ArrayList api中的任何特定内容,如果你以后需要替换任何其他类型的List而不必更改除第一行之外的任何代码。

所有这些答案都与他们在某处读到的一些教条相同。

变量声明和初始化语句, Type x = new Constructor(); ,绝对是实施细节的一部分。 它不是公共API的一部分(除非它是public final ,但List是可变的,所以这是不合适的)

作为一个实现细节,你是谁试图欺骗你的抽象? 最好保持类型尽可能具体。 它是数组列表还是链表? 它应该是线程安全吗? 选择对于您的实现很重要,您仔细选择了特定的列表。 然后你宣布它只是一个List ,好像它没关系,你不关心?

将其声明为List的唯一合理理由是我懒得打字 。 这也包含了这样的论点: 如果我需要移动到另一个List impl,我可以少修改一个

这个原因只有在变量范围很小的情况下才合法,您可以一目了然地看到它的所有用法。 否则,保留最具体的类型,以便在使用该变量的所有代码中显示其性能和语义特征。

由于类型推断和学说OOP有限,这是一个Java怪癖。 对于局部变量,它主要是风格; 如果您需要子类型的某些特定function,请使用子类型(下面的示例),否则可以使用其中任何一种。

Java风格是使用超类型,甚至在实现主体内强制接口,并与可见类型保持一致( Effective Java 2nd Edition:Item 52:通过接口引用对象)。 在具有更多类型推断的语言中,例如C ++ / C#/ Go /等,您不需要显式声明类型,并且局部变量将具有特定类型。

对于可见类型(public或protected:fields,或参数和方法的返回值),您几乎总是希望使用最通用的类​​型:接口或抽象类,以提供更好的灵活性( Effective Java :Item 40:Design method signature )。 但是,对于不可见的类型(私有或包私有成员或局部变量),可以使用(它只是样式),有时需要使用更具体的类型,包括具体类。

有关标准指南,请参阅Effective Java ; 个人想法如下。

即使不可见也使用更一般类型的原因是为了减少噪音:你说你只需要更通用的类型。 如果您更改类型,则在不可见的成员(如私有方法)上使用常规类型也可以减少流失。 但是,这不适用于局部变量,它只是改变一行: ConcreteFoo foo = new ConcreteFoo(); to OtherConcreteFoo foo = new OtherConcreteFoo();

您需要子类型的情况包括:

  • 您只需要在子类型上出现的成员,例如:
    • 实现的一些function,例如ensureCapacity for ArrayList
    • (常见于测试代码中)假类的一些成员,如(假设) FakeFileSystem#createFakeFile
  • 您依赖于子类型的行为,特别是在超类型方法的覆盖中,例如:
    • 具有更一般类型的参数,
    • 更具体的返回类型,或
    • 抛出更具体或更少的exception类型。

作为最后一个示例,请参阅我应该关闭StringReader吗? : StringReader.html #clut覆盖Reader.html #close并且不会抛出IOException ,因此使用StringReader而不是Reader来表示本地变量意味着您不需要处理实际上不会发生的exception,并且显着减少了样板。

如果您使用的是Java 1.4或更早版本,那么我将使用第二个。 最好尽可能一般地声明你的字段,以防你以后需要把它变成别的东西,比如Vector,等等。

从1.5开始,我将使用以下内容

 List x = new ArrayList(); 

它为您提供了一点类型安全性。 列表’x’可以添加一个String对象,就是这样。 当您从List’x’获得一个项目时,您可以指望一个String将要返回的事实。 这也有助于删除不必要的转换,这可能会使您在6个月后返回时难以阅读代码并尝试记住代码的作用。 如果您尝试添加任何其他对象类型,您的编译器/ IDE将通过显示错误来帮助您记住列表’x’应该进入的类型。

如果要向List添加多个Object类型,则可以添加Annotation以抑制编译错误

 @SuppressWarnings("unchecked") List y = new ArrayList(); 

我知道至少有一种情况,当使用接口声明变量不起作用时。 当你想使用reflection。

我对一些代码进行了错误修复,我将变量声明为Map并为其分配了一个HashMap的实例。 此变量用作通过reflection访问的方法调用中的参数。 问题是reflection尝试使用HashMap签名而不是声明的Map签名来查找方法。 由于没有使用HashMap作为参数的方法,因此我无法通过reflection找到方法。

 Map map = new HashMap(); public void test(Map m) {...}; Method m = this.getClass().getMethod("test", new Class[]{map.getClass()}); 

找不到使用该接口的方法。 如果您使用HashMap进行另一个版本的测试,那么它将起作用 – 但现在您被迫使用具体类声明您的变量而不是更灵活的接口…