泛型入门
使用泛型
Java5之后,Java引入了“参数化类型”的概念,允许程序在创建集合时指定元素的类型。
importjava.util.ArrayList;importjava.util.List;public classa{public static voidmain(String[] args){
List strList = new ArrayList();
strList.add("书本1");
strList.add("书本222");//下面代码将引起编译错误//strList.add(5);//不需要进行强制类型转换//strList.forEach(str -> System.out.println((String)str.length()));
strList.forEach(str ->System.out.println(str.length()));
}
}
Java9增强的"菱形"语法
import java.util.*;public classa{public static voidmain(String[] args){//Java自动推断ArrayList的<>应该是String
List strList = new ArrayList<>();//Java自动推断HashMap的<>应该是String,List
Map> schoolsInfo = new HashMap<>();
}
}
Java9再次增强了"菱形"语法,它甚至允许在创建匿名内部类时使用菱形语法,Java可根据上下文来推断匿名内部类中泛型的类型
import java.util.*;interface Foo{voidtest(T t);
}public classa{public static voidmain(String[] args){//指定Foo类中泛型为String
Foo f = new Foo<>(){public voidtest(String t){
System.out.println("test方法的参数t为:" +t);
}
};//使用泛型通配符,此时相当于通配符的上限为Object
Foo> fo = new Foo<>(){//test()方法的参数类型为Object
public voidtest(Object t){
System.out.println("test方法的Object参数为:" +t);
}
};//使用泛型通配符,通配符的上限为Number
Foo extends Number> foo = new Foo<>(){//test()方法的参数类型为Number
public voidtest(Number t){
System.out.println("test方法的Number参数为:" +t);
}
};
}
}
深入泛型
定义泛型接口、类
所谓泛型,就是允许在定义类,接口时使用类型形参,这个类型形参(或叫泛型)将在声明变量、创建对象、调用方法时动态地指定(即传入实际的类型实参)。
下面是Java5改写后的List接口、Iterator接口、Map接口的代码片段
import java.util.*;public interface List{//在该接口里,E可作为类型使用//下面方法可以使用E作为参数类型
voidadd(E x);
Iteratoriterator();
...
}public interface Iterator{
E next();
...
}public interface Map{
SetkeySet();
V put(K key,V value);
...
}
包含泛型声明的类型可以在定义变量、创建对象时传入一个类型实参,从而可以动态地生成无数多个逻辑上的子类,但这种子类在物理上并不存在。
可以为任何类、接口增加泛型声明(集合类是泛型的重要使用场所):
public class Apple{privateT info;publicApple(){}publicApple(T info){this.info =info;
}public voidSetInfo(T info){this.info =info;
}publicT getInfo(){return this.info;
}public static voidmain(String[] args){//由于传给T形参的是String,所以构造器参数只能是String
Apple a1 = new Apple<>("苹果");
System.out.println(a1.getInfo());//由于传给T形参的是Double,所以构造器参数只能是Double或double
Apple a2 = new Apple<>(5.2);
System.out.println(a2.getInfo());
}
}
从泛型类继承派生子类
当创建了带泛型声明的接口、父类之后,可以为该接口创建实现类,或从该父类派生子类,需要指出的是,当使用这些接口、父类时不能再包含泛型形参。例如,下面的代码就是错误的。
//定义类A继承Apple类,Aplle类不能跟泛型形参
public class A extends Apple{}
如果想从Apple类派生一个子类,则可以改为如下代码:
//使用Apple类时T形参传入String类型
public class A extends Apple
调用方法时必须为所有的数据形参传入参数值,与调用方法不同的是,使用类、接口时也可以不为泛型形参传入实际的类型参数:
//当使用Apple类时,没有为T形参传入实际的类型参数
public class A extends Apple
像这种省略Apple类时省略泛型的形式本称为原始类型(raw type)。编译器会发出警告,此时,系统会把Apple类里的T形参当成Object类型处理。
public class A1 extendsApple{publicString getInfo(){//super.getInfo()方法返回值是Object类型//所以加toString()才返回String类型
return super.getInfo().toString();
}
}
public class A1 extends Apple{//正确重写了父类的方法,返回值
publicString getInfo(){return "子类" + super.getInfo();
}//下面方法是错误的,重写父类方法时返回值类型不一致
publicObject getInfo(){return "子类";
}
}
并不存在泛型类
不管为泛型形参传入哪一种类型实参,对于Java来说,它们依然被当成同一个类处理,在内存里也只占用一块内存空间,因此在静态方法、静态初始化块或静态变量的声明和初始化中不允许使用类上定义的泛型形参。
//下面两条代码错误
staticT info;public static void bar(T mas){}
由于系统并不会真正生成泛型类,所以instanceof运算符后不能使用泛型类。
java.util.Collection cs = new java.util.Collection<>();//下面代码错误
if(cs instanceof java.util.ArrayList){...}