0. 把一个对象"丢进"集合后, 集合会"忘记"这个对象的数据类型, 当再次取出该对象时, 该对象的编译类型就变成了Object类型(运行时类型不变), 因此取出元素后通常需要进行强制类型转换.
1. java引入了"参数化类型(parameterized type)"的概念, 允许创建集合时指定集合元素的类型(如List<String>, 表名List只能保存字符串类型的对象). Java的参数化类型叫泛型(Generic).
2. 泛型用法: 在集合接口、类后增加尖括号, 尖括号里放一个shujuleixing,即表明这个集合接口、集合类只能保存特定类型的对象.
-可以为任何类、接口增加泛型声明(并不只有集合类才可以使用泛型声明, 虽然集合类的泛型重要使用场所).
3. Java7开始, 允许用List<String> strList = new ArrayList<>(); 形式代替 List<String> strList = new ArrayList<String>(); 形式, 即可以省略构造器后菱形中的类型, 系统会自己判断.
4. 所谓泛型, 就是允许在定义类、接口、方法时使用类型形参, 这个类型形参将在声明变量、创建对象、调用方法时动态的指定(即传入实际的类型参数, 也可称为类型实参).
5. 不管为泛型的类型形参传入哪一种类型实参, 它们依然会被当成同一个类处理, 在内存中也只占用一块内存空间, ? 因此在静态方法、静态初始化快或者静态变量的声明和初始化中不允许使用类型形参.
6. 由于系统中并不会真正生成泛型类, 所以instanceof运算符后不能使用泛型类.
7. 不能把一个List<String>类型参数传给test(List<Object> c). 因为List<String>对象不能被当成List<Object>对象使用, 也就是说, List<String>类不是List<Object>类的子类.
8. Java泛型的设计原则是, 只要编译时没出现警告, 运行时就不会ClassCastExcetion异常.
9. 为了表示各种泛型List的父类, 可以使用List<?>(意思是未知类型元素的List). 这个问号被称为"通配符". 但是下面这样会编译错误:
List<?> c = new ArrayList<String>(); //下面出错, 因为不知道c集合里元素的类型, 所以不能添加. c.add(new Object());
10. 还可以使用形如List<? extends Shapes> 的形式, 表示List中的元素的数据类型只能是Shapes类或其子类, 因此把这个Shapes叫做通配符的上限(upper bound).
- 同List<?>一样, 不能把任何对象添加到List<? extends Shapes>集合中(因为不知道这集合里是什么类型).
11. 同样, 也可以这么来:
class Apple<T extends Number>{}在定义类型形参时设定上限, 限定传来的实参类型, 必须是上限类型或上限类型的子类.
12. 还可以这么弄(neng):
class Apple<T extends Number & java.io.Serializable){}可以为类型形参设定多个上限(父类上限最多有一个, 接口上限随意), 表示该类型形参必须是其上限类型或其上限类型的子类, 并且实现所有上限接口.
- 与类同时继承父类、实现接口类似, 为类型形参指定多个上限时, 所有的接口上限必须位于类上限之后.
13. 泛型方法(Generic Method), 就是在声明方法是定义一个或多个类型形参. 用法:
static <T> void fromArrayToCollection(T[] a, Collection<T> c) { for(T o:a) { c.add(o); } }例子:
import java.util.*; public class GenericMethodTest { static <T> void fromArrayToCollection(T[] a, Collection<T> c) { for (T o:a) { c.add(o); } } public static void main(String[] args) { Object[] oa = new Object[100]; Collection<Object> co = new ArrayList<>(); fromArrayToCollection(oa, co); String[] sa = new String[100]; Collection<String> cs = new ArrayList<>(); fromArrayToCollection(sa, cs); // Collection<T> c对应cs, 所以泛型T代表String(即cs对应的类型), fromArrayToCollection(sa, co); // Collection<T> c对应co, 所以泛型T代表Oject(即co对应的类型), sa对应String, String自动转换成Object(所以可以赋给形参T[] a), 所以可以这么写. } }
14. 当然还可以这么弄(neng):
static <T> void test(Collection<? extends T> from, Collection<T> to){}
15.
public interface Collection<E> { boolean containsAll(Collection<?> c); boolean addAll(Collection<? extends E> c); }和下边等价的,
public interface Collection<E> { boolean <T> containsAll(Collection<T> c); boolean <T extends E> addAll(Collection<T> c); }重点来了. 通常, 泛型方法用来表示方法的多个参数之间, 或方法返回值与参数之间的类型依赖关系, 如果没有这样的类型依赖关系, 用类型通配符好看.
16. 还可以这样,
public class Collections { public static <T> void copy(List<T> dest, List<? extends T> src) {...} ... }等价于下面这样,
public class Collections { public static <T, S extends T> void copy(List<T> dest, List<S> src){...} ... }
17. "菱形语法"和泛型构造器. 使用菱形语法可以省略尖括号中的内容, 但是如果程序显式地指定了泛型构造器中声明的类型形参的实际类型, 则不可以使用"菱形"语法.
class MyClass<E> { public <T> MyClass(T t) { System.out.println("Parameter t is: " + t); } } public class GenericDiamondTest { public static void main(String[] args) { MyClass<String> mc1 = new MyClass<>(5); MyClass<String> mc2 = new <Integer> MyClass<String>(5); MyClass<String> mc3 = new <Integer> MyClass<>(5); // #1 这句报错 } }#1句代码既指定了泛型构造器中的类型形参是Integer类型, 又想使用"菱形"语法, 所以这行代码无法通过编译. -----> ? 为毛啊.
18. 除了上限通配符<? extends Type>, 伟大的Java还支持下限通配符<? super Type>, 表示必须是Type本身或其父类.
19. 允许在使用带泛型声明的类时不指定是记得类型参数, 此时它的类型参数称为原始类型(raw type), 默认是声明该参数时指定的第一个上限类型. 所以当把一个具有泛型信息的对象赋给另一个没有泛型信息的变量时, 所有的尖括号之间的类型信息都将被扔掉(像List<String>类型被转换成List那样).
20. 可以直接把List对象赋给List<String>对象, 编译不会出错(仅提示"使用了未经检查或不安全的操作").
21. 因为Java泛型设计有个重要原则, "如果一段代码在编译时没有提出"[unchecked] 未经检查的转换"警告, 则程序在运行时不会发生ClassCastExecption异常", 所以:数组元素的类型不能包含类型变量或类型形参(除非是无上限的类型通配符), 但是可以声明元素类型包含类型变量或类型形参的数组. 也就是说, 只能声明List<String>[]形式的数组, 但不能创建ArrayList<String>[10]这样的数组对象. 如果有像下面这样的语句可能编译时不提示, 运行就出错:
List<String>[] lsa = new List<String>[10];- -. 这么说看着"警告"满屏警告还挺好的了~~
22. 创建元素类型是类型变量的数组对象也将导致编译错误. (因为类型变量在运行时并不存在, 所以编译器无法确定实际类型是什么.)