泛型
来源:互联网 发布:mac开机黑屏有进度条 编辑:程序博客网 时间:2024/05/16 10:44
集合可以存储任何类型的对象,但是当把一个对象存入集合后,集合会“忘记”这个对象的类型,将该对象从集合中取出时,这个对象的编译类型就变成了Object类型。换句话说,在程序中无法确定一个集合中的元素到底是什么类型的。那么在取出元素时,如果进行强制类型转换就很容易出错。
package com.first;import java.util.ArrayList;public class HelloWorld { public static void main(String[] args) { ArrayList list=new ArrayList(); list.add("a"); //添加字符串对象 list.add("b"); list.add(1); //添加Integer对象 for (Object obj : list) { String str=(String)obj;//强制转换成String类型 } }}
这时候会报类型转换的异常,java.lang.ClassCastException。
当试图将对象强制转换为不是实例的子类时,抛出该异常。以下代码会报同样的错。
Object x = new Integer(0);System.out.println((String)x);
为了解决这个问题,在Java中引入了“参数化类型”这个概念,即泛型。它可以限定方法的数据类型,在定义集合类时,使用“<参数化类型>”的方式制定该类中方法操作的数据类型,具体格式如下:
ArrayList<参数化类型> list=newArrayList<参数化类型>();
前后的泛型必须一致,或者后面的泛型可以省略不写(1.7的新特性,菱形泛型)。
泛型的好处:
- 提高安全性(将运行期的错误转换到编译期)
- 省去强转的麻烦
自定义泛型
假设要实现一个简单的容器,用于缓存程序中的某个值,此时在这个容器类中势必要定义两个方法save()和get(),一个用于保存数据,另一个用于取出数据。
为了能存储任意类型的对象,save()方法的参数需要定义为Object类型,同样get()方法的返回值也需要是Object类型。但是当使用get()方法取出这个值时,有可能忘记当初存储的是什么类型的值,在取出时将其转换为String类型,这样程序就会发生错误。
package com.first;public class HelloWorld { public static void main(String[] args) { CachePool pool=new CachePool(); pool.save(1); String temp=(String)pool.get(); System.out.println(temp); }}class CachePool{ Object temp; public void save(Object temp){ this.temp=temp; } public Object get(){ return temp; }}
程序会报类型转换错误。
为了避免这个问题,就可以使用泛型。如果在定义一个类CachePool时使用<T>
声明参数类型(T其实就是Type的缩写,这里也可以使用其他字符,为了方便理解都定义为T),将save()方法的参数类型和get()方法的返回值类型都声明为T,那么在存入元素时元素的类型就被限定了,容器中就只能存入这种T类型的元素,在取出元素时就无须进行类型转换。
package com.first;public class HelloWorld { public static void main(String[] args) { CachePool<Integer> pool=new CachePool<Integer>(); pool.save(1); int temp=pool.get(); System.out.println(temp); }}class CachePool<T>{ T temp; public void save(T temp){ this.temp=temp; } public T get(){ return temp; }}
运行结果为1。
在定义CachePool类时,声明了参数类型为T,在实例化对象时通过将参数T指定为Integer,同时在调用save()方法时传入的数据也是Integer类型,那么调用get()方法取出的数据自然就是Integer类型,这样做的好处是不需要进行类型转换。
其中方法的泛型也可以和类的泛型不同,但建议相同。如果不同,需要在方法上声明该泛型。
如果是静态方法的话,就必须在方法上声明泛型了,因为类的泛型,是在实例化对象的时候才会把泛型传进来,而静态方法是随着类的加载而加载的,那时候还没有对象。
class CachePool<T>{ T temp; public void save(T temp){ this.temp=temp; } public T get(){ return temp; } public<Q> void show(Q q){ //如果用T的话,就和类的泛型相同了 System.out.println(q); } public static<T> void print(T t){ //这个T和类上的T没有联系,为了防止歧义,也可以叫W System.out.println(t); }}
在接口上自定义泛型
interface Inter<T>{ public void show(T t);}class Demo implements Inter<String>{ @Override public void show(String t) { System.out.println(t); }}
通配符
<?>
代表任意类型
List<?> list=new ArrayList<String>();
前后应该类型一致才可以,但开发中可能不确定后面会接受什么类型,这时候就可以用通配符<?>
。
注意这里不能写成Object,Object和String类型不一致,会报错。
在boolean addAll(Collection<? extends E> c)
这个方法中,参数Collection中存储的类型只能是E及其子类。
package com.first;import java.util.ArrayList;public class HelloWorld { public static void main(String[] args) { ArrayList<Person> list1=new ArrayList<Person>(); list1.add(new Person("张三",22)); list1.add(new Person("李四",23)); ArrayList<Student> list2=new ArrayList<Student>(); list2.add(new Student("王五",24)); list2.add(new Student("赵六",25)); list1.addAll(list2); System.out.println(list1); }}class Person{ String name; int age; public Person(String name, int age) { 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; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; }}class Student extends Person { public Student(String name, int age) { super(name, age); } @Override public String toString() { return "Student [name=" + name + ", age=" + age + "]"; }}
list2中的Student是继承自list1中的Person类的,可以运行成功。
但如果写成list2.addAll(list1);
就会报错,因为list1中元素的类型Person并不是list2中Student类的子类。
- ? extends E:向下限定,E及其子类
- ? super E:向上限定,E及其父类
- 泛型
- 泛型
- 泛型
- 泛型
- 泛型
- 泛型
- 泛型
- 泛型
- 泛型
- 泛型
- 泛型
- 泛型
- 泛型
- 泛型
- 泛型
- 泛型
- 泛型
- 泛型
- 【HDU1517】A Multiplication Game(博弈论)
- 最小二乘平差之附有参数的条件平差
- ThinkPHP5快速入门 初学memo - URL问题
- Java--EditPlus配置javac,java命令
- PAT A1018 Public Bike Management(30)
- 泛型
- PostgreSQL学习篇16.2 同步流复制的standby数据库
- 4月1-5号在武汉举行第五届全国Revit开发中高级实战训练营
- rest 最全实战-从创建到访问
- 多台web服务器之间共享session
- leetcode506: Relative Ranks
- Java设计模式之行为型模式(访问者模式)
- 3D打印前沿进展与技术介绍 —— 2016 RSNA 收获分享(下)
- python - 介绍(了解)