我们应该使用最顶层的父类作为一种引用变量吗?

时间:2017-01-17 22:54:26

标签: java performance collections

我见过一些人使用最顶层的父类作为变量类型来保存子实例,而有些人只使用父类。例如:

Collection obj = new ArrayList();
Or
List obj = new ArrayList();

这里,List只在Collection下面,那么我们不能使用第一行而不是第二行吗?

同样,我们不能在集合框架中的任何地方使用Collection类的引用变量,只能在Collection下保存类的任何实例?

这是一个好习惯吗?

所以,我想知道哪些是最佳实践,为什么?

如果某人能够在技术上证明性能方面存在问题,那将会非常感激。

3 个答案:

答案 0 :(得分:2)

一般的想法是尽可能地隐藏,以便更容易改变。如果你需要索引例如(List.get(int index)那么它必须是一个列表,因为一个集合不支持.get(index)。如果你不需要索引,那么隐藏你正在使用一个列表的事实,意味着你可以毫不费力地切换到以后可能不是列表的其他集合。

例如,也许一个月后我想使用集合代替列表。但Set不支持.get(index)。因此,使用此List的任何人都可能使用列表的索引功能,这会使切换到集合变得困难,因为其他人使用.get()的每一个都会中断。

另一方面,过度隐藏类型可能会导致意外的性能问题,因为您的方法的使用者不知道类型。假设您返回一个实际上是链表的List(索引为O(n))。假设此列表的使用者对另一个列表中的每个条目执行查找。这可能是O(n * m)性能,这是非常慢的。如果你首先宣传它是一个链表,那么链表的消费者会意识到将多个索引放入这个列表并且消费者可以制作本地副本可能不是一个好主意。

图书馆代码(假设您正在设计的代码)

public class Util {
  public static List<String> makeData() {
    return new LinkedList(Arrays.asList("dogs", "cats", "zebras", "deer"));
  }
}

来电者的代码(假设正在使用您的图书馆或方法的代码)

public static void main(String [] args) {
  List<String> data = Util.makeData();
  int [] indicesToLookUp = {1,4,2,3,0};
  for( int idx : indicesToLookUp ) {
    if(idx < data.size()) { 
      // each index (LinkedList.get()) is slow O(N)
      doSomethingWithEntry(idx, list.get(idx)); 
    }
  }
}

你可能会认为这是调用者的错,因为他错误地认为List是一个ArrayList&lt;&gt;并且应该制作列表的本地副本。

答案 1 :(得分:2)

这真的取决于你的需求。在您的示例中,它对于基本需求并没有太大的改变,但如果您检查这两个接口,则会有一些更改。看: https://docs.oracle.com/javase/7/docs/api/java/util/Collection.htmlhttps://docs.oracle.com/javase/7/docs/api/java/util/List.html

我们可以注意到List可以让你访问Collection没有的方法 例如,set(int index,E element)在List接口中定义,而不是在Collection中定义。

这是因为从Collection继承的每个类都不需要实现所有相同的方法。

表现明智,它没有任何影响。

始终使用具有所需功能的最顶级父类。对于您的示例,不需要高于List。

答案 2 :(得分:2)

没有所谓的&#34;最佳实践&#34;用于选择要用于引用类型的类。实际上,最高层次结构中的类是Object类。您是否使用Object作为参考类型?不,但通常您可以根据自己的需要选择更高级别的所有方法。

不要遵循所谓的最佳做法&#34;而是应用最适合您情况的内容。

这些是使用更高层次结构类作为引用类型的一些优点和缺点:

优势

  • 允许对共享相同祖先(超类)的对象进行分组
  • 允许将给定类的所有实例分配给它

    Animal dog = new Dog(); 
    Animal cat = new Cat();
    
  • 允许多态

    dog.makeNoise();
    cat.makeNoise();
    

当您访问常见行为或成员时,这只是一个优势。

<强>缺点

  • 当您访问存在于一个对象中但不存在于另一个对象中的行为时,需要进行转换。

    dog.swim();  //error, class Animal do not have swim()
    ((Dog)dog).swim();
    
  • 当你开始在公共父类中转储各种对象时,你可能很难弄清楚哪些成员属于哪个类。

    (Cat(cat)).swim();  //error, class Cat do not have swim()