1. 泛型
1.1 什么是范型
范型,就是允许在定义类,接口时通过一个标识表示类中某个属性的类型或者某个方法的返回值及参数类型。这个类型参数在使用时确定,也就是类型的参数化。
1.2 自定义范型
1.2.1 范型类和接口
在定义类的时候使用泛型参数,使用方法如下:
public class Factory<T>{ private T things; public Factory(T things){ this.things = things; } public T getThings(){ return things; } }
有时候,子类会出现继承一个泛型父类的情况,这种情况下,有两种情况:
- 给父类泛型传入参数,子类不带泛型参数。
public class SubFactory extends Factory<String>{ }
- 子类含有泛型参数,且把子类的泛型参数当作一个类型参数传入到父类当中。
public class SubFactory<T> extends Factory<T>{ }
当定义的类中含有多个泛型参数时,多个泛型参数使用逗号隔开。通常使用 T、E、K、V 当作泛型参数的名称。
如果一个类含有泛型参数,那么传入的泛型参数不同,其类就不一样,如:G<A>
和 G<B>
不是同一个类,G<A>
引用不能引用 G<B>
对象 。
如果 A 是 B 的父类,那么 G<A>
和 G<B>
不具有父子类关系 ,但 A<G>
和 B<G>
具有父子关系。
基本类型不能当作泛型的参数传入,只能传入对应得包装类。
泛型参数不能直接通过 new 创建对象,如果需要通过泛型参数创建对象,可以使用如下得方法:
T t = (T)new Object();
静态方法中无法使用泛型参数,原因时类的泛型参数只有在创建对象时才能够确定,而静态方法是属于类的。
定义泛型接口和定义泛型类方式差不多,只不过是从定义类转到了定义接口。
public interface Factory<T>{ T produce(); }
泛型类和泛型接口的使用方法很简单,参考代码如下:
Factory<String> f = new Factory<>();
如果定义的类中有泛型参数,但在使用的时候没有传入参数,那么泛型参数擦除,一般情况下类型为 Object 。
1.2.2 范型方法
方法中使用了类中定义的泛型参数不能称为泛型方法,下面的就不是泛型方法:
// 泛型类 public class Factory<T>{ private T things; // 不是泛型方法 public Factory(T things){ this.things = things; } // 不是泛型方法 public T getThings(){ return things; } }
泛型方法的声明形式如下:
// 泛型类 public class Copy<T>{ public <E> E[] copyArray(E[] src){ } }
泛型方法使用方式如下:
Copy c = new Copy(); String[] src = {"Hello", "world"}; String[] dest = c.copyArray(src);
泛型方法在使用时,不需要显示的传入泛型的参数,而是会根据传入的方法参数类型自动的推断出泛型的参数类型,所有泛型方法是可以使用 static 修饰的,下面的情况也是正确的。
// 泛型类 public class Copy<T>{ // 泛型方法 public static <E> E[] copyArray(E[] src){ } }
1.3 通配符的使用
public class Test{ public void showList1(List<Object> list){} public void showList2(List<String> list){} }
showList
方法的功能是用来变量 List
。但是由于 List<Object>
和 List<String>
不是同一类型,所有无法做到一个方法遍历这两种类型的。为了解决这个问题,就需要使用通配符。
通配符的符号为:?
,把 ?
代替具体的泛型参数传入到 List 类中,那么其 List<?>
定义的变量可以引用泛型的 List,相当于 带泛型参数的 List 的父类,所以上面的代码可以改成如下:
public class Test{ public static void showList(List<?> list){ Iterator<?> iterator = list.iterator(); while(iterator.hasNext()){ Object obj = iterator.next(); } } public static void main(String[] args){ List<Object> list1 = new ArrayList<>(); List<String> list2 = new ArrayList<>(); showList(list1); showList(list2); } }
当使用通配符 ?
当作泛型参数传入时,只能读取对象的参数,但无法往里面添加参数唯一能添加的只有 null
,如:
List<?> list = new ArrayList<>(); Object obj = list.get(0); // 编译无法通过 list.add("AA");
1.3.1 限制条件的通配符使用
定义了两个类,分别为 Person 类和 Student 类,其中 Student 类继承 Person 类。? extends Person
的含义是 ?
最大的类型为 Person。? super Person
表示 ?
最小的类型为 Person,最大为 Object。下面通过代码来对比一下:
List<? extends Person> list1 = new ArrayList<>(); List<? super Person> list2 = new ArrayList<>(); List<Person> list3 = new ArrayList<>(); List<Student> list4 = new ArrayList<>(); List<Object> list5 = new ArrayList<>(); list1 = list3; list1 = list4; // 编译有问题 list1 = list5; list2 = list3; // 编译有问题 list2 = list4; list2 = list5; // 子类可以自动转变为父类,但父类不能自动转变为子类 Person p = list1.get(0); // 编译有问题 list1.add(new Person()); Object obj = list2.get(0); list2.add(new Person()); list2.add(new Student());