泛型

本文深入讲解Java泛型的基本概念、好处及其应用场景,包括泛型类、泛型方法、泛型接口以及泛型的高级应用等内容。

泛型

基本概念

在没有出现泛型之前,java也提供了对Object的引用“任意化”操作,这种“任意化”操作就是对Object引用进行向下转型及向上转型操作。但是某些强制类型转换的错误也许不会被编译器捕捉,而在运行后出现异常,可见强制类型转换存在安全隐患,所以在jdk1.5版本后提供了泛型机制。泛型就是jdk1.5出现的安全机制。
泛型机制的好处:
1.将运行时期的问题ClassCastException转到了编译时期。
2.避免了强制转换的麻烦。
泛型<>什么时候用:
当操作的引用数据类型不确定的时候,就使用<>。将要操作的引用数据类型传入即可。其实<>就是一个用于接收具体引用数据类型的参数范围。
在程序中,只要用到了带有<>的类或者接口,就要明确传入的具体引用数据类型。
泛型技术是给编译器使用的技术,用于编译时期,确保了类型安全。
泛型的擦除:运行时,会将泛型去掉,生成的class文件中是不带泛型的,这个称为泛型的擦除。
泛型的补偿:在运行时,通过获取元素的类型进行转换动作。不用使用者再强制转换了。
(由于编译好之后会擦除泛型,但是擦除之后,元素还是提升为了Object类型,这样要使用元素的特有属性的时候,又没进行强制的类型转换,这时候就通过补偿机制,自动将类型向下转型)
先来看一个例子:
例一:

public class GenericDemo {

    public static void main(String[] args) {
        ArrayList<String> al=new ArrayList<String>();
        al.add("avb");//public boolean add(Object obj)
        al.add("avb1");
        al.add("avb2");
//      al.add(4);//al.add(new Integer(4));

        Iterator<String> it=al.iterator();
        while(it.hasNext()){
            String str=it.next();
            System.out.println(str);
        }   
    }
}

在这个例子中,如果不添加泛型,在输出时的需要将集合里已经提升为Object类的元素强行向下转型为String类型:String str=(String)it.next();这时候编译时不会报错,但是在运行时会抛出ClassCastException异常:java.lang.Integer cannot be cast to java.lang.String,这是因为al.add(4);这语句添加进去的元素是Integer类型,添加进集合后提升成Object类,但是再强行向下转型为String类时,会出错。
加了泛型之后,编译不会通过,会在al.add(4);这一行报错。
例二:泛型在集合中的应用

public class GenericDemo2 {

    public static void main(String[] args) {
        //TreeSet<Person> ts=new TreeSet<Person>();
        TreeSet<Person> ts=new TreeSet<Person>(new ComparatorByName());
        ts.add(new Person("lisa",20));
        ts.add(new Person("lily",19));
        ts.add(new Person("lucy",23));
        ts.add(new Person("limi",20));

        Iterator<Person> it=ts.iterator();
        while(it.hasNext()){
            Person p=it.next();
            System.out.println(p);
        }
    }
}

//person:lily--19
//person:limi--20
//person:lisa--20
//person:lucy--23

public class Person implements Comparable<Person> {
    private String name;
    private int age;

    public Person() {
        super();

    }

    /**
     * @param name
     * @param age
     */
    public Person(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }


    @Override
    //Person类本身不具备比较性,需要通过Comparable接口或者Comparator来实现比较性
    //按照年龄
    public int compareTo(Person p) {
        int temp=this.age-p.age;
        return temp==0?this.name.compareTo(p.name):temp;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override
    //Object类的方法没有定义泛型,所以它们的参数不能改成Person等自定义的类(重写)
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Person other = (Person) obj;
        if (age != other.age)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }

    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:"+getName()+"--"+getAge();
    }
}

//按照姓名
public class ComparatorByName implements Comparator<Person> {

    @Override
    public int compare(Person o1, Person o2) {
        int temp =o1.getName().compareTo(o2.getName());

        return temp==0?o1.getAge()-o2.getAge():temp;
    }

}

在这个例子中,对TreeSet集合,Comparable接口,Comparator接口均添加了泛型。这样添加进TreeSet的类型便限定为Person类的元素了。

泛型类
在jdk1.5后,使用泛型来接收类中要操作的引用数据类型。
泛型类,什么时候用:当类中操作的引用数据类型不确定的时候,就用泛型来表示。
例三:泛型类示例

/*
public class Tool {
    private Object object;

    public Object getObject() {
        return object;
    }
    public void setObject(Object object) {
        this.object = object;
    }
//tool工具类为了提高扩展性,把接收的类型定义为object类,这样使用里面的元素的时候需要向下转型,从而会引起安全隐患
}
*/
//在jdk1.5后,使用泛型来接收类中要操作的引用数据类型。
//当类中操作的引用数据类型不确定的时候,就用泛型来表示。
//Tool.java
public class Tool<Q>{
    private Q q;

    public Q getObject() {
        return q;
    }

    public void setObject(Q object) {
        this.q = object;
    }


//Student.java
public class Student extends Person{

    public Student() {
        super();

    }

    public Student(String name, int age) {
        super(name, age);

    }

    @Override
    public String toString() {

        return "student:"+getName()+"--"+getAge();
    }

}


//Worker.java
public class Worker extends Person {

    public Worker() {
        super();

    }


    @Override
    public String toString() {

        return "worker:"+getName()+"--"+getAge();
    }


    /**
     * @param name
     * @param age
     */
    public Worker(String name, int age) {
        super(name, age);

    }
}

//GenericDefineDemo3.java
public class GenericDefineDemo3 {

    public static void main(String[] args) {
        /*
        Tool tool=new Tool();
//      tool.setObject(new Student());//tool里面本应存放student类型的元素
        tool.setObject(new Worker());//往tool里存放了一个worker类型的元素
        Student stu=(Student)tool.getObject();//想用student类里的特有方法,因此将存入tool工具类里的元素向下转型为student类
                                                //这时候因为tool里的work类不能被转为student类而发生异常
 */
        Tool<Student> tool= new Tool<Student>();
        tool.setObject(new Student());
//      tool.setObject(new Work());//编译时会报错

        Student stu=tool.getObject();
    }

}

泛型方法
当方法静态时,不能访问类上定义的泛型,如果静态方法使用泛型,只能将泛型定义在方法上。
定义泛型的方法不能使用某一具体类型的特有方法,只能使用Object类的的通用方法。

//Tool.java
public class Tool<Q>{
//泛型方法的泛型定义在修饰符后返回值前
    public <W> void show(W str){
        System.out.println("show:"+str);
    }
    /*
     * 当方法静态时,不能访问类上定义的泛型,如果静态方法使用泛型,
     * 只能将泛型定义在方法上。
     * */
    public static <Y> void method(Y obj){
        System.out.println("method:"+obj);

    }
    public void print(Q str){
        System.out.println("print:"+str);
    }
}

//GenericDefineDemo4.java
public class GenericDefineDemo4 {

    public static void main(String[] args) {
        Tool<String> tool=new Tool<String>();
        tool.show("ABC");
        tool.show(new Integer(4));
        tool.print("miaomiao");
        Tool.method("haha");
        Tool.method(new Integer(7));
    }
}

泛型接口
将泛型定义在接口上。

public class GenericDefineDemo5 {

    public static void main(String[] args) {
        InterImpl in=new InterImpl();
        in.show("headache");
        //创建子类对象的时候才明确类型
        InterImpl2<Integer> in2=new InterImpl2<Integer>();
        in2.show(5);

    }

}
//泛型接口,将泛型定义在接口上
interface Inter<T>{
    public void show(T t);
}

class InterImpl implements Inter<String>{
//实现类时知道接口泛型的具体类型
    @Override
    public void show(String t) {
        System.out.println("show:"+t);
    }

}
//实现类的时候仍然不知道泛型的类型,这时候实现的类也定义相同的泛型,

class InterImpl2<Q> implements Inter<Q>{
    public void show(Q q){
        System.out.println("show:"+q);
    }
}

泛型的高级应用

泛型通配符
在泛型机制中,提供了类型通配符,其主要作用是在创建一个泛型对象时限制这个泛型类的类型实现或者继承某个接口或者类的子类。要声明这样一个对象可以使用“?”通配符来表示,同时使用extends或者super关键字来对泛型加以限制。

public class GenericAdvanceDemo {

    public static void main(String[] args) {
        ArrayList<String> al=new ArrayList<String>();

        al.add("abc");
        al.add("abc1");

        ArrayList<Integer> al2=new ArrayList<Integer>();
        al2.add(5);
        al2.add(6);

        printCollection(al);
        printCollection(al2);

    }
/*
 * 迭代并打印集合元素
 * */

    //不明确类型并且不对类型进行操作的情形:泛型通配符
    public static void printCollection(Collection<?> al) {
        Iterator<?> it=al.iterator();
        while(it.hasNext()){
            System.out.println(it.next().toString());
        }
    }
    //不明确类型,但是对类型进行操作的情形下
    public static <T> T printCollection_1(Collection<T> al) {
        Iterator<T> it=al.iterator();
//      while(it.hasNext()){
//          T t=it.next();
//          System.out.println(t);
//      }
        T t=it.next();
        return t;
    }

}

泛型的限定
泛型的上限:class 类名 <T extends anyClass>
与通配符结合: ? extends E:接收E类型或者E的子类型对象
泛型的下限:class 类名 <T super anyClass>
与通配符结合:? super E:接收E类型或者E的父类型。下限

public class GenericAdvanceDemo2 {

    public static void main(String[] args) {
        ArrayList<Worker> al=new ArrayList<Worker>();

        al.add(new Worker("miaowu",21));
        al.add(new Worker("wangwang",14));

        ArrayList<Student> al2=new ArrayList<Student>();
        al2.add(new Student("lame",16));
        al2.add(new Student("juice",25));

        printCollection(al);
        printCollection_1(al2);

    }
    //泛型的限定:只接收Person和其子类
    public static void printCollection(Collection<? extends Person> al) {
        Iterator<? extends Person> it=al.iterator();
        while(it.hasNext()){
//          System.out.println(it.next().toString());
            Person p=it.next();
            System.out.println(p.getName()+":"+p.getAge());

        }
    }
    //泛型的限定:只接收Student和其父类
    //泛型上限
    public static void printCollection_1(Collection<? super Student> al){
        Iterator<? super Student> it=al.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }
    }
}

泛型上限的体现:
一般在存储元素都是用上限,因为这样取出都是按照上限类型来运算的,不会出现类型安全隐患。
在Collection 接口中的方法: boolean addAll()(Collection<?extends E) c)
实现该方法的伪代码如下:

class MyCollection<E>{
    public void add(E e){

    }
    public void addAll(MyCollection<? extends E> e){

    }
}

使用这个方法:

public class GenericAdvanceDemo3 {

    public static void main(String[] args) {
        ArrayList<Person> al1=new ArrayList<Person>();
        al1.add(new Person("cup",45));
        al1.add(new Person("hand",36));

        ArrayList<Worker> al2=new ArrayList<Worker>();
        al2.add(new Worker("miaowu",21));
        al2.add(new Worker("wangwang",14));

        ArrayList<Student> al3=new ArrayList<Student>();
        al3.add(new Student("lame",16));
        al3.add(new Student("juice",25));

        ArrayList<String> al4=new ArrayList<String>();
        al4.add("abcdef");

//      al1.addAll(al4);//错误,类型不匹配

        al1.addAll(al2);

        System.out.println(al1.size());
    }

泛型下限的体现:
通常对集合中的元素进行取出操作时,可以用下限。比如比较器。
TreeSet集合中有一个构造方法:TreeSet(Comparator<? extends E> comparator)

比较器的定义如下:

 class TreeSet<E>
 {
    TreeSet(Comparator<? super E> comp);
 }

要进行比较,就必须取出元素,用大于元素类型或者此元素类型的比较器进行接收,进行比较。这时候就用了泛型的下限。
(假设在存储着Person类和student类元素的容器,这时候要将容器内的元素统一按照姓名来排序,这时候的进行了泛型下限限定的比较器可以取出Person类元素,也可以取出Student类元素来进行比较)

public class GenericAdvanceDemo4 {

    public static void main(String[] args) {
//      TreeSet<Person> al1=new TreeSet<Person>();
        TreeSet<Person> al1=new TreeSet<Person>(new CompByName());
        al1.add(new Person("cup",45));
        al1.add(new Person("hand",36));
        al1.add(new Person("never",20));

        TreeSet<Worker> al2=new TreeSet<Worker>();

        al2.add(new Worker("miaowu",21));
        al2.add(new Worker("wangwang",14));     

//      TreeSet<Student> al3=new TreeSet<Student>();
        TreeSet<Student> al3=new TreeSet<Student>(new CompByStuName());
        al3.add(new Student("lame",16));
        al3.add(new Student("juice",25));
        al3.add(new Student("nancy",23));

        TreeSet<String> al4=new TreeSet<String>();
        al4.add("abcdef");


//      Iterator<Person> it=al1.iterator();
//      while(it.hasNext()){
//          System.out.println(it.next());
//      }

        Iterator<Student> it3=al3.iterator();
        while(it3.hasNext()){
            System.out.println(it3.next());
        }
    }
}
/*
 * 
 * 什么时候用下限:通常对集合中的元素进行取出操作时,可以用下限。比如比较器。
 * */
class CompByName implements Comparator<Person>{

    @Override
    public int compare(Person o1, Person o2) {
        int temp=o1.getName().compareTo(o2.getName());

        return temp==0?o1.getAge()-o2.getAge():temp;
    }

}

class CompByStuName implements Comparator<Student>{
    @Override
    public int compare(Student o1, Student o2) {

        int temp=o1.getName().compareTo(o2.getName());

        return temp==0?o1.getAge()-o2.getAge():temp;
    }

}

通配符的体现
containsAll(Collection<?> c)

包含方法的内在实现就是使用equals方法,这个方法的参数是Object类,所以使用通配符”?”
此方法的设计原理:

class MyCollection2<E>{
    public boolean containsAll(Collection<?> coll){

        return true;
    }
}

此方法的使用:

public class GenericAdvanceDemo5 {

    public static void main(String[] args) {
        ArrayList<Person> al1=new ArrayList<Person>();

        al1.add(new Person("abc",45));
        al1.add(new Person("hand",36));
        al1.add(new Person("never",20));

        ArrayList<Person> al2=new ArrayList<Person>();
        al2.add(new Person("abc222",45));
        al2.add(new Person("cup233",36));
        al2.add(new Person("never23333",20));

        al1.containsAll(al2);

        TreeSet<String> al4=new TreeSet<String>();
        al4.add("abcdef");
        al4.add("abc");

        al1.containsAll(al4);

        Iterator<Person> it=al1.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }
    }
    public static void printCollection(Collection<?> al){
        Iterator<?> it=al.iterator();
        while(it.hasNext()){
            System.out.println(it.next().toString());//全都调用的toString方法,是Object类的方法,所以不确定,用?通配符
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值