泛型的理解与使用
泛型概述
对集合有所了解的童鞋们都知道,集合中是可以存放任意对象的,只要把对象存储集合后,那么这时他们都会被提升成Object类型。当我们再取出每一个对象,并且进行相应的操作,这时必须采用类型转换。我们通过代码来分析一下
public class GenericDemo {
public static void main(String[] args) {
Collection coll = new ArrayList();
coll.add("abc");
coll.add("itcast");
coll.add(5);//由于集合没有做任何限定,任何类型都可以给其中存放
Iterator it = coll.iterator();
while(it.hasNext()){
//需要打印每个字符串的长度,就要把迭代出来的对象转成String类型
String str = (String) it.next();
System.out.println(str.length());
}
}
}
程序在运行时发生了问题java.lang.ClassCastException。
为什么会发生类型转换异常呢?
我们来分析下:由于集合中什么类型的元素都可以存储。导致取出时类型强转引发运行时 ClassCastException。
泛型:可以在类或方法中预支地使用未知的类型,一般在创建对象时,将未知的类型确定具体的类型。当没有指定泛型时,默认类型为Object类型。其主要原理就是在类声明时通过一个标识标识雷总某个属性的类型或者是某个方法的返回值及参数类型。这样在类声明或者实例化时只要指定好需要的类型即可。
概念理解有点生涩隐晦,下面通过具体的实例来看看吧。
一、泛型类和对象的定义
1. 泛型类和对象格式
1.1 泛型类定义格式
class 类<泛型类型>{
修饰符 泛型类型 属性;
public 泛型类型 getter(){
return 属性
}
public void setter(泛型类型 变量){
this.mvp
}
}
1.2 泛型对象定义格式
类名称<具体类>对象名称 = new 类名称<具体类>();
2. 代码演示
//T表示此类型是有外部调用本类时指定的
public class Box<T> {
//var的类型由T指定,也就是外部指定
//没有T类型,在这里代表未知的一种数据类型 未来传递什么就是什么类型
private T var;
//返回值的类型由外部指定
public T get() {
return var;
}
//设置的参数类型由外部指定
public void add(T var) {
this.var = var;
}
//测试泛型类
public static void main(String[] args) {
//里面的var类型为Integer,就是创建一个泛型为Integer的类
Box<Integer> integerBox = new Box<Integer>();
//里面的var类型为String,就是创建一个泛型为String的类
Box<String> stringBox = new Box<String>();
//调用add方法,自动装箱
integerBox.add(new Integer(10)); //设置数字
stringBox.add(new String("菜鸟教程")); //设置字符串
System.out.printf("整型值为 :%d\n\n", integerBox.get());
System.out.printf("字符串为 :%s\n", stringBox.get());
}
}
二、含有泛型的方法
2.1 格式:修饰符 <代表泛型的变量> 返回值类型 方法名(参数){ }
代码演示
public class MyGenericMethod {
public <MVP> void show(MVP mvp) {
System.out.println(mvp.getClass());
}
public <MVP> MVP show2(MVP mvp) {
return mvp;
}
//调用方法时,自动确定泛型的类型
public static void main(String[] args) {
// 创建对象
MyGenericMethod mm = new MyGenericMethod();
// 演示看方法提示
mm.show("aaa");
mm.show(123);
mm.show(12.45);
}
}
三、泛型通配符
当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过通配符<?>表示。但是一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合中元素自身方法无法使用。
import java.util.*;
public class GenericTest {
public static void main(String[] args) {
List<String> name = new ArrayList<String>();
List<Integer> age = new ArrayList<Integer>();
List<Number> number = new ArrayList<Number>();
name.add("icon");
age.add(18);
number.add(314);
getData(name);
getData(age);
getData(number);
}
// ?代表可以接受任意类型
public static void getData(List<?> data) {
System.out.println("data :" + data.get(0));
}
}
四、泛型的上下限
泛型的上限:
- 格式:
类型名称 <? extends 类 > 对象名称
- 意义:
只能接收该类型及其子类
泛型的下限:
- 格式:
类型名称 <? super 类 > 对象名称
- 意义:
只能接收该类型及其父类型
比如:现已知Object类,String 类,Number类,Integer类,其中Number是Integer的父类
代码演示:
public static void main(String[] args) {
Collection<Integer> list1 = new ArrayList<Integer>();
Collection<String> list2 = new ArrayList<String>();
Collection<Number> list3 = new ArrayList<Number>();
Collection<Object> list4 = new ArrayList<Object>();
getElement1(list1);
getElement1(list2);//报错
getElement1(list3);
getElement1(list4);//报错
getElement2(list1);//报错
getElement2(list2);//报错
getElement2(list3);
getElement2(list4);
}
// 泛型的上限:此时的泛型?,必须是Number类型或者Number类型的子类
public static void getElement1(Collection<? extends Number> coll){}
// 泛型的下限:此时的泛型?,必须是Number类型或者Number类型的父类
public static void getElement2(Collection<? super Number> coll){}
总结
泛型的必要性:Collection、Map集合对元素的类型是没有任何限制的。本来我的Collection集合装载的是全部的A对象,但是外边把B对象存储到集合中,是没有任何语法错误的。把对象扔进集合中,集合是不知道元素的类型是什么的,仅仅知道是Object。因此在get()的时候,返回的是Object。外边获取该对象,还需要强制转换,所以泛型是在的学习是必要的,这在后面的集合的学习中至关重要。