------- android培训、java培训、期待与您交流! ----------
泛型的引出
在程序中定义了一个
ArrayList al =new ArrayList();
Al.add(“123”);
Al.add(1);
使用iterator进行遍历的时候
需要强转对象后,才能使用其对象特有的方法,由于next返回 一个object
Iterator it=al.iterator();
比如String s = (String) it.next;
System.out.println(s.length());
当迭代到1的时候,就会出现问题 。但是在编译的时候发现不了这个问题。
原因是arraylist存入不同类型元素到导致的。
所以jdk1.5版本后出现了泛型这个新特性。解决了安全问题。
好处
1:将运行时期出现的问题ClassCastException,转移到了编译时期,
方便程序员解决问题,让运行时期问题减少。
2:避免了强制转换麻烦。
其实泛型就是将一个本来不确定类型,使用强制类型的方式来处理数据。
定义一个泛型集合
ArrayList<String>al = new ArrayList<String>();
//泛型也就是将一个本来不确定的类型的容器使之成为一个指定的类型的容器。
//也就是这个集合中只能存储String类型的对象。
Al.add(1);//系统就会在编译时候报错
这时
使用al.iterator()将数据(String)取入迭代器中。所以迭代器中也要指明存入元素的类型
所以,在Iterator定义的时候
Iterator <String>it = al.iterator();
使用 it.next();的到的对象就是指定的类型了,不用强转了。
泛型的格式:通过<>来定义要操作的引用数据类型。
在使用java提供的对象时候,什么时候写泛型呢?
通常在集合框架中很常见,只要见到<>就要定义泛型。
比如:在定义几个集合的时候,使用了泛型,就是指定了集合所要操作的数据类型。
在集合中的方法也是操作的具体类型的对象。
其实对于<>来说,就是用来接收类型的。
当使用集合时,将集合中要存储的数据类型作为参数传递到<>中即可。
其实也就是一个参数类型的变量,可以动态指定一个变量的类型。
在定义一个TreeSet集合时候,如果我们指定了其中存入的元素为String
则
TreeSet<String>ts = new TreeSet<String>();
当我们想定义一个特殊的比较器时候,如下
classComimplementsComparator
{
publicintcompare()
{
}
}
在上边实现接口的时候,我们很自然的就想到了,可以指定这个比较器究竟比较的什么类型的元素,这样就避免了compare中对object进行强转的问题。所以用泛型指定
一个比较器接口,使之明确自己要去比较什么类型的对象
classComimplementsComparator<String>
{
publicintcompare()
{
}
}
在对应的compare中也就明确了类型
publicintcompare(Strings,strings1)
{
}
自己理解:
当指定了一个类型的同时,在集合中凡是出现<T>的地方 ,就相当于给T赋值了一个类型。把T指明了一个指定的类型。
有了泛型,终于避免了强转。(不安全的方式)
学习完集合中,也发现定义一个java类不再是那么的简单了。
需要定义equals方法,hashCode方法,-----》为了让hashSet集合类使用。
需要实现Comparable接口,是一个类具有默认的比较性。至于用不用,也需要写上,这样就可以用到各个集合上。
需要定义toString()方法,便于集合对象的显示。
接下来就是,我们可不可以自己定义一个泛型类开发呢?
比如
我们使用一个工具类创建一个对象的时候
classWorker
{
}
class Tool
{
privateWorkerw;
publicWorker getW() {
returnw;
}
publicvoidsetW(Worker w) {
this.w = w;
}
}
如果我们需要创建一个学生对象的时候,就还需要创建一个学生工具类,很麻烦。这时就想到了泛型去指定这个Tool中所创建的类型。
classTool<T>//这里定义的类就能指定一个数据的类型,在创建这个类的时候,指定这个//T的类型,从而使类中出现T的地方都转成指定的类型。
{
private Tw;
public TgetW() {
returnw;
}
publicvoidsetW(Tw) {
this.w =w;
}
}
从而就可以创建一个指定类型的对象了。
上述就是创建一个泛型类的演示。
现在就可以使用泛型去创建一个该类的对象了。
什么时候需要定义泛型类
当类中要操作的引用数据类型不确定的时候,使用一个类型变量去指定其类型(根据传入的参数)。
早期定义object完成扩展,现在定义泛型完成扩展。
泛型定义在方法上,也叫泛型方法
为什么要定义泛型方法了,由于在定义泛型类的时候,只要明确了类型,在整个类中有效,如果被方法使用,那么泛型类的对象要操作的具体类型后,所有要操作的类型就已经固定了。
为了让不同方法可以操作不同类型,而且类型还不确定,那么可以将泛型定义在方法上。
把泛型定义在方法上其实很简单
packageday2;
importjava.util.*;
publicclassFanxing {
publicstaticvoidmain(String[] args)
{
newDemo().show("23");
newDemo().print(new Integer(123));
}
}
class Demo
{
public<T> voidshow(T t)
{
System.out.println(t);
}
public<Q>void print(Q q)
{
System.out.println(q);
}
}<T>放在返回值类型的前面。
在调用时候,传入什么类型的数据,就使用什么类型
当一个静态的方法使用泛型类的时候
我们知道,只有在创建一个对象的时候,才能指定泛型类所定义的类型。
而静态方法不需要创建对象,所以不能使用泛型类传入的类型。
所以与普通方法不同。不可以访问类上定义的泛型
如果静态方法操作的应用数据类型不确定,可以将泛型定义在方法上。
publicstatic<B>void method(B b)
{
System.out.println(b);
}
要十分主要你究竟是在定义一个泛型类还是在使用一个泛型类去创建一个对象。
这两者这间有很大的区别
泛型的高级应用。
我们定义了两个集合
ArrayList<String>al = new ArrayList<String>();
ArrayList<Integer> al2 = new ArrayList<Integer>();
但是不能这样
ArrayList<String> al =new ArrayList<Integer>();
叫做左右两边的类型不匹配。
当我们想去打印类型不同的集合中的对象时候,我们可以用一个通配符来表示
Public static void print(ArrayList<?>al)//这就是你传什么就是什么吧。
//用问号当做一个占位符。
{
Iterator<?> it = al.iterator();
System.out.println(it.next);
}
这样就能打印print(al);print(al2)
也可以这样使用
Public static <T> voidprint(ArrayList<T> al)//这就是你传什么就是什么吧。
//用问号当做一个占位符。
{
Iterator<T> it = al.iterator();
System.out.println(it.next);
}
这样也可以
但是这样写可以这样做。T t = it.next();
当一个函数的参数既想接收该类型的集合,又想传一个该类型子类型的集合的时候,我们使用
public static void print(ArrayList<?extends Person> al)
//这就表明了既可以使用Perosn类型的ArrayList也可以是person类型的子类型的arraylist
?叫做占位符,也就是泛型定义的类型不明确。
?extendsE 说明这个位置可以接收E类型或者E的子类型的集合。
?superE 可以接收E类型或者E的父类型
要明确这是在创建的时候要传入的类型值。