泛型
泛型
泛型:在定义类,接口,方法时使用类型形参,这个类型形参将在声明变量、创建对象、调用方法时动态地指定(即传入实际的类型参数,也可以称为类型实参)。
在定义的接口,类中声明形参,类型形参在整个接口,类里可当成类型使用。
List<String> l1 = new ArrayList<>();
List<String> l2 = new ArrayList<>();
l1.getClass() == l2.getClass(); //返回true 不存在泛型类 不管泛型的实际类型参数是什么,他们在运行的时候总是同样一个类
不管为泛型的类型形参传入哪一种类型实参,对于java来说,他们依然被当成同一个类处理,在内存中也只占一块内存空间,因此在静态方法、静态初始化块或者
静态变量的声明和初始化中不允许使用类型形参。
public class Car<T>
{
//下面代码错误 不能在静态变量声明中使用类型形参
static T info;
T age;
//下面代码错误 不能再静态方法声明中使用类型形参
public static void getCarBrand(T msg){}
}
类型通配符
为了表示各种泛型List的父类,可以使用类型通配符,类型通配符是一个问号(?),将一个问号作为类型实参传给List集合,写作:List<?>。这个问号被称为通配符,表示它的元素类型可以匹配任何类型。
设定类型通配符的上限
1 public class Apple<T extends Number> { 2 T price; 3 4 public static void main(String[] args){ 5 Apple<Integer> ai = new Apple<>(); 6 Apple<Double> ad = new Apple<>(); 7 8 //此处编译异常 此处将String传给T形参,但是String不是Number的子类型,会引起编译错误 9 Apple<String> as = new Apple<>(); 10 } 11 }
使用类型通配符设定类上限,传入的参数类型必须为上限类型或者上限类型的子类。
Integer和Double类型都必须为Number类型的子类。
泛型方法
语法格式:
修饰符 <T, S> 返回类型 方法名(方法参数){ //方法体 }
1 public class TestError { 2 //定义一个将任意类型数组值复制到集合中的方法 3 public static <T> void formArrayToCollection(T[] arry, Collection<T> c) { 4 for (T t : arry) { 5 c.add(t); 6 } 7 } 8 9 public static void main(String[] args) { 10 String[] arr = {"Bob", "Jack", "Marry"}; 11 List<String> list = Lists.newArrayList(); 12 formArrayToCollection(arr, list); 13 list.forEach(System.out::println); 14 } 15 }
设定类型通配符的下限
1 public class TestError { 2 3 public static <T> void formArrayToCollection(T[] arry, Collection<T> c) { 4 for (T t : arry) { 5 c.add(t); 6 } 7 } 8 //将一个集合中的元素复制到另外一个集合里面,并返回集合最后一个元素的值 9 public static <T> T copyType(Collection<T> des, Collection<? extends T> src){ 10 T last = null; 11 /*src的类型必须为类型T或者类型T的子类,返回最后一个值得类型为T类型。 12 * src集合里面的元素类型可能是T类型或者T类型的子类, 13 */ 14 for (T t : src){ 15 des.add(t); 16 last = t; 17 } 18 return last; 19 } 20 21 public static void main(String[] args) { 22 List<Integer> src = Lists.newArrayList(); 23 for (int i = 0; i< 10; i++){ 24 src.add(i); 25 } 26 List<Number> des = Lists.newArrayList(); 27 //此处得到的是Number类型,然而这个方法明显返回的类型应该是Integer类型,丢失了集合元素值得类型 28 Number last = copyType(des, src); 29 //Integer last = copyType(des, src);此处会报错 30 System.out.println(last); 31 des.forEach(System.out::println); 32 } 33 }
采用上面这个方法丢失了集合元素的类型。
改进:设定通配符的下限。Java中使用<? super Type>来设定通配符的下限,这个通配符表示元素类型必须为Type本身或者Type的父类。于是采用通配符的下限来改写上述的Copy方法;
1 public class TestError { 2 3 public static <T> void formArrayToCollection(T[] arry, Collection<T> c) { 4 for (T t : arry) { 5 c.add(t); 6 } 7 } 8 9 public static <T> T copyType(Collection<? super T> des, Collection<T> src){ 10 T last = null; 11 /*src的类型必须为类型T或者类型T的子类,返回最后一个值得类型为T类型。 12 * src集合里面的元素类型可能是T类型或者T类型的子类, 13 */ 14 for (T t : src){ 15 des.add(t); 16 last = t; 17 } 18 return last; 19 } 20 21 public static void main(String[] args) { 22 List<Integer> src = Lists.newArrayList(); 23 for (int i = 0; i< 10; i++){ 24 src.add(i); 25 } 26 List<Number> des = Lists.newArrayList(); 27 //此处得到的是Number类型,然而这个方法明显返回的类型应该是Integer类型 28 //Number last = copyType(des, src); 29 // 此处可以使用Integer类型 30 Integer last = copyType(des, src); 31 System.out.println(last); 32 des.forEach(System.out::println); 33 } 34 }
使用上述这个方法可以使得copyType()方法推断出最后一个被赋值的元素类型是Integer类型,而不是笼统的Number类型。