泛型是jdk1.5出现的新特性,用于解决安全问题,它是一种类型安全机制。
我们在定义数组的是时候,就已经明确了数组中应该存储的元素类型,而在集合中没有这个明确,所以在jdk1.5出现了泛型,在定义集合容器的时候,就规定这个集合应该存储什么类型的元素。
泛型的好处:
1,将运行时期出现的ClassCastException转移到了编译时期。方便程序员解决问题。让运行时期问题减少,安全。
2,避免了强制类型转换的麻烦。
示例一:泛型入门,泛型的最简单使用。
import java.util.*;
public class GenericDemo
{
public static void main(String[] args) {
//使用泛型,明确要存入ArrayList中的元素类型是String类型。
ArrayList<String> al = new ArrayList<String>();
al.add("sdfda");
al.add("sdfdaew");
al.add("sdfdaqwr");
al.add("sdfdaefw");
//Iterator本来是迭代器类型,加了泛型就是告诉程序,迭代器中的元素是String类型的。
for (Iterator<String> it = al.iterator();it.hasNext() ; )
{
//String s =(String)it.next();
//System.out.println(s + "::" + s.length());//如果要打印字符串的长度就必须要将迭代器中的数据强制转换成字符串类型;
String s = it.next();
System.out.println(s.length());
}
}
}
二,java文档中泛型的使用。
在javaAPI中,其实很多地方都存在这泛型的使用,当然这些都是jdk1.5之后才有的,1.5之前是没有泛型的。
比如文档中:java.util包中的Comparator接口:
int |
这里的T就是说比较的o1,o2可以使任意类型,所以只要指定了类型,操作时就可以不用使用强制类型转换,用法请看下面的示例:
import java.util.*;
public class GenericDemo2
{
public static void main(String[] args) {
TreeSet<String> ts = new TreeSet<String>(new myComparator());
ts.add("sdh");
ts.add("aaa");
ts.add("dfsg");
ts.add("rrr");
for (Iterator<String> it=ts.iterator();it.hasNext() ; )
{
String s = it.next();
sop(s);
}
}
public static void sop(String obj) {
System.out.println(obj);
}
}
class myComparator implements Comparator<String>
{
public int compare(String s1,String s2) {
int num = new Integer(s1.length()).compareTo(new Integer(s2.length()));
if (num == 0)//如果要改成逆序的,不需要写几个if判断,直接把上一行的s1和s2调换顺序即可
return s1.compareTo(s2);
return num;
}
}
三,泛型类的定义与使用:
当要操作的引用数据类型不确定时,可以通过泛型来完成。在jdk1.5之前没有泛型,程序员们是怎样操作的呢?早期的程序员在遇到数据类型不确定时都是通过类型提升,因为任何类型都是Object类的子类,所以将这些类都转换成Object类,得到的结果也是Object类型的,最后通过类型转换将Object类型的对象转换成需要的类型。
下面这个程序将两种泛型类的定义方式都写出了,对比就可以发现泛型的优势所在。
public class GenericDemo3
{
public static void main(String[] args) {
/*
Tool t = new Tool();
t.setObject(new Worker3());
Worker3 w = (Worker3)t.getObject();*/
Tool<Worker3> t = new Tool<Worker3>();
t.setMM(new Worker3());
Worker3 w = t.getMM();
//泛型的还有一大好处是将运行时可能出现的异常放到了编译时期,避免类型之间的转换。
}
}
class Student3
{
}
class Worker3
{
private String name;
private int num;
public String getName() {
return name;
}
}
//早期方式:
/*
class Tool
{
private Object obj;
public void setObject(Object obj) {
this.obj = obj;
}
public Object getObject() {
return obj;
}
}
*/
//使用泛型方式:
class Tool<MM>
{
private MM m;
public void setMM(MM m) {
this.m = m;
}
public MM getMM() {
return m;
}
}
四,泛型方法的使用:
泛型可以定义在类上也可以定义在类中的方法上;
1,当定义在类上时,当创建对象时指定该对象的类型后,引用类中的方法时,其类型也都固定了;所有操作的类型都固定了。
2,为了让不同的方法操作不同的类型,而且操作的类型还不确定时,就可以把泛型定义在方法上。
3,当然这两种方法因情况而定。当某个方法使用完之后,该方法的类型又变成了不确定类型,还可以在其被调用。
注意:当泛型定义在静态法方法上时,一定不能定义成类的泛型,类没初始化之前静态方法就加载到内存中,此时类的泛型还不知道,静态法方法是不可以访问类上定义的泛型的。所以如果静态方法操作的数据类型不确定时,就可以将泛型定义在方法上。
**泛型定义在方法上,类型是放在返回值类型的前面,并且紧挨着返回值类型。
示例:泛型定义在方法上的例子
public class GenericDemo4
{
public static void main(String[] args) {
/* Demo4<String> d = new Demo4<String>();
d.setDemo4("kkgj");
d.getDemo4("dfg");*/
/*
Demo4<> d = new Demo4();
d.setDemo("sdajgka");
d.showDemo(new Integer(15));
d.setDemo(4);*/
Demo4<String> d = new Demo4<String>();
d.setDemo4("sdajgka");
d.getDemo4("sdg");
d.getDemo4(45);
Demo4.showDemo("----dsfg");
Demo4.showDemo(555);
}
}
/*
class Demo4
{
public <T> void setDemo(T t) {
System.out.println("haha------" + t);
}
public <Q> void showDemo(Q q) {
System.out.println("hehe+++++++" + q);
}
}
*/
class Demo4<T>
{
public void setDemo4(T t) {
System.out.println("haha" + t);
}
public <Q> void getDemo4(Q q) {
System.out.println("hehe" + q);
}
public static <W> void showDemo( W w) {//如果在静态方法中引用了泛型数据类型,则编译时会出错,原因是对象一建立,
//静态方法最先加载到内存中。所以静态方法一定要定义成泛型。
System.out.println("SDF" + w);
}
}
总结一点:当把泛型定义在类上时,类中的方法可以定义泛型也可以不定义,如果类中既包含泛型方法,又包含非泛型方法,则在调用时
非泛型方法的操作类型是随着泛型类的类型走的,而泛型方法的操作类型是不确定的,可以被多次调用。
五,泛型定义在接口上:
这种情况不太常用,一般都是别人提供的接口已经指定接口的类型。
示例:
interface Inter<T>
{
public abstract void show(T t);
}
//知道实现该接口时的类型,这个方法只能操作String类型
/*
class ImpInter implements Inter<String>
{
public void show(String s) {
System.out.println(s);
}
}
*/
//当不明确要实现接口的类型时,还可以把类定义成泛型。
class imp <T> implements Inter<T>
{
public void show(T t) {
System.out.println("aaa" + t);
}
}
public class GenericDemo5
{
public static void main(String[] args) {
/* ImpInter i = new ImpInter();
i.show("sdg");*/
imp<Integer> i = new imp<Integer>();
i.show(4);
}
}
六,泛型中通配符?的使用:
泛型通配符”?”也叫做占位符,它的作用和前面几个示例中的T的作用是差不多的,为什么说它是一个占位符呢?因为有?的地方是在告诉使用者,这里有泛型,具体是什么类型的泛型,不知道!而使用T则告诉使用者,这里有泛型,泛型的类型是T类型。
?的好处在程序中是怎么表现出来的呢。请参看下面这和示例。
需求:定义一个Person类,在定义一个Student类继承自Person类,并将Person对象及Student对象存储到ArrayList集合中,使用迭代器迭代出集合中的元素。
(重点在程序后面的注释部分)
import java.util.*;
public class GenericDemo6
{
public static void main(String[] args) {
ArrayList<Person> al = new ArrayList<Person>();
al.add(new Person("abc01"));
al.add(new Person("abc02"));
al.add(new Person("abc03"));
print3(al);
ArrayList<Student> al1 = new ArrayList<Student>();
al1.add(new Student("abc-----01"));
al1.add(new Student("abc-----02"));
al1.add(new Student("abc-----03"));
print3(al1);
}
public static void print1(ArrayList<?> al) { //如果这里将?改成Person,那么就规定了al中只能存储Person类型的元素
//如果想存储Person类型的子类型就不可以了,所以通配符的好处也就体现出来了。
//但是如果我规定在al中存储Person类型和Person的子类型,怎么做呢?请看print3()方法。
for (Iterator<?> it = al.iterator();it.hasNext() ; )
{
Person t = (Person)it.next();
System.out.println(t.getName());
}
}
/*上面print1方法使用泛型通配符来明确ArrayList集合al中存储的元素类型是任意类型
这里的print2使用的是T来表示al中存储的元素类型是T类型,这两种方式都可以告诉使用者al可以使任意类型。
但是T表示的是一种确定的类型,首先必须在void前面说明,它与通配符不同的地方在于可以直接操作T类型,
而通配符是不能对它进行操作的,因为?只是说明这是一种不确定的类型,不能直接操作?*/
public static <T> void print2(ArrayList<T> al) {
for (Iterator<T> it = al.iterator();it.hasNext() ; )
{
//T t = it.next();
System.out.println(it.next().getName());
}
}
//泛型通配符?限定泛型的上界
public static void print3(ArrayList<? extends Person> al) { //这里表示al中可以存储Person类及Person的子类,这也就是泛型的限定。
for (Iterator<? extends Person> it = al.iterator();it.hasNext() ; )
{
System.out.println(it.next().getName());
}
}
}
class Person
{
private String name;
Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
class Student extends Person
{
Student(String name) {
super(name);
}
}
泛型限界的好处:
TreeSet(Comparator<? super E> comparator) (摘自javaAPI)
这里给TheeSet集合传入一个比较器,比较器中比较的对象是E类型的或者E的父类型。
当我们定义一个父类的时候,其下有很多子类型继承自该父类,他们都需要实现Comparator接口,如果传入的比较器将其类型限定为某一个子类型,那么当其他子类也要实现Comparator接口时,就需要重新定义另一个比较器,而我们看到TreeSet集合传入的比较器比较的对象的类型是E或者父类,那么我们就可以将比较器类型规定为这些子类的父类型,那么当它的子类需要用到这个比较器时,就可以直接拿来用,这也是多态的一个体现。
请参看示例:
import java.util.*;
public class GenericDemo7
{
public static void main(String[] args) {
myComp m = new myComp();
TreeSet<Student> ts = new TreeSet<Student>(m);
ts.add(new Student("Student001"));
ts.add(new Student("Student002"));
ts.add(new Student("Student003"));
ts.add(new Student("Student004"));
for (Iterator<Student> it = ts.iterator() ;it.hasNext() ; )
{
System.out.println(it.next().getName());
}
TreeSet<Worker> ts2 = new TreeSet<Worker>(m);
ts2.add(new Worker("Worker001"));
ts2.add(new Worker("Worker002"));
ts2.add(new Worker("Worker003"));
ts2.add(new Worker("Worker004"));
for (Iterator<Worker> it = ts2.iterator() ;it.hasNext() ; )
{
System.out.println(it.next().getName());
}
}
}
/*
class myComp implements Comparator<Student>
{
public int compare(Student s1,Student s2) {
return s2.getName().compareTo(s1.getName());
}
}
*/
class myComp implements Comparator<Person>//这里是Person p =new Student();多态;
//如果这里写的是Student则每次新建父类Person的子类时,都必须重新在写一个相同的比较方法
//创建不同的泛型。
{
public int compare(Person s1,Person s2) {
return s2.getName().compareTo(s1.getName());
}
}
class Person
{
private String name;
Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
class Student extends Person
{
Student(String name) {
super(name);
}
}
class Worker extends Person
{
Worker(String name) {
super(name);
}
}