首页 > 代码库 > 泛型技术

泛型技术

泛型技术的介绍

  这个技术是JDK5中提供的。

  针对集合这类容器而言,它中可以存放任意的对象,当任何的对象存放到集合中之后,都被提升成Object类型,当我们从集合中遍历出每个对象的时候,拿到的都是Object类型,这时如果我们想使用对象自身的功能时,就需要向下转型。只要使用向下转型都可能发生ClassCastException异常

  在给集合中存放对象的时候,以Object形式存放进去的。在取出时也是Object类型,当我们在使用对象的特有方法时,进行向下转型,结果发生了异常。造成这个异常的原因是集合中可以存放任意类型的对象,而在取出的时候,我们并没有对取出的类型做任何的判断,直接把取出的类型转成某种特定的类型。在转的时候,不是这种类型的数据,在强转时就会发生异常。

  集合是个容器,数组也个是容器。可是数组在定义好之后,它的长度固定了,其中存放的数据类型也被固定了。如果给数组中存放类型不匹配的元素,直接在编译时期就报错。

int[] arr = new int[4];arr[0] = 3.14;  //这里直接编译报错。

  数组在定义的时候,就把其中存放的数据类型限定死 了。存放的数据一旦不同就直接编译失败。

  我们现在就把数组这种思想移到集合上,在定义集合的时候,也限定集合中存放的数据类型,那么在真正给集合中存放数据的时候如果类型不一致,直接让编译失败。这种实现就Java给我们提供的泛型技术

泛型技术的简单应用

  泛型技术:

  • 格式:<具体的类型>  这个类型只能是引用数据类型
  • 作用(解决的问题):把运行时期的问题,提前到了编译时期。可以保证程序在编译通过之后,运行的时候不会再出现问题。
  • 应用:
    • 在API中,只要看到接口,类名等后面使用<变量名> 都是泛型技术;
    • 当我们在使用这些接口,或者类的时候,要使用者类来指定<> 中间的具体类型。
  • 泛型技术在JDK7中升级,升级成菱形技术
    public class GenericDemo2 {    public static void main(String[] args) {        //创建TreeSet集合,并且使用泛型技术        TreeSet<String> set = new TreeSet<String>( new Comparator<String>(){            public int compare(String o1, String o2) {                int temp = o1.length() - o2.length();                return temp == 0 ? o1.compareTo(o2) : temp;            }                    } );                set.add("aaa");        set.add("dddd");        set.add("a");        set.add("AAA");        set.add("DDDD");        set.add("aaa");                for (String obj : set) {            System.out.println(obj);        }    }}

泛型类、方法、静态方法泛型

  在查阅api的时候发现,api中提供的类上或者接口上应用了泛型技术。oracle公司在设计Java中的这些类的时候,它们在类上,或者接口定义了泛型,那么我们就可以模仿他们,在自己定义类的或者接口的时候,也能够使用泛型技术。

泛型类

  需求:定义一个类,负责设置数据,和获取数据

  • 设置字符串数据
    class Demo{    private String value;    public String getValue() {        return value;    }    public void setValue(String value) {        this.value =http://www.mamicode.com/ value;    }}
  • 设置Integer数据
    class Demo2{    private Integer value;    public Integer getValue() {        return value;    }    public void setValue(Integer value) {        this.value =http://www.mamicode.com/ value;    }}

   上面书写的2个类的确可以设置值也可以获取值,但是它们只能给某个特定的类型来设置值和获取值。当类型不一致时,就无法设置和获取,为了能够给任意对象设置值和获取值,那么:

  • 可以把类中的变量类型定义Object类型。
    class Demo3{    private Object value;    public Object getValue() {        return value;    }    public void setValue(Object value) {        this.value =http://www.mamicode.com/ value;    }}

  把类中的变量类型改成Object类型之后的确可以保存任意类型的数据,但是调用获取方法时返回的却变成了Object类型,当要使用对象的特有数据时,又要向下转型。这时我们可以使用泛型技术来完成需求。

  这时可以把泛型定义在类上,定以在类上的泛型,就相当于给这个类定义了参数,当使用这个类的时候,要求使用者来指明这个参数到底是什么类型。当把泛型定义在类上时,这个类就成为泛型类。定义在类上的泛型可以在类中直接使用

  • 泛型类
    class Demo4<IT>{    private IT value;    public IT getValue() {        return value;    }    public void setValue(IT value) {        this.value =http://www.mamicode.com/ value;    }}
    public class GenericDemo {    public static void main(String[] args) {        //创建拥有泛型的类对象时,要求指明具体的泛型类型        Demo4<String> d4 = new Demo4<String>();        d4.setValue("aaaa");        String value = d4.getValue();                Demo4<Integer> d5 = new Demo4<Integer>();        d5.setValue(123);        Integer value2 = d5.getValue();    }}

泛型方法及静态方法泛型

  定义在类上的泛型可以在类中的成员变量方法上使用。有时我们也会遇到方法上要使用的泛型和类上定义的不一致,这时就需要把泛型定义在方法上

  • //类上定义的泛型class Test<W>{    private W value;    //方法上使用类上的泛型    public void show( W w){        System.out.println(w);    }    /*     * 定义功能,专门负责打印任何数据,可以在这个方法上定义泛型     * 在方法上定义的泛型,必须写在返回值的前面     */    public <Q> void print( Q q ){        System.out.println(q);    }    /*     * 静态方法无法使用类上的泛型。     * 因为类上的泛型的具体类型必须是在创建本类对象的时候明确。     * 而静态的方法它不需要对象就可以直接执行。     */    public static <T> void method( T w ){        System.out.println(w);    }}public class GenericDemo4{    public static void main(String[] args) {        Test<String> t = new Test<>();        t.print("aaa");        t.print(123);        t.print(new  Object());
    Test.method("bbb"); }}

泛型接口和泛型传递

  • 泛型接口,即:把泛型定义在接口上,泛型接口。
  • 把泛型定义在类上,创建这个类对象的时候,由具体的创建者来明确具体的泛型类型
  • 定义在接口上,接口是要有实现类来实现这个接口。这时就可以在实现这个接口的时候明确具体的泛型
  • 当在定义一个类实现某个接口的时候,如果接口上有泛型,但是当前这个实现并不能明确具体的类型时,这时这个实现类可以继续把泛型往下传递,最后后具体创建这个实现类对象的使用这个类明确具体的泛型。这个技术称为泛型的传递

 

  • 定义泛型接口
    interface Inter<W>{    public void show(W w);}
  • 实现类实现泛型接口
    /* * 在书写接口的实现类,就是在使用接口,这时可以明确接口上的泛型类型 */class InterImpl implements Inter<String>{    @Override    public void show(String w) {        System.out.println(w);    }}
  • 泛型的传递
    //interface Collection<E>{}//interface List<E> extends Collection<E>{}//class ArrayList<E> implements List<E>{}/* * 在定义实现类的时候,如果不知道接口中泛型的具体类型时,可以继续使用泛型 */class InterImpl2<W> implements Inter<W>{    public void show(W w) {    }}
    public class GenericDemo5{    public static void main(String[] args) {        InterImpl2<String> in = new InterImpl2<>();    }}

泛型通配符

  使用一个符号来匹配所有的数据。这个符号就被称为通配符。

  • 需求:

    定义功能,专门用来打印集合中的数据

     * 打印集合,就是使用遍历,取出集合中的每个元素,然后打印在屏幕上即可。

    public class GenericDemo7 {    public static void main(String[] args) {        ArrayList<String> al = new ArrayList<String>();                Collections.addAll(al, "aaaa","dddd","rrrrr","sssss","itcast","ABCD");                printCollection(al);                LinkedList<Integer> ll = new LinkedList<Integer>();        ll.add(123);        ll.add(333);        ll.add(444);        ll.add(5555);                printCollection(ll);    }    /*     * 定义功能负责打印Collection集合中的元素     * 这个方法要打印的Collection集合中的元素,这时并不知道真正传递进来的容器中存放的是什么类型的元素。     * 在定义功能的时候,无法明确Collection的泛型类型。     * 这时可以使用泛型中的通配符来代替。     * Java中泛型的通配符可以使用?来表示     *  Collection<?> coll 当前这个方法可以接收Collection下的所有子集合。?表示的是子集合中的元素类型,     *  当传递的子集合中存放的是什么类型,?就代表什么类型     */    public static  void printCollection( Collection<?> coll ){        for (Iterator<?> it = coll.iterator(); it.hasNext();) {            System.out.println(it.next());        }    }    /*    public static void printCollection( ArrayList<String> coll ){        for (Iterator<String> it = coll.iterator(); it.hasNext();) {            System.out.println(it.next());        }    }    public static void printCollection( LinkedList<Integer> coll ){        for (Iterator<Integer> it = coll.iterator(); it.hasNext();) {            System.out.println(it.next());        }    }    */}

泛型上下限

  • /* * 泛型的限定 */public class GenericDemo6 {    public static void main(String[] args) {        //这个容器中存放Student对象        ArrayList<Student> al = new ArrayList<Student>();                al.add(new  Student("zhangsan",23));        al.add(new  Student("lsi",23));        al.add(new  Student("wangwu",23));        al.add(new  Student("zhaoliu",23));        al.add(new  Student("zhangsan",23));                printCollection(al);                //这个容器中存放Person对象        ArrayList<Person> al2 = new ArrayList<Person>();                al2.add(new  Person("zhangsan",23));        al2.add(new  Person("lsi",23));        al2.add(new  Person("wangwu",23));        al2.add(new  Person("zhaoliu",23));        al2.add(new  Person("zhangsan",23));                printCollection(al2);        //这个容器中存放Worker对象        LinkedList<Worker> ll = new LinkedList<Worker>();                ll.add(new  Worker("xiaoqiang",33));        ll.add(new  Worker("wangcai",32));        ll.add(new  Worker("huaan",28));        ll.add(new  Worker("qiuxiang",18));                //printCollection(ll);    }    /*     * 这个方法Collection<?> coll 它可以接收Collection下的所有子类,并且可以打印其中的数据     * 现在对这个方法进行限定,可以打印集合中的元素,但是要求集合中的元素必须是Person类型或者是Person的子类类型     * 这时需要在方法上加泛型的限定     *     泛型的上限限定    ? extends Person  这里表示?可以是Person本身,也可以是Person的子类类型     *     泛型的下限限定   ? super Student  这里?表示当前可以是 Student类型,也可以是Student的父类类型     */    public static  void printCollection( Collection< ? extends Person > coll ){        for (Iterator<?> it = coll.iterator(); it.hasNext();) {            System.out.println(it.next());        }    }}

泛型在API中的应用

以TreeSet构造方法为例:

public  class   TreeSet<E> implements Set<E>{    public TreeSet(){}}

//可以接收一个集合作为创建TreeSet容器时的初始化数据

public TreeSet( Collection<  ?  extends E >  c ){}

这个构造方法主要作用用于创建TreeSet集合,在创建这个集合的时候可以给其传递一个Collection下的子集合,作为TreeSet集合的创建时的初始数据。

TreeSet<...> set  =  new  TreeSet<...>(...)

在创建TreeSet集合对象的时候,明确了泛型,那么就表示TreeSet定义的E泛型就被明确出来了。由于TreeSet集合要对其中的元素进行排序。

在明确的这个E一定是具备排序的功能。那么在创建TreeSet的时候,传递另外一个集合进来,就要求传递进来的这个集合中的元素一定要能够和E进行比较,然后进行排序。

Collection<  ?  extends E >  这里限定上限的原因是由于创建TreeSet集合的时候明确的类型E类型,那么给TreeSet集合对象初始数据的时候,初始的数据可以E本身,也可以是E的子类类型, 这个样就能够保证在TreeSet集合中存放下。

public TreeSet(Comparator<? super E> comparator) {

 创建TreeSet集合的时候,可以传递比较器。比较器的功能是对集合中的元素进行比较的。

比较器就要从集合中把元素取出来,对其中的元素进行比较。比较器首先要从集合中把元素取出来,并且比较器就要使用响应的类型来接收取出的这些元素。

new TreeSet<E> (new  Copmarator(){});

 

Collecions工具类中的方法

                      定义泛型                                       返回值类型     方法名         参数列表

public static   <T extends Object & Comparable<? super T>>    T       max    (Collection<? extends T> coll)    {

 <T extends Object & Comparable<? super T>> T这个类型必须是Object子类同时T类型还要实现Comparable接口

}

Java总结篇系列:Java泛型

泛型技术