1.泛型概述
泛型:JDK1.5以后出现的新特性,用于解决安全问题,是一个类型安全机制
优点:
1.将运行时期可能抛出的异常
ClassCastException()
,转移到编译时期,方便程序员解决问题。让运行期间的问题减少,且程序更加安全2.避免使用时对象强制类型转换的麻烦
2.泛型使用
泛型格式:通过<>来定义要操作的引用数据类型
在使用java提供的对象时, 什么时候写泛型?
1.通常在集合框架中很常见,只要见到有
<>
出现就要定义泛型。当使用集合时,将集合中需要存储的数据类型作为参数传递到<>即可。
2.<>
用于接收泛型的具体类型,且只能是引用数据类型,因此基本数据类型需要传入其包装类
(例:int–>Integer)。
示例代码:
//创建List集合子类ArrayList对象
ArrayList<String> al=new ArrayList<String>();
说明:以上代码的作用即创建一个用于装载String类型数据的ArrayList对象,<>
中传入的是需要装载的数据类型。
3.泛型类
什么时候定义泛型类?
当类中要操作的引用数据类型不确定的时候,可以定义泛型来完成扩展
格式:
public class GenericDemo
<T>
{}
示例代码:
public class GenericDemo<T>{
//利用泛型定义成员变量的数据类型
private T input;
//包含泛型的构造方法
public GenericDemo(T input){
this.input=input;
}
}
说明:以上代码就定义了一个简单的泛型类。利用泛型定义成员变量以及构造方法参数的类型,只需要在使用时指明具体数据类型即可。 这就使得这个类具有了较好的拓展性。
4.泛型方法
当方法的参数为泛型时,该方法就成为了一个泛型方法
格式:
public
<Q>
void setItem(Q q){}
说明:
1.泛型类定义的泛型,是在整个类中都有效的。如果这个泛型被类中方法使用,那么在泛型类的对象明确要操作的具体类型后,所有使用了这个泛型的方法的数据类型就也同时确定了。
2.为了让同一方法可以操作不同的数据类型,且类型不确定,则可以将泛型定义在方法上,且该泛型仅在这个方法中有效
3.可以同时在类和方法上定义泛型
例:public<Q>
void show(T t){}
注:T
是泛型类定义的泛型,Q
是在方法中定义的泛型
示例代码:
//泛型方法
public <Q> void setItem(Q q){
System.out.println("设置值:"+q);
}
说明:定义泛型方法只需要将泛型符号写在方法的返回值类型前即可。
5.静态泛型方法
静态方法不可以访问类上定义的泛型。如果静态方法操作的数据类型不确定,就只能将泛型定义在该方法上。静态泛型方法的定义方式与泛型方法的使用方式相似。
格式:
public static
<W>
void show(W w){}
6.泛型接口
泛型除了可以用来定义类,还可以用来定义接口
格式:
interface GenericImp<
T
>{}
示例代码:
//部分代码
//泛型接口
interface GenericImp<T>{
public void show(T t);
}
//使用泛型接口
class GenericClaByImp implements GenericImp<String>{
//实现抽象方法
public void show(String t) {
System.out.println(t);
}
}
说明:当自定义类使用泛型接口时,指明接口泛型的具体数据类型即可。
7.泛型限定
1.使用通配符
使用泛型时,若不确定泛型的具体数据类型,则可以使用通配符<?>
的方式
示例代码:
import java.util.ArrayList;
import java.util.Iterator;
public class GenericLimitDemo {
//自定义通用输出元素的方法
public void printElement(ArrayList<?> al){
Iterator<?> it=al.iterator();
while(it.hasNext()){
System.out.println("元素--"+it.next());
}
}
public static void main(String[] args) {
//新建存储数字的ArrayList对象
ArrayList<Integer> intergerArray=new ArrayList<Integer>();
intergerArray.add(5);//int自动装箱
intergerArray.add(4);
intergerArray.add(15);
intergerArray.add(12);
//新建存储字符串的ArrayList对象
ArrayList<String> stringArray=new ArrayList<String>();
stringArray.add("demoElement1");
stringArray.add("demoElement2");
stringArray.add("demoElement3");
stringArray.add("demoElement4");
stringArray.add("demoElement5");
//实例化测试类对象
GenericLimitDemo gld=new GenericLimitDemo();
//调用同一方法对不同数据类型的集合进行元素输出
gld.printElement(intergerArray);
gld.printElement(stringArray);
}
}
说明:从代码中可以看到,定义的通用输出方法printElement()
事先并不确定参数的数据类型通配符,因此可以使用通配符<?>
代替参数集合的参数部分。<?>
也相当于直接不指定泛型,即输出方法可以改为’printELement(ArrayList al)’这是java为了向下兼容,但是这样做并不严谨。
也可以用定义泛型方法的方式代替这种通配符操作
示例代码:
//部分代码
//使用泛型方法自定义通用输出元素的方法
public <T> void printElement(ArrayList<T> al){
Iterator<T> it=al.iterator();
while(it.hasNext()){
System.out.println("元素--"+it.next());
}
}
1.泛型的限定
场景引入1:
//说明:Dog extends Animal
ArrayList<Animal> al=new ArrayList<Dog>();
说明:使用集合框架时,必须保证在实例化对象时等号两侧的泛型数据类型一致。即使两侧的具体泛型类型具有继承关系也不允许。
场景引入2:
//说明:Dog extends Animal
//定义取出元素的方法
public static void print(ArrayList<Animal> al){
//省略具体代码
}
public static void main(String[] args){
ArrayList<Dog> al=new ArrayList<Dog>();
//调用输出方法输出装有Dog的ArrayList对象
print(al);
}
说明:以上代码定义了一个输出Animal集合对象的方法,在调用时却传入Animal的子类对象Dog,这样的操作会使程序报错。
解决方法:
<? extends E>
:可以接收E类型或者E类型的子类型。即限定泛型上限。
<? super E>
:可以接受E类型或者E的父类型。即限定泛型下限。
说明: 限定泛型参数的上限或下限即为泛型的参数提供了一个接收类型的范围
示例代码:
//说明:Dog extends Animal
//定义取出元素的方法
public static void print(ArrayList<? extends Animal> al){
Iterator<? extends Animal> it=al.iterator();
while(it.hasNext()){
//省略具体操作代码
}
}
public static void main(String[] args){
ArrayList<Dog> al=new ArrayList<Dog>();
//调用输出方法输出装有Dog的ArrayList对象
print(al);
}
说明:以上代码段利用定义泛型上限的方式,使得print
方法可以接受Animal对象或者其子类对象作为参数。因此,这里便体现了泛型限定对方法的拓展。