泛型技术

Posted 灰太郎^_^

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了泛型技术相关的知识,希望对你有一定的参考价值。

泛型技术的介绍

  这个技术是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 = value;
        }
    }
  • 设置Integer数据
    class Demo2{
        private Integer value;
        public Integer getValue() {
            return value;
        }
        public void setValue(Integer value) {
            this.value = value;
        }
    }

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

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

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

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

  • 泛型类
    class Demo4<IT>{
        private IT value;
    
        public IT getValue() {
            return value;
        }
    
        public void setValue(IT value) {
            this.value = 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泛型

以上是关于泛型技术的主要内容,如果未能解决你的问题,请参考以下文章

作业09-集合与泛型

Java泛型:类型擦除

201621044079 韩烨作业09-集合与泛型

201621123062《java程序设计》第九周作业总结

什么意思 在HashMap之前 ? Java中的泛型[重复]

Java核心技术-泛型程序设计