### ArrayList 可变长度的数组。`ArrayList`内部就是一个`Object[]`数组,配合存储一个当前分配的长度,就可以充当“可变数组”。 ```java public class ArrayList{ private Object[] array; private int size; public void add(Object e){ ... } public void remove(int index){ ... } public Object get(int index){ ... } } ``` 如果用上述`ArrayList`存储`String`类型,会有这么几个缺点: - 需要强制转型; - 不方便,易出错。 ```java ArrayList list = new ArrayList(); list.add("hello"); // 获取到Object,必须强制转型为String String first = (String) list.get(0); ``` 容易出现ClassCastException,“误转型” ```java list.add(new Integer(123)); // ERROR: ClassCastException: String second = (String) list.get(1); ``` 要解决上述问题,我们可以为`String`单独编写一种`ArrayList` ```java public class StringArrayList{ private String[] array; private int size; public void add(String e){ ... } public void remove(int index){ ... } public String get(int index){ ... } } ``` 这样一来,存入的必须是`String`,取出的也一定是`String`,不需要强制转型,因为编译器会强制检查放入的类型。 ```java StringArrayList list = new StringArrayList(); list.add("Hello"); String first = list.get(0); //编译错误,不允许放入非String类型 list.add(new Integer(123)); ``` 新的问题是,如果要存储`Integer`,还需要为`Integer`单独编写一种`ArrayList`。还需要为其他所有class单独编写一种`ArrayList`。 为了解决新的问题,我们必须把`ArrayList`变成一种模板:`ArrayList`。 ```java public class ArrayList{ private T[] array; private int size; public void add(T e){ ... } public void remove(int index){ ... } public T get(int index){ ... } } ``` `T`可以是任何class。这样一来,我们就实现了:编写一次模版,可以创建任意类型的`ArrayList`。 ```java //创建可以存储String的ArrayList ArrayList strList = new ArrayList(); //创建可以存储Float的ArrayList ArrayList floatList = new ArrayList(); //创建可以存储Person的ArrayList ArrayList personList = new ArrayList(); ``` 因此,泛型就是定义一种模板,例如`ArrayList`,然后在代码中为用到的类创建对应的`ArrayList<类型>`。由编译器针对类型作检查。 ```java strList.add("Hello"); String s = strList.get(0); strList.add(new Integer(123)); //compile error Integer n = strList.get(0); //compile error ``` 这样一来,既实现了编写一次,万能匹配,又通过编译器保证了类型安全:这就是泛型。 ### 使用泛型 使用`ArrayList`时,如果不定义泛型类型时,泛型类型实际上就是`Object`。 ```java //编译器警告 List list = new ArrayList(); list.add("Hello"); list.add("World"); //要进行强制转型 String first = (String) list.get(0); String second = (String) list.get(1); ``` 此时,只能把``当作`Object`使用,没有发挥泛型的优势。 当我们定义泛型类型``后,`List`的泛型接口变为强类型`List` ```java //无编译器警告 List list = new ArrayList(); list.add("Hello"); list.add("World"); //无强制转型 String first = list.get(0); String second = list.get(1); ``` 当我们定义泛型类型``后,`List`的泛型接口变为强类型`List`。 ```java List list = new ArrayList(); list.add(new Integer(123)); list.add(new Double(12.34)); Number first = list.get(0); Number second = list.get(1); ``` 编译器如果能自动推断出泛型类型,就可以省略后面的泛型类型。编译器看到泛型类型`List`就可以自动推断出后面的`ArrayList`的泛型类型必须是`ArrayList`。 ```java List list = new ArrayList(); // List list = new ArrayList<>(); ``` ### 泛型接口 除了`ArrayList`使用了泛型,还可以在接口中使用泛型。例如,`Arrays.sort(Object[])`可以对任意数组进行排序,但待排序的元素必须实现`Comparable`这个泛型接口。 ```java public interface Comparable { /** * 返回负数: 当前实例比参数o小 * 返回0: 当前实例与参数o相等 * 返回正数: 当前实例比参数o大 */ int compareTo(T o); } ```