文章目录
- 一、泛型概述
- 二、泛型的定义
- 2.1 泛型类
- 2.2 泛型方法
- 2.3 泛型接口
- 三、泛型深入
- 3.1 泛型通配符
- 3.2 泛型上下限
- 3.3 案例:定义一个 “所有车量进行比赛” 的方法
- 四、可变参数
一、泛型概述
泛型是 JDK5 中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查。
格式:<数据类型>;
好处:统一数据类型。把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常,因为编译阶段类型就能确定下来。
注意:泛型只能支持引用数据类型。集合体系的全部接口和实现类都是支持泛型的使用的。
二、泛型的定义
泛型可以在很多地方进行定义:
- 类后面 → 泛型类
- 方法申明上 → 泛型方法
- 接口后面 → 泛型接口
2.1 泛型类
定义类时同时定义了泛型的类就是泛型类。
格式:修饰符 class 类名<泛型变量> { ... }
作用:编译阶段可以指定数据类型,类似于集合的作用。
注:此处泛型变量 T 可以随便写为任意标识,常见的如E、T、K、V等。
public class MyArrayList<T> {
private ArrayList<T> list = new ArrayList<>();
public void add(T t){
list.add(t);
}
public void remove(T t){
list.remove(t);
}
}
2.2 泛型方法
定义方法时同时定义了泛型的方法就是泛型方法。
格式:修饰符 <泛型变量> 方法返回值 方法名称(形参列表){...}
作用:方法中可以使用泛型接收一切实际类型的参数,方法更具备通用性。
public static <T> void printArray(T[] arr){
if(arr != null){
StringBuilder builder = new StringBuilder("[");
for (int i = 0; i < arr.length; i++) {
builder.append(arr[i]).append(i == arr.length - 1 ? "" : ", ");
}
builder.append("]");
System.out.println(builder);
}else {
System.out.println(arr);
}
}
2.3 泛型接口
使用了泛型定义的接口就是泛型接口。
格式:修饰符 interface 接口名称<泛型变量>{...}
作用:泛型接口可以约束实现类,泛型接口可以让实现类选择当前功能需要操作的数据类型。
原理:实现类可以在实现接口的时候传入自己操作的数据类型这样重写的方法都将是针对于该类型的操作。
案例:教务系统,提供一个接口可约束一定要完成数据(学生,老师)的增删改查操作。
public interface Data<E> {
void add(E e);
void delete(int id);
void update(E e);
E queryById(int id);
}
public class Student {
...
}
public class StudentData implements Data<Student>{
@Override
public void add(Student student) {
}
@Override
public void delete(int id) {
}
@Override
public void update(Student student) {
}
@Override
public Student queryById(int id) {
return null;
}
}
public class Teacher {
...
}
public class TeacherData implements Data<Teacher>{
@Override
public void add(Teacher teacher) {
}
@Override
public void delete(int id) {
}
@Override
public void update(Teacher teacher) {
}
@Override
public Teacher queryById(int id) {
return null;
}
}
三、泛型深入
3.1 泛型通配符
通配符:?,其可以在 使用泛型 的时候代表一切类型。
注:E T K V 是在 定义泛型 的时候使用的。
3.2 泛型上下限
泛型上限:? extends Car
,? 必须是 Car 或者其子类。
泛型下限:? super Car
,? 必须是 Car 或者其父类。
3.3 案例:定义一个 “所有车量进行比赛” 的方法
需求:定义一个 “所有车进行比赛” 的方法,该方法参数只可接收汽车类型的集合,其中汽车的父类和子类如下:
class Car{
}
class BENZ extends Car{
}
class BMW extends Car{
}
试想 1:如果参数为泛型方式,则任何类的集合都可作为参数,这是错误的。
public static void go(ArrayList<T> cars){
}
试想 2:如果设置参数为父类类型,也是会存在问题:虽然 BMW 和 BENZ 都继承了 Car 但是 ArrayList<BMW>、ArrayList<BENZ>、 ArrayList<Car>
没有关系的。
public static void go(ArrayList<Car> cars){
}
因此,可采用“泛型上限” 的方式解决此问题。客户端模拟代码如下:
public class GenericDemo {
public static void main(String[] args) {
ArrayList<BMW> bmws = new ArrayList<>();
bmws.add(new BMW());
bmws.add(new BMW());
bmws.add(new BMW());
go(bmws);
ArrayList<BENZ> benz = new ArrayList<>();
benz.add(new BENZ());
benz.add(new BENZ());
benz.add(new BENZ());
go(benz);
}
// 所有车比赛
public static void go(ArrayList<? extends Car> cars){
}
}
四、可变参数
可变参数用在形参中可以接收多个数据。
格式:数据类型...参数名称
作用:接收参数非常灵活方便。可以不接收参数,可以接收一个或者多个参数,也可以接收一个数组。
可变参数在方法内部本质上就是一个数组。
注意:一个形参列表中可变参数只能有一个;可变参数必须放在形参列表的最后面。
举例:假如需要定义一个方法求和,求和数字个数不确定。
public static void main(String[] args) {
System.out.println(getAll()); // 0
System.out.println(getAll(10)); // 10
System.out.println(getAll(10, 20, 30)); // 60
System.out.println(getAll(new int[]{10, 20, 30, 40, 50})); // 150
}
/**
注意:一个形参列表中只能有一个可变参数,可变参数必须放在形参列表的最后面
*/
public static int getAll(int... nums){
int co = 0;
for (int i = 0; i < nums.length; i++) {
co += nums[i];
}
return co;
}
文章参考:Java入门基础视频教程,java零基础自学就选黑马程序员Java入门教程(含Java项目和Java真题)