【问题标题】:Interface with concrete classes [duplicate]与具体类的接口[重复]
【发布时间】:2016-10-26 17:19:42
【问题描述】:

如果我们考虑以下两种实现,第一种的实际用途是什么?

List<String> a= new ArrayList<String>();
ArrayList<String> b= new ArrayList<String>();

根据我在帖子中读到的内容,第一个实现有助于避免破坏性更改,就像我们可以再次更改实现一样

a=new TreeList<String>();

但我不明白用 treelist 更改实现的实际用途是什么,因为我们只能使用 List 接口方法?

【问题讨论】:

  • 想想“排序”。 ArrayList 不会按add 排序。其他实现可能会这样做。因此,如果要求更改为对项目进行排序,您只需交换实现即可。您更改了客户端代码的行为而不更改其实现(除了您选择实现的那一行)。当然:如果您需要使用特定于特定实现的方法,那么您必须使用该实现。

标签: java design-patterns arraylist collections interface


【解决方案1】:

但我不明白用 treelist 更改实现的实际用途是什么,因为我们只能使用 List 接口方法?

接口List的不同实现对于不同的操作有不同的性能特征。您应该选择List 的哪个实现不是任意的 - 不同的实现对于不同的目的更有效。

例如,在ArrayList 的中间某处插入一个元素会很昂贵,但在LinkedList 上却很便宜,因为实现的工作方式。同样,通过索引访问元素在ArrayList 上很便宜,但在LinkedList 上很昂贵。

可能会发生这样的情况,当您开始编写程序时,您使用ArrayList 并没有考虑太多,但后来您发现LinkedList 会更有效。

当您针对接口List 而不是特定实现进行编程时,很容易从ArrayList 更改为LinkedList - 到程序的其余部分,它仍然看起来像List,所以你只需要改变一行。

【讨论】:

  • 非常感谢..我现在明白了
【解决方案2】:

假设您决定自己开发一个更高效的 List 实现。也许一个在内部具有更好的内存管理,或者可能是一种更快的设置方法(插入)实现。您可以只实现 List 接口,其余代码将继续工作而无需任何更改,除了这一行。您还可以扩展 ArrayList 并编写自己的代码。

//Old code
List<String> a = new ArrayList<String>();
a.set(0, "Test");

//New code
List<String> a = new MyCustomisedList<String>();
//Same code, but your optimized set logic. May be faster...
a.set(0, "Test");

【讨论】:

    【解决方案3】:

    TreeList 不存在,所以我们以PersistentList 为例。

    假设您有一个要保存到数据库的@Entity

    public class MyMagicEntity {
        @OneToMany
        List<MyChildEntity> children;
    
        public void setChildren(final List<MyChildEntity> children) {
            this.children = children;
        }
    }
    

    现在,当您创建 MyMagicEntity 时,您会执行类似的操作

    final MyMagicEntity mme = new MyMagicEntity();
    final List<MyChildEntity> children = new ArrayList<>();
    children.add(new MyChildEntity("one"));
    children.add(new MyChildEntity("two"));
    children.add(new MyChildEntity("three"));
    mme.setChildren(children);
    
    //save to DB
    

    因此,您创建了一个 ArrayList,并将其传递给您的 MyMagicEntity,并将其分配给 List - 只要它是 List,它并不关心底层实现。

    现在,稍后你会这样做:

    final MyMagicEntity mme = //load from DB
    final List<Children> children = mme.getChildren();
    

    那么,children 是什么?好吧,如果我们使用 JPA 和 Hibernate,它实际上是 PersistentList,而不是 ArrayList

    当我们访问children 的成员时,Hibernate 会从数据库中提取它们。这个List 仍然是List - 你的程序不需要知道这些。

    不使用List 接口你能做到这一点吗?不!因为:

    • 您无法创建PersistentList
    • Hibernate 无法创建ArrayList

    虽然这是一个极端的例子,List 的基本行为完全不同,但这适用于各种其他情况。

    例如:

    • ArrayListLinkedList 具有不同的性能特征,您可能需要切换
    • Guava 有一个 ImmutableList,您可能想使用它
    • Collections.unmodifyableListimplements List,你可能想用它
    • 您可以有一个由文件支持的List

    基本思想是List 定义了任何列表必须能够做什么,而不是如何完成。

    【讨论】:

    • 感谢您的回答
    【解决方案4】:

    这里的List是一个接口,它包含了一个List可以执行的所有常用操作方法。

    List 接口是 ArrayListLinkedList 和更多类的父级。所以,它可以容纳所有这些类型的对象引用。

    所有这些List 方法在不同的类中都有不同的(或自己的类型)实现。所以,无论你使用什么方法,都会根据 Object 的覆盖方法定义自动应用属于该类。

    List<String> a= new ArrayList<String>();
    ArrayList<String> b= new ArrayList<String>();
    

    现在,在您的情况下,您可以声明两种方式都可以。但假设这样的场景。

    您正在调用一些服务,并且您知道返回任何 List 类型(非特定)对象。它可能是 LinkedListArrayList 或任何其他类型的列表。

    那时无论您得到什么响应,您都可以轻松地将这些响应保存在 List 类型的引用变量中。

    收集结果后,您可以进一步区分对象类型。

    【讨论】:

      猜你喜欢
      • 2023-04-06
      • 1970-01-01
      • 2014-09-04
      • 2013-04-04
      • 2015-07-10
      • 1970-01-01
      • 2017-11-13
      • 2013-12-21
      相关资源
      最近更新 更多