一、泛型的引出
需求:ArrayList集合存储元素并遍历:
public class GenericDemo {
public static void main(String[] args) {
//创建集合对象
ArrayList array = new ArrayList() ;
//添加元素
array.add("hello") ;
array.add("world") ;
array.add("java") ;
array.add(new Integer(100)) ;
//遍历
//获取迭代器
Iterator<String> it = array.iterator() ;
while(it.hasNext()) {
String s = it.next() ;
System.out.println(s);
}
}
}
在写程序时没有问题,运行时发现报错:java.lang.ClassCastException:类转换异常,这是由于当前ArrayList集合中存储了两种类型的元素:String和Integer类型,在遍历的时候,使用的是String接收的,对于Integer类型就出现了异常!
回想数组:
String[] str = new String[3] ;
str[0] = "hello" ;
str[1] = "world" ;
str[2] = 100 ; 错误的,
数组直接定义了存储的类型,防止出现其他类型的元素,集合能不能也像数组一样,直接规定我们集合的存储类型,针对这种情况
一种技术:泛型
<数据类型> --- 引用数据类型
在刚才的代码中加入泛型,重新写一下:
public class GenericDemo {
public static void main(String[] args) {
//创建集合对象
ArrayList<String> array = new ArrayList<String>() ;
//添加元素
array.add("hello") ;
array.add("world") ;
array.add("java") ;
//array.add(new Integer(100)) ; //已经定了当前集合时String类型
//遍历
//获取迭代器
Iterator<String> it = array.iterator() ;
while(it.hasNext()) {
//java.lang.ClassCastException:类转换异常
String s = it.next() ;
System.out.println(s);
}
}
}
可以发现:此时如果要往集合中添加Integer类型的元素则会报错,因为此时集合中规定只能添加String类型的元素。
二、泛型的好处
1)将运行时期异常提前到了编译时期2)优化了设计,解决了黄色警告线问题
3)避免了强制类型转换
泛型的引出可以提供程序的安全性!
需求:使用ArrayList集合存储自定义对象并遍历,加入泛型
测试类:(Student类大家自己写)
public class GenericDemo2 {
public static void main(String[] args) {
//创建集合对象
ArrayList<Student> array = new ArrayList<Student>() ; //=号右边的泛型:泛型推断
//创建学生对象
Student s1 = new Student("王昭君",25) ;
Student s2 = new Student("西施",27) ;
Student s3 = new Student("杨贵妃",25) ;
Student s4 = new Student("貂蝉",28) ;
//添加到集合中
array.add(s1) ;
array.add(s2) ;
array.add(s3) ;
array.add(s4) ;
//获取迭代器
Iterator<Student> it = array.iterator() ;
while(it.hasNext()) {
Student s = it.next() ;
System.out.println(s.getName()+"----"+s.getAge());
}
System.out.println("------------------");
//普通for循环
for(int x = 0 ; x < array.size() ; x ++) {
Student s = array.get(x) ;
System.out.println(s.getName()+"---"+s.getAge());
}
}
}
在早期的时候,使用Object类型代表任意类型,向上转型没有问题,
使用向下转型的时候,可能会出现问题(需要的类型有问题),这个时候就需要引入泛型操作
看API,接口,类,方法上有<E> ,都是泛型
没有泛型的Object工具类:
public class ObjectTool {
private Object obj ;
public Object getObj() {
return obj ;
}
public void setObj(Object obj) { //Object obj = new Integer() ; //多态
this.obj = obj ;
}
}
没有泛型的测试类:
public class ObjectToolDemo {
public static void main(String[] args) {
//创建ObjectTool该类 对象
ObjectTool ot = new ObjectTool() ;
//赋值
ot.setObj(new Integer(27));
//获取值
Integer i = (Integer) ot.getObj() ; //向下转型
System.out.println("年龄是:"+i);
System.out.println("---------------------");
ot.setObj(new String("高圆圆")) ;
String s = (String)ot.getObj() ;
System.out.println("姓名是:"+s);
System.out.println("---------------------");
//java.lang.ClassCastException :程序不安全 :类转换异常 (转换不当)
ot.setObj(new String("高圆圆"));
Integer ii = (Integer)ot.getObj() ;//类转换异常,运行时出错
System.out.println(ii);
}
}
三、定义泛型的不同位置
1.把泛型定义在类上
提高程序安全性,解决了向下类型转换出现的问题:ClassCastException
加入泛型的Object工具类:
public class ObjectTool<T> {
private T obj ;
public T getObj() {
return obj ;
}
public void setObj(T obj) {
this.obj = obj ;
}
}
加入泛型的测试类:
public class ObjectToolDemo {
public static void main(String[] args) {
//创建ObjectTool对象
ObjectTool<String> ot = new ObjectTool<String>() ;
//赋值
ot.setObj(new String("高圆圆"));
String s = ot.getObj() ;
System.out.println("姓名是:"+s);
//ot.setObj(new Integer(27)); 直接隐藏的问题解决了
System.out.println("---------------");
ObjectTool<Integer> ot2 = new ObjectTool<Integer>() ;
ot2.setObj(new Integer(27));
//ot2.setObj(new String("高圆圆"));
Integer i = ot2.getObj() ;
System.out.println("年龄是:"+i);
}
}
此时就会避免类转换异常的错误,因为此时ot里必须添加String类型的元素,ot2里必须添加Integer类型的元素,否则在编译时期就会报错。
2.把泛型定义在方法上
前面我们把泛型定义在类上,发现如果要添加一种类型的元素则必须创建一个此类型的对象才能接收,如果有很多类型的元素,那么就得创建很多接收这种类型元素的对象,那么可不可以把泛型定义在方法上呢?答案是肯定的,接下来我们就来看一个例子。
工具类:
public class ObjectTool{
public <T> void show(T t) {
System.out.println(t);
}
}
测试类:
public class ObjectToolDemo {
public static void main(String[] args) {
ObjectTool ot = new ObjectTool() ;
ot.show("hello");
ot.show(true);
ot.show(100);
}
}
这时ot就可以接收任何类型的元素了。
3.把泛型定义在接口上
//将泛型定义在接口上
public interface Inter<T> {
public abstract void show() ; //抽象方法
}
第一种情况:知道具体的类型
public class InterImpl implements Inter<String> {
@Override
public void show() {
System.out.println("hello");
}
}
第二种情况:不知道具体类型
public class InterImpl<T> implements Inter<T>{
@Override
public void show() {
System.out.println("hello");
}
}
测试类:
public class InterDemo {
public static void main(String[] args) {
//第一种情况的测试
//创建接口的子实现类对象
Inter<String> i = new InterImpl() ;
i.show();
System.out.println("---------------------");
//第二种情况
Inter<Integer> i2 = new InterImpl<Integer>();
i2.show() ;
Inter<String> i3 = new InterImpl<String>() ;
i3.show();
}
}
四、泛型的高级通配符
<?> :代表任意类型Object类型,或者任意的Java类
<? extends E>:向下限定,E的子类或者E这个类型
<? super E>:向上限定,E及其他的父类
public class GenericDemo {
public static void main(String[] args) {
//创建集合对象,泛型如果明确的情况下,前后必须保持一致
Collection<Object> c1 = new ArrayList<Object>() ;
//Collection<Object> c2 = new ArrayList<Cat>() ;
//Collection<Object> c3 = new ArrayList<Animal>() ;
//<?> :代表任意类型Object类型,或者任意的Java类
Collection<?> c4 = new ArrayList<Object>() ;
Collection<?> c5 = new ArrayList<Animal>() ;
Collection<?> c6 = new ArrayList<Dog>() ;
Collection<?> c7= new ArrayList<Cat>() ;
//<? extends E>:向下限定,E的子类或者E这个类型
Collection<? extends Object> c8 = new ArrayList<Object>() ;
Collection<? extends Object> c9 = new ArrayList<Animal>() ;
Collection<? extends Object> c10 = new ArrayList<Cat>() ;
//Collection<? extends Aninal> c11 = new ArrayList<Object>() ;
//<? super E>:向上限定,E及其他的父类
//Collection<? super Animal> c12 = new ArrayList<Cat>() ;
Collection<? super Animal> c13 = new ArrayList<Animal>() ;
Collection<? super Animal> c14 = new ArrayList<Object>() ;
}
}
class Animal{
}
class Cat extends Animal {
}
class Dog extends Animal{
}
至此,泛型的学习到此结束,欢迎大家指正错误,共同进步!