问题:使用ArrayList集合存储元素遍历的时候,按照正常的操作出现了问题,
当前ArrayList集合中存储了两种类型的元素分别String和Integer类型,在遍历的时候,使用的是String接收的,对于Integer类型就出现了异常!
我们可以回想一下数组的特点:
String[ ] str = new String[3] ;
str[0] = "you" ;
str[1] = "beautiful" ;
str[2] = 500; 错误的,
我们知道数组直接定义了存储的类型,防止出现其他类型的元素,那么集合能不能也像数组一样,直接规定我们集合的存储类型呢?针对这种情况有一种技术可以解决这个问题。
泛型
<数据类型> --- 切记,一定是引用数据类型
泛型:将明确集合类型的工作推迟到了创建对象或者调用方法的时候,属于一种参数化类型,可以作为参数传递.
泛型的好处:
1)将运行时期异常提前到了编译时期
2)优化了设计,解决了黄色警告线问题
3)避免了强制类型转换
当我们在方法上定义泛型之后,大大减少了工作量,只需要在自建类中定义一个show方法,就可以在测试类中输出集中不同类型的数据。
当前ArrayList集合中存储了两种类型的元素分别String和Integer类型,在遍历的时候,使用的是String接收的,对于Integer类型就出现了异常!
我们可以回想一下数组的特点:
String[ ] str = new String[3] ;
str[0] = "you" ;
str[1] = "beautiful" ;
str[2] = 500; 错误的,
我们知道数组直接定义了存储的类型,防止出现其他类型的元素,那么集合能不能也像数组一样,直接规定我们集合的存储类型呢?针对这种情况有一种技术可以解决这个问题。
泛型
<数据类型> --- 切记,一定是引用数据类型
泛型:将明确集合类型的工作推迟到了创建对象或者调用方法的时候,属于一种参数化类型,可以作为参数传递.
泛型的好处:
1)将运行时期异常提前到了编译时期
2)优化了设计,解决了黄色警告线问题
3)避免了强制类型转换
泛型的引出可以提高程序的安全性。
我们可以通过一段代码具体学习泛型
需求:使用ArrayList集合存储自定义对象并遍历,加入泛型
public class Student {
private String name ;
private int age ;
public Student() {
super();
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
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("沙僧",29) ;
Student s4 = new Student("唐僧",30) ;
//添加到集合中
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());
}
}//孙悟空----25
//猪八戒----27
//沙僧----29
//唐僧----30
******************
//孙悟空---25
//猪八戒---27
//沙僧---29
//唐僧---30
在早期的时候,使用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 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("*************************");ot.setObj(new String("高圆圆"));Integer ii = (Integer)ot.getObj() ;System.out.println(ii);}
我们可以看到,编译没有问题,但是运行的时候出现了一个异常
java.lang.ClassCastException :程序不安全 :类转换异常 (转换不当),所以程序不安全,我们更想让程序在编译时期就提醒我们有错误,这样对开发者来说修改bug更方便。
我们将泛型加在类上试一下。
public class ObjectTool<T> {
private T obj ;
public T getObj() {
return obj ;
}
public void setObj(T obj) {
this.obj = obj ;
}
我们可以看到,<T>就是泛型的标志。
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);
System.out.println("---------------");
ObjectTool<Integer> ot2 = new ObjectTool<Integer>() ;
ot2.setObj(new Integer(27));
Integer i = ot2.getObj() ;
System.out.println("年龄是:"+i);
}//姓名是:高圆圆
//---------------
//年龄是:27
泛型也可以在方法上定义。
public <T> void show(T t) {
System.out.println(t);
}
ObjectTool ot = new ObjectTool() ;
ot.show("hello");
ot.show(true);
ot.show(100); //hello
//true
//100
当没有泛型的时候,自建类中,需要使用方法重载定义三次show方法,然后在测试类中分别输出,当我们在方法上定义泛型之后,大大减少了工作量,只需要在自建类中定义一个show方法,就可以在测试类中输出集中不同类型的数据。
泛型也可以定义在接口上,而且有两种方式,我们通过下面这段代码学习一下。
首先,创建一个接口,在接口上定义泛型;
public interface Inter<T> {
//接口中变量是常量: public static final int num ;
public abstract void show() ; //抽象方法
//接口中泛型,子实现类的第一种情况,就是已经知道是什么类型了
public class InterImpl implements Inter<String> {
@Override
public void show() {
System.out.println("hello");
}
public static void main(String[] args) {
//第一种情况的测试
//创建接口的子实现类对象
Inter<String> i = new InterImpl() ;
i.show();//hello
第二种情况,在接口的子实现类中,并不知道是什么类型
public class InterImpl<T> implements Inter<T>{
@Override
public void show() {
System.out.println("hello");
}
Inter<Integer> i2 = new InterImpl<Integer>();
i2.show() ;
Inter<String> i3 = new InterImpl<String>() ;
i3.show();
//hello
//hello
我们再来看一下泛型高级(通配符)
<?> :代表任意类型Object类型,或者任意的Java类
<? extends E>:向下限定,E的子类或者E这个类型
<? super E>:向上限定,E及其他的父类
我么看下面这段代码具体理解一下这三种通配符
public static void main(String[] args) {
//创建集合对象,泛型如果明确的情况下,前后必须保持一致,不如c2,c3就会报错
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>() ;因为Animal是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{
}