java---泛型
泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。 Java语言引入泛型的好处是安全简单。
在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。
泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,以提高代码的重用率。
泛型的好处:
(1). 将运行时的异常提前至了编译时。
(2). 避免了无谓的强制类型转换 。
上边说的是理论,可能一时半会难理解,先看下面的一段代码:
1 public class Demo1 { 2 3 public static void main(String[] args) { 4 ArrayList list = new ArrayList(); 5 6 list.add("aa"); 7 list.add("bb"); 8 list.add("cc"); 9 10 list.add(123); 11 12 for(int i = 0 ; i < list.size() ; i++){ 13 String str = (String) list.get(i); 14 15 int j = (int)list.get(i); 16 17 System.out.println("大写:"+ str.toUpperCase()); 18 } 19 20 } 21 22 }
第4行创建了一个ArrayList,我们本来的目的是往list中添加字符串类型的元素,后续也要对这些字符串进行相应的处理,但是如果我们添加了一条int类型的数据,编译时也不会报错,但是运行时会报错。而且,这种定义方法每次接收都要进行类型强转,如第13行和15行,极其的不方便,那么引入泛型就可以解决这个问题了。同样是上边的代码,稍作修改引入泛型:
1 public class Demo1 { 2 3 public static void main(String[] args) { 4 ArrayList<String> list = new ArrayList<String>(); //<String> 表示该容器只能存储字符串类型 的数据。 5 6 list.add("aa"); 7 list.add("bb"); 8 list.add("cc"); 9 10 // list.add(123); 11 12 for(int i = 0 ; i < list.size() ; i++){ 13 String str = (String) list.get(i); 14 15 // int j = (int)list.get(i); 16 17 System.out.println("大写:"+ str.toUpperCase()); 18 } 19 20 } 21 22 }
第4行在定义list的时候就引入泛型,那么10行和15行已经被我注释掉了,因为这两行报错,不能定义或者接收String以外的类型。下面我们就分别讲一下泛型
1 泛型在集合中的应用
上边的两段代码就是在集合中应用的例子。
ArrayList<String> list = new ArrayList<String>(); true 推荐使用。
ArrayList<Object> list = new ArrayList<String>(); false
ArrayList<String> list = new ArrayList<Object>(); false
//以下两种写法主要是为了兼顾新老系统的兼用性问题。
*
ArrayList<String> list = new ArrayList(); true
ArrayList list = new ArrayList<String>(); true
注意: 泛型没有多态的概念,左右两边的数据 类型必须 要一致,或者只是写一边的泛型类型。
推荐使用: 两边都写泛型。
2 泛型在方法上的使用
1 修饰符 <声明自定义的泛型>返回值类型 函数名(使用自定义泛型 ...){ 2 3 }
在泛型中不能使用基本数据类型,如果需要使用基本数据类型,那么就使用基本数据类型对应的包装类型。
byte----> Byte
short---> Short
int----> Integer
long----> Long
double ----> Double
float -----> Float
boolean-----Boolean
char-------> Character
方法泛型注意的事项:
(1). 在方法上自定义泛型,这个自定义泛型的具体数据类型是在调用该 方法的时候传入实参时确定具体的数据类型的。
(2). 自定义泛型只要符合标识符 的命名规则即可, 但是自定义泛型我们一般都习惯使用一个大写字母表示。 T Type E Element
1 public class Demo2 { 2 3 public static void main(String[] args) { 4 String str = getData("abc"); 5 Integer i = getData(123); 6 } 7 8 9 public static <T>T getData(T o){ 10 11 return o; 12 } 13 }
3 泛型类
1 泛型类的定义格式: 2 3 class 类名<声明自定义泛型>{ 4 5 }
泛型类要注意的事项:
(1). 在类上自定义泛型的具体数据类型是在使用该类的时候创建对象时候确定的。
(2). 如果一个类在类上已经声明了自定义泛型,如果使用该类创建对象 的时候没有指定 泛型的具体数据类型,那么默认为Object类型
(3).在类上自定义泛型不能作用于静态的方法,如果静态的方法需要使用自定义泛型,那么需要在方法上自己声明使用。
1 class MyArrays<T>{ 2 3 //元素翻转 4 public void reverse(T[] arr){ 5 for(int startIndex = 0, endIndex = arr.length-1 ; startIndex<endIndex ; startIndex++,endIndex--){ 6 T temp = arr[startIndex]; 7 arr[startIndex] = arr[endIndex]; 8 arr[endIndex] = temp; 9 } 10 } 11 12 // 13 public String toString(T[] arr){ 14 StringBuilder sb = new StringBuilder(); 15 for(int i = 0 ; i < arr.length ; i++){ 16 if(i==0){ 17 sb.append("["+arr[i]+","); 18 }else if(i==arr.length-1){ 19 sb.append(arr[i]+"]"); 20 }else{ 21 sb.append(arr[i]+","); 22 } 23 } 24 return sb.toString(); 25 } 26 27 28 public static <T>void print(T[] t){ 29 30 } 31 32 33 } 34 35 public class Demo3 { 36 37 public static void main(String[] args) { 38 Integer[] arr = {10,12,14,19}; 39 40 MyArrays<Integer> tool = new MyArrays<Integer>(); 41 tool.reverse(arr); 42 System.out.println("数组的元素:"+tool.toString(arr)); 43 44 MyArrays<String> tool2 = new MyArrays<String>(); 45 String[] arr2 = {"aaa","bbb","ccc"}; 46 tool2.reverse(arr2); 47 48 49 ArrayList<String> list = new ArrayList<String>(); 50 51 } 52 }
注意,上边代码28行在泛型类MyArrays中定义了一个静态方法,这个静态方法也是泛型静态方法。由于泛型类中的泛型确定类型是在类定义对象时开始的,但是静态方法可以不通过对象调用,所以即使是泛型类中的静态方法,也要重新定义泛型,可以同名,相当于形参。
4 泛型接口
1 泛型接口的定义格式: 2 3 interface 接口名<声明自定义泛型>{ 4 5 } 6 7 接口调用格式: 8 9 public class 类名 implements 接口名<String>{ 10 11 } 12 13 如果要延长接口自定义泛型 的具体数据类型,那么格式如下: 14 15 public class 类名<T> implements 接口名<T>{ 16 17 }
正常调用泛型接口程序:
1 interface Dao<T>{ 2 3 public void add(T t); 4 } 5 6 public class Demo4 implements Dao<String> { 7 8 public static void main(String[] args) { 9 Demo4 d = new Demo4(); 10 } 11 12 public void add(String t){ 13 14 } 15 16 }
如果让接口自定义的泛型延长使用,代码如下:
interface Dao<T>{ public void add(T t); } public class Demo4<T> implements Dao<T> { public static void main(String[] args) { Demo4<String> d = new Demo4<String>(); } public void add(T t){ } }
5 泛型的上下限
先来看两个需求
需求1: 定义一个函数可以接收接收任意类型的集合对象, 要求接收的集合对象只能存储Integer或者是Integer的父类类型数据。
需求2: 定义一个函数可以接收接收任意类型的集合对象, 要求接收的集合对象只能存储Number或者是Number的子类类型数据。
代码如下:
1 public class Demo5 { 2 3 public static void main(String[] args) { 4 ArrayList<Integer> list1 = new ArrayList<Integer>(); 5 ArrayList<Number> list2 = new ArrayList<Number>(); 6 7 HashSet<String> set = new HashSet<String>(); 8 //getData(set); 9 10 } 11 12 //泛型的上限 13 public static void getData(Collection<? extends Number> c){ 14 15 16 } 17 18 //泛型的下限 19 public static void print(Collection<? super Integer> c){ 20 21 } 22 23 }
那么上限就指的是只能存储某个类及其子类,下限指的是只能存储某个类及其父类。
<? super Integer> : 只能存储Integer或者是Integer父类元素。 泛型 的下限
<? extends Number> : 只能存储Number或者是Number类型的子类数据。 泛型上限