tl;博士
这个:
LinkedList < Integer > curList = new LinkedList<>() ; // <-- Declared as `LinkedList`.
parentList.add( curList ) ;
……还有这个:
List < Integer > curList = new LinkedList<>() ; // <-- Declared as `List`.
parentList.add( curList ) ;
…具有相同的效果。
在这两种情况下,parentList 都期望收到一个包含Integer 对象的List。 LinkedList 同时是 LinkedList 和 List。所以通过定义为List 或定义为LinkedList 的curList 没有区别。在这两种情况下,parentList.add 方法都会收到 List。
将LinkedList 对象分配给List 变量不会改变对象的性质。
List 表示任何类型的 List 实现:ArrayList、LinkedList 等等
您的第一个集合 parentList 是一个列表列表。
- 该列表中的每个元素本身就是另一个列表。声明
List< List < Integer > > parentList 告诉我们,我们有一个 Integer 对象列表的列表。
- 外部列表和内部列表都可以是任何类型的
List,不一定是ArrayList 或LinkedList。
您的第二个集合curList 只是Integer 对象的列表。
- 我们从它的声明中知道这一点,
LinkedList < Integer > curList。
- 在此声明中,我们说列表绝对是
LinkedList,而不是任何其他类型的List。
这是一个完整的例子。
首先我们创建一个List 对象,其中包含Integer 对象的列表。这个外部列表是空的。
然后我们添加三个单位奇数(3、5、9)的列表。这个不可修改的列表是由List.of 方法生成的。用于List返回的对象的List的具体实现在未承诺的情况下,可能因情况和Java版本而异。我们所知道的是,我们正在一些实现List,其中包含Integer 对象。
然后我们创建一个LinkedList 持有Integer 对象。添加几个两位数后,我们将列表添加到外部列表中。
以下是这段代码中似乎让您感到困惑的关键点:
- 我们可以将这个
curList 变量声明为更通用的List < Integer > curList,而不是更具体的LinkedList < Integer > curList。用于curList 的List 的类型无关紧要。外部列表parentList 表示它不关心将哪个List 实现添加为元素,只关心添加的元素是some 种List,它包含Integer 对象。
- 我们可以决定如果外部列表是
LinkedList 而不是ArrayList,它会更好地服务。如果我们这样决定,我们可以简单地将List< List < Integer > > parentList = new ArrayList<>(); 更改为List< List < Integer > > parentList = new LinkedList<>();——其他代码都不会中断。我们可以将new ArrayList 切换为new LinkedList,而无需更改任何其他代码。这就是polymorphism 在OOP 中的好处。
/* package whatever; // don't place package name! */
import java.util.*;
import java.lang.*;
import java.io.*;
/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
public static void main (String[] args) throws java.lang.Exception
{
List< List < Integer > > parentList = new ArrayList<>();
parentList.add( List.of( 3 , 5 , 9 ) ) ; // `List.of` returns *some* kind of (unmodifiable ) `List`; we do not know the concrete class nor do we care.
LinkedList < Integer > curList = new LinkedList<>();
curList.add( 21 ) ;
curList.add( 42 ) ;
parentList.add(curList);
System.out.println( parentList ) ;
}
}
看到这个code run live on IdeOne.com。
[[3, 5, 9], [21, 42]]
在该输出中,我们看到一个集合,一个包含两个元素的List。每个元素本身就是一个List,一个Integer 元素的列表。外部列表的第一个元素包含对三个Integer 对象列表的引用,而外部列表的第二个元素包含对两个Integer 对象列表的引用。
你说:
我想我有点被甩了,因为我没想到你可以将 LinkedList 添加到声明为 List 的东西中。
某物 (parentList) 被声明为列表列表。您传递的LinkedList 是List,所以它适合,它是一个匹配类型。任何LinkedList 也是List。
如果您希望外部列表 (parentList) 仅包含 ArrayList 对象而不是任何类型的 List,您可以将其声明为:
List< ArrayList < Integer > > parentList = new ArrayList<>();
使用这样的声明,将new LinkedList<>() 传递给它的add 方法会导致编译器错误。方钉,圆孔。
但是,如果您的代码稍后需要 ArrayList 类独有的方法,而这些方法未在更通用的 List 接口中定义,则您只会进行声明。一般来说,你应该使用最通用的接口或满足你需要的超类,而不是更具体的具体类或子接口。如果List 有你以后的代码需要的方法,那么声明List< List < Integer > > parentList。这为您提供了灵活性。
你说:
我以为你必须做类似的事情
List<Integer>curList = new LinkedList<>();
parentList.add(curList);
当您将 LinkedList 对象分配给 List 变量时,您不会更改任何内容。 LinkedList 对象同时是 ListedList 和 List。
当您将curList 传递给parentList.add 方法时,该方法看到的只是List。将curList 声明为List 或LinkedList 与add 方法无关。编译器检查传递给 add 的任何内容是否是 (a) some 种 List 实现,any List 实现,以及 (b) 持有 @ 987654420@ 个对象。