泛型方法
修饰符 <类型变量,类型变量,...> 返回值类型 方法名(形参列表){
}
public static <T> void test(T t){ }
注意:下面这种不是泛型方法
1. public E get(int index){ 2. return (E) arr[index]; 3. }
具体体现:
public class Test{ public static void main(String[] args){ test(new Dog()); //假设传入了狗对象 } //泛型方法 public static <T> T test(T t){ //返回值类型也为T类型 return t; } }
泛型的通配符
来看一个需求:所有汽车一起参加比赛,需要汽车类、具体的汽车品牌类,然后设计方法只能让汽车一起比赛。
现在假设已经定义了Car、BMW、BENZ类。
第一种情况:(定义总体的汽车集合,将各种汽车都放进这个集合里面)
public class Test{ public static void main(String[] args){ ArrayList<Car> cars = new ArrayList<>(); //创建一个汽车集合 //我们可以将一辆奔驰和宝马放进这个汽车集合里面 cars.add(new BMW()); cars.add(new BENZ()); //然后调用比赛的函数,没有什么问题 go(cars); } public static void go(ArrayList<Car> cars){ } }
第二种情况:(定义不同的具体汽车集合,例如宝马车为一个集合,奔驰车为一个集合)
public class Test{ public static void main(String[] args){ ArrayList<Car> cars = new ArrayList<>(); //创建一个汽车集合 ArrayList<BMW> bmws = new ArrayList<>(); //创建宝马车集合 bmws.add(new BMW()); bmws.add(new BMW()); //加入两辆宝马进入这个集合 ArrayList<BENZ> benzs = new ArrayList<>(); //创建奔驰车集合 benzs.add(new BENZ()); benzs.add(new BENZ()); //同样加入两辆奔驰进入这个集合 //此时调用比赛方法 go(bmws); go(benzs); //会发现报错,无法调用,数据类型不一样 } public static void go(ArrayList<Car> cars){ } }
此时的ArrayList<BMW>和ArrayList<BENZ>其实和ArrayList<Car>已经没有关系了,或者说不算作是同一个数据类型了,所以无法调用传参为ArrayList<Car>的go方法。
所以就需要用到泛型方法了
对go方法进行修改:
public static <T> void go(ArrayList<T> cars){ }
但这个还是存在问题的,比如这个方法不仅宝马、奔驰对象可以加入,其他一些对象,比如狗(Dog)对象也可以加入;这就不符合我们的需求了。所以再稍加修改:
public static <T extends Car> void go(ArrayList<T> cars){ }
这就是我们限定了T能接受的类型,它只能是Car类或者是Car的子类;Dog类和其他不是Car的子类的就会被踢出去了 。
这样其实就可以满足这个需求了。
接着我们就可以引入我们的通配符知识点了,
我们看这里ArrayList<T>它自己就是一个泛型类,而前面又再定义了一个泛型方法,这是没有没有必要的,Java中提供了一种方案,让我们可以解决这个问题,也就是通配符:
? 通配符,在使用泛型的时候可以代表一切类型
public static void go(ArrayList<? extends Car> cars){ }
我们在定义泛型的时候一般用E、T、K、V......但是我们使用泛型的时候,就可以用“?”来表示一切类型,这里“? extends Car”的意思就是:可以传入一切是Car或者Car子类的类型。
这种写法称为上限技术,顾名思义Car类就是它最高可以传入的类型了;与其相反的就是下限技术:(用得稍微少一点)
public static void go(ArrayList<? super Car> cars){ }
也就是最低也要传入Car类,往上就是Car的父类。
小结
通配符
- 就是“?”,可以在“使用泛型”的时候代表一切类型;E T K V是在定义泛型的时候使用。
泛型的上下限
- 泛型上限:? extends Car :?能接收的必须是Car或者其子类。
- 泛型下限:? super Car:?能接收的必须是Car或者其父类。
泛型擦除和注意事项
泛型是工作在编译阶段的,一旦程序编译成class文件,class文件中就不存在泛型了,这就是泛型擦除。
泛型不支持基本数据类型,只能支持对象类型(引用数据类型)。
关于泛型擦除,我们要使用Java的反编译工具XJad来查看,
我们自己编写的代码在编译成class文件之后,泛型的数据类型都恢复成Object的了,底层会自己强转类型。也就是擦除了泛型。
第二点,泛型不支持基本数据类型,比如int、double,都是不支持的。
如果你非要使用基本数据类型,可以使用int类型的对象类型 Integer ,以及double类型的对象类型Double,例如:
public class Test{ public static void main(String[] args){ //ArrayList<int> list1 = new ArrayList<>(); //ArrayList<double> list1 = new ArrayList<>(); //都会报错 //使用他们的对象类型就不会报错了 ArrayList<Integer> list1 = new ArrayList<>(); ArrayList<Double> list1 = new ArrayList<>(); } }
关于这些对象类型,接下来的文章会再详细了解。
至此,面向对象最核心的语法知识就全部学完了,接下来Java的学习,就是拿着这些语法知识去学习一个一个的API,API掌握得越多,Java的编程能力相对来说也就更强了,关于API的学习,前面我们学习过一些,接下来将更深入、更多地去学习。
END