泛型:
是一种把类型明确的工作推迟到创建对象或者调用方法的时候才去明确的特殊的类型。
参数化类型,把类型当作参数一样的传递。
集合中存在什么样的安全隐患
集合默认可以存储任意类型的对象。
当在存储String的集合中,存储一个Integer类型,调用String类型的特有方法就会报错,导致程序崩溃。
集合中泛型的使用
创建集合使用泛型指定集合只能存放的数据类型。(把运行时期的问题提前到了编译期间)
遍历集合时不需要进行类型转换。(避免了强制类型转换)
泛型的好处(优点)
增强了集合安全性,把运行时的错误转为编译时错误。
省去了类型强转的麻烦。
优化了程序设计,解决了黄色警告线
Java中的伪泛型
泛型只在编译时存在,编译后就被擦除,在编译之前我们就可以利用泛型限制集合存储对象的类型。
集合中使用泛型注意事项
在泛型中没有多态的概念,要不左右两边的数据类型要保持一致,要不只写一边。推荐两边都写一样的。
泛型不准使用基本数据类型,如果需要使用基本数据类型,要使用基本数据类型对应的包装类。
示例代码
public class Demo01 { public static void main(String[] args) { // 创建集合对象 ArrayList<String> list = new ArrayList<>(); list.add("aaa"); list.add("bbb"); list.add("ccc"); // list.add(1234); // list.add(new Integer(1234)); // 遍历集合 for(int index = 0 ;index < list.size(); index ++) { // Object str = list.get(index); // 向下转型 // String s = (String)str; String s = list.get(index); System.out.println(s.length()); } } }
泛型方法
泛型方法的引入
定义一个方法接收一个任意类型的参数,返回值类型与实际参数类型一致。
什么是泛型方法
泛型概述
泛型是JDK1.5新特性
泛型变量可以理解为是某种数据类型的占位符。
泛型变量还可以理解为是某种数据类型的变量。
泛型变量的命名规则:只要是合法的标识符就可以,一般使用一个大写字母表示,常用:T,K,V,E
泛型方法概念
在方式声明上使用了泛型变量的方法就是泛型方法
泛型方法的格式
修饰符 <泛型变量> 返回值类型 方法名(参数列表){} * public static <T> T test(T obj){}
泛型方法注意事项
* 泛型方法上泛型变量的具体数据类型是在调用方法时传入参数决定。 * 泛型方法的参数列表中如果没有使用到泛型变量,那么泛型变量的具体数据类型是由接收方法返回值数据类型决定。如果方法没有返回值,则默认是Object * 泛型方法上泛型变量一般都会使用在参数列表上。
示例代码
public class Demo02 { public static void main(String[] args) { int a; a = 100; Integer in = test(11); String str = test("aa"); } /** * 泛型方法 */ public static <T> T test(T obj) { return obj; } }
泛型类的定义和使用
泛型类的引入
* 定义一个数组工具类MyArrays * 提供两个成员方法,方法参数接收一个任意类型的数组 * 一个方法的功能是将数组的元素反转. * 一个方法的功能是将数组的元素拼接成一个字符串返回。 * 字符串格式:"[1,2,3,4,5]"
泛型类的概念
在类定义上使用了泛型变量的类
泛型类的定义格式
class 类名<泛型变量> { }
泛型类注意事项
泛型类上定义的泛型变量的具体数据类型是在创建该类对象时由使用者指定。
如果在创建泛型类对象时没有指定具体的数据类型,则默认是Object
静态方法不能使用类上定义的泛型变量,如果该方法要使用泛型变量,则要将该方法定义为泛型方法。
示例代码
/* * 泛型类 */ public class MyArraysTool<T>{ /** * 泛型方法 * 将数组的元素反转. */ public static <T> void reverse(T[] arr) { for(int i = 0,j = arr.length - 1; i < j;i++,j--) { T temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } /** * 将数组的元素拼接成一个字符串返回。 */ public String toString(T[] arr) { StringBuilder sb = new StringBuilder(); for(int index = 0;index < arr.length; index ++) { sb.append(arr[index]).append(", "); } return sb.toString(); } } /* * 测试类 */ public class Demo{ public static void main(String[] args) { // 整型数组 Integer[] arr = {1,2,3}; // 字符串数组 String[] strs = {"a","b","c"}; // 创建MyArraysTool对象 MyArraysTool<Integer> tool = new MyArraysTool<>(); tool.reverse(arr); MyArraysTool<String> tools = new MyArraysTool<>(); tools.reverse(strs); } }
查看官方泛型类ArrayList的定义和使用
// ArrayList泛型类的定义 class ArrayList<E>{ public boolean add(E e){ } public E get(int index){ } } // 创建对象时,确定泛型的类型 例如,ArrayList<String> list = new ArrayList<String>(); 此时,变量E的值就是String类型 class ArrayList<String>{ public boolean add(String e){ } public String get(int index){ } } 例如,ArrayList<Integer> list = new ArrayList<Integer>(); 此时,变量E的值就是Integer类型 class ArrayList<Integer>{ public boolean add(Integer e){ } public Integer get(int index){ } }
泛型接口
什么是泛型接口
在定义接口时使用了泛型变量的接口
泛型接口的定义格式
interface 接口名<泛型变量>{ }
如何实现泛型接口
实现接口的同时指定泛型变量的具体数据类型(不推荐)
实现接口时不指定泛型变量的具体数据类型,那么此时该实现类就要定义为泛型类。(推荐使用,比较灵活)。那么泛型变量的具体数据类型由类使用者在创建该类对象时指定。
泛型接口定义和实现演示
/** * 数据访问层 * 对数据库进行增删改查操作 * 姓名,性别 ===> Student stu ==> add(Stu) */ public interface Dao<T> { public void add(T t); public void delete(int id); public T find(int id); public void update(T t); } /** * 实现接口的同时指定泛型变量的具体数据类型 */ public class StudentDao implements Dao<Student>{ public void add(Student t) { } public void delete(int id) { } public Student find(int id) { return null; } public void update(Student t) { } } /** * 实现接口时不指定泛型变量的具体数据类型,那么此时该实现类就要定义为泛型类 */ public class BaseDao<T> implements Dao<T>{ public void add(T t) { } public void delete(int id) { } public T find(int id) { return null; } public void update(T t) { } }
查看官方泛型接口List的定义
/* * 带有泛型的接口 * * public interface List <E>{ * abstract boolean add(E e); * } * * 实现类先实现接口,不理会泛型 * public class ArrayList<E> implements List <E>{ * } * 调用者 : new ArrayList<String>() 后期创建集合对象的时候,指定数据类型
泛型上下限
泛型上下限引入
需求1 - 定义一个方法可以接收任意类型的集合对象 - 集合对象只能存储Integer或者是Integer的父类数据。 需求2 - 定义一个方法可以接收任意类型的集合对象 - 集合对象只能存储Number或者是Number的子类数据。
示例代码
public class Demo05{ public static void main(String[] args) { // 创建集合对象 ArrayList<Integer> list01 = new ArrayList<>(); ArrayList<String> list02 = new ArrayList<>(); ArrayList<Object> list03 = new ArrayList<>(); ArrayList<Number> list04 = new ArrayList<>(); ArrayList<Student> list05 = new ArrayList<>(); // Collections.sort(list05); test01(list01); //test01(list02); test01(list03); test01(list04); test02(list01); //test02(list02); //test02(list03); test02(list04); } /** - 定义一个方法可以接收任意类型的集合对象 - 集合对象只能存储Integer或者是Integer的父类数据。 */ public static void test01(ArrayList<? super Integer> list) { } /** 需求2 - 定义一个方法可以接收任意类型的集合对象 - 集合对象只能存储Number或者是Number的子类数据。 */ public static void test02(ArrayList<? extends Number> list) { } public static void test03(ArrayList<?> list) { } }
泛型上下限概述
泛型上下限的引入 需求1 - 定义一个方法可以接收任意类型的集合对象 - 集合对象只能存储Integer或者是Integer的父类数据。 需求2 - 定义一个方法可以接收任意类型的集合对象 - 集合对象只能存储Number或者是Number的子类数据。 泛型的上下限概述 ? 通配符,可以匹配任意的数据类型。 泛型的下限 * ? super Integer:表示能接受Integer及其父类类型的数据。 如果定义泛型T 则 T super Integer:表示T只能接受Integer及其父类类型的数据。 泛型上限 * ? extends Number:表示能够接受Number及其子类类型的数据。 如果定义泛型T 则 T extends Number:表示T只能够接受Number及其子类类型的数据。 通配符?的使用注意事项 * 一般不会单独使用,一般都会结合泛型上下限使用。 * 不能用来定义泛型方法,泛型类,泛型接口。
/* * 编写一个泛型方法求两个数之和.两个数可以传入 int、long、float、double 类型. * * 提示:泛型不能使用基本数据类型,需要使用引用数据类型.Integer,Long,Float,Double 都是 Number 的子类. * Number 有 doubleValue()方法 * */ public class Demo06 { public static void main(String[] args) { System.out.println(sum(8, 4.4)); } // ? extends Number public static <T extends Number> double sum(T a, T b) { return a.doubleValue() + b.doubleValue(); } }