zoukankan      html  css  js  c++  java
  • JavaSE| 泛型

    泛型

    泛型:对后续所有操作的类型做约束,对后续操作起作用,对之前的不起作用; 对类型进行约束; 

    父 ----> 子,从范围上,父范围小,子范围大;把范围小的给范围大的,

    JDK1.5改写了集合框架中的全部接口和类,为这些接口、类增加了“类型形参”,这个类型形参将在声明变量、创建对象时确定,即传入实际的类型,我么称为“类型实参”。我们把这个“参数化的类型”称为泛型(Generic)。

    我们可以为任何类和接口增加泛型声明,并不是只有集合类才可以使用泛型声明。

    ArrayList<E>的<E>就是类型形参,ArrayList<String>的<String>就是类型实参,String类型就是用来确定E的类型用的。

    为了区别,我们可以将int max(int a, int b)中a,b称为数据形参,将 int max = max(3,6);中3,6称为数据实参

    泛型形参的命名一般使用单个的大写字母,如果有多个类型形参,那么使用逗号分隔,例如:Map<K,V>。

    常见字母(见名知意):

    例如定义学生类的成绩score不止一个类型:

    ...    
        public static void main(String[] args) {
            
            Student<Integer> stu = new Student(1, "kris", 99);
            Student<String> stu1 = new Student(2, "smile", "优秀");
            Student<Double> stu2 = new Student(5, "aa", 89.9);
        }
    }
    
    
    class Student<T>{
        
        private int id;
        private String name;
        private T score;
    .....
    }

    可以在定义接口、类时指定类型形参,类型形参在整个接口或类体中可以当成类型使用,几乎所有可使用其他普通类型的地方都可以使用这种类型形参,例如:属性类型、方法的形参类型、方法返回值类型等。

    但是泛型类或泛型接口上的泛型形参,不能用于声明静态变量,也不能用在静态方法中,那是因为静态成员的初始化是随着类的初始化而初始化的,此时泛型实参无法指定,那么泛型形参的类型就不确定。

    当创建带泛型声明的类时,为该类定义构造器时,构造器名还是原来的类名,不需要增加泛型声明。例如:Student<T>类定义的构造器依然是Student(),而不是Student<T>,但调用构造器时却可以使用Student<T>的形式,而且应该为T类型形参传入实际的类型实参。

    泛型实参的要求

    首先泛型实参必须是引用数据类型,不能是基本数据类型,可以指定为包装类型,因为集合中只能存储对象。

    什么时候指定泛型实参?

    (1)在用泛型类、接口声明变量时

    (2)在继承泛型类或实现泛型接口时,如果子类不延续使用该泛型,那么必须明确指定实际类型。此时子类不再是泛型类了

    (3)在创建泛型类对象时

    public class EmployeeManager {
        
        private ArrayList<Employee> lis;
        public static void test(ArrayList<String> lis){
                
            ArrayList<String> list = new ArrayList<String>();
            ArrayList<String> li = new ArrayList<>();//JDK1.7之后支持如下简化写法:
        }
    }
            //继承泛型类
    class MyArrayList extends ArrayList<String>{//此处ArrayList<String>就不能写成ArrayList<E>
            //因为MyArrayList不再是泛型类了,因此E必须给出具体的类型
    }
                            //实现泛型接口
    class Employee implements Comparable<Employee>{
    
        @Override
        public int compareTo(Employee o) {
            // TODO Auto-generated method stub
            return 0;
        }
        
    }

    继承泛型类、实现泛型接口时,想要继续保留父类、父接口的泛型,必须在父类、父接口和子类、子接口中都要保留泛型。

    设定泛型形参的上限

    如果泛型形参没有设定上限,那么泛型实参可以是任意引用数据类型。如果泛型形参设定了上限(例如:T  extends  父类上限),那么只能指定为该父类本身或其各子类类型。

    在一种更极端的情况下,程序需要为形参设定多个上限(至多有一个父类上限,可以有多个接口上限)表明该类型形参必须是其父类的子类(包括是父类本身也行),并且实现多个上限接口。

    与类同时继承父类、实现接口类似的是:为类型形参指定多个上限时,所有的接口上限必须位于类上限之后。也就是说,如果需要为类型形参指定类上限,类上限必须位于第一位。

    class Student<T extends Number & java.io.Serializable> {
        //...省略其他代码
    }
    ....
          Student<Integer> stu = new Student(1, "kris", 99);
            //Student<String> stu1 = new Student(2, "smile", "优秀");//以下代码编译报错,因为String不是Number的子类
            Student<Double> stu2 = new Student(5, "aa", 89.9);
            
            ArrayList<Integer> list = new ArrayList<>();
            
    
    class Student<T extends Number>{
        
        private int id;
        private String name;
        private T score;
        public Student(int id, String name, T score) {
            super();
            this.id = id;
            this.name = name;
            this.score = score;
    ....    
    }

    定义泛型方法

    在定义类、接口时可以使用类型形参,在该类的方法和属性定义、接口的方法定义中,这些类型形参可被当成普通类型来用。但是,在另外一些情况下,(1)如果我们定义类、接口时没有使用类型形参,但是某个方法定义时,想要自己定义类型形参;(2)另外我们之前说类和接口上的类型形参是不能用于静态方法中,那么当某个静态方法想要定义类型形参。那么,JDK1.5之后,还提供了泛型方法的支持。

    * 二、泛型方法
     * 1、什么时候使用泛型方法
     * (1)因为刚才说,泛型类或泛型接口上的泛型形参是不能用于静态成员的,
     * 那么当静态方法需要用到泛型时,只能用泛型方法。
     * 
     * (2)如果泛型类或泛型接口上的泛型形参,但是对于某个方法来说,不适用,或这个类或接口本身不是泛型类和泛型接口,
     * 这个方法想要单独声明泛型,那么也得用方法方法,这个方法可以是非静态的
     * 
     * 2、如何声明
     * 【修饰符】 <泛型形参列表> 返回值类型  方法名(方法的形参列表) {
     * }
     * 泛型方法中声明的泛型形参,只能用在当前方法中。
     * 
     * 需要实现这样的一个方法,该方法负责将一个数组的所有元素添加到一个Collection集合中
     * 
     * 3、泛型方法的泛型形参是什么时候指定的
     * 调用时
     * 
     * 4、可以给泛型方法的泛型形参指定上限
     * 【修饰符】 <泛型形参   extends 父类上限> 返回值类型  方法名(方法的形参列表) {
     * }
     * 
     * 需求:接收一个集合(里面都是图形),打印所有图形的面积
    ArrayList<String> list = new ArrayList<String>();
    
    ..
    public <T extends Graphic> void print(ArrayList<T> list){
          for (T t : graphics) {
    
            System.out.println(t.getArea());
    
                }
    
          }
    String[] array = {"Hello", "java"};
            Collection<String> c = new ArrayList<String>();
            //Object[] arr =  array;//这样子是多态引用是可以的
    //Collection<Object> coll = c;//但右边ArrayList<String>,左边是不接收的;报错
            fromArrayToCollection(array, c);
            //编译报错
        
    
        public static void fromArrayToCollection(Object[]a, Collection<Object> c){
            for(Object object : a){
                c.add(object);
            }
        }
    
    //可以这样子写;//该方法负责将一个数组的所有元素添加到一个Collection集合中
        public static <T> void copy(T[] arr, Collection<T> coll){
            for (int i = 0; i < arr.length; i++) {
                coll.add(arr[i]);
                
            }
        }

    泛型擦除

    * 1、泛型的擦除:
     * 当我们使用泛型类或泛型接口时,如果没有指定泛型的实参,那么它会出现泛型擦除的现象,
     * 如果泛型形参有上限,就按照第一个上限处理,如果没有上限,就按照Object处理。
     * 
     * 2泛型类指定为不同泛型实参时,运行时是同一种类型
     * 
     * 3、instanceof后面不支持泛型类;// 由于系统中并不会真正生成泛型类
     * 
     * 4、泛型类不能创建数组;  ArrayList<String>[] array = new ArrayList<String>[5]; //编译错误; this.arr = new T[length];    
     * 
     * 5、try..catch的catch中不能使用泛型
    public class TestErase {
    
        public static void main(String[] args) {
            ArrayList list = new ArrayList<String>();//ArrayList<String>被转换为了ArrayList
            list.add("kris");
            list.add("smile");
            
            for (Object object : list) {
                System.out.println(object);
            }
            
            Object object = list.get(1); //泛型被擦除,按照默认上限Object处理
            
            Student s = new Student("kk", 99); //Student<Number>
            
            Number score = s.getScore();//泛型被擦除,按照第一个上限Number处理
            
        }
    
    }
    
    class Student<T extends Number>{
        private String name;
        private T score;
        public Student(String name, T score) {
            super();
            this.name = name;
            this.score = score;
        }
        public T getScore() {
            return score;
        }
    
    
    }
    View Code
     public static void main(String[] args) {
            
            ArrayList<String> list1 = new ArrayList<>(); 
            ArrayList<Integer> list2 = new ArrayList<>();
            System.out.println(list1.getClass());//class java.util.ArrayList
            System.out.println(list2.getClass());//class java.util.ArrayList
            System.out.println(list1.getClass() == list2.getClass());//true
            
            ArrayList<Object> list = new ArrayList<String>();   //错误的
        
            /*ArrayList<String>不是ArrayList<Object>的子类,
            因为他们的运行时类型都是ArrayList,因此不允许如下赋值操作。
            这点和数组不同,因为数组是要生成新的Class对象的,String[]仍然是Object[]的子类,因此允许如下赋值操作。
            Object[] arr = new String[5];*/
    
        }
        

    通配符

    * 通配符:?
     * 1、类型通配符只能用于方法中,不能用在泛型类或泛型接口的声明上。
     * 2、形式:
     * (1) <?> 代表的是任意类型
     * (2)<? extends 上限Upper> 代表的是Upper的类型或它的子类
     * (3)<? super 下限Lower> 代表的是Lower的类或它的父类

    当我们声明一个方法时,某个形参的类型是一个泛型类或泛型接口类型,但是在声明方法时,又不确定该泛型实际类型,我们可以考虑使用类型通配符。

               
                    //使用类型通配符
        public static void test(List<?> c){ //List c这种形式使用List接口时没有传入实际类型参数,这将引起泛型警告。
            for (int i = 0; i < c.size(); i++) {//List<Object> c这种太局限了,调用时,只能传List<Object>,List<Object>不能接收List<String>等其他集合
                System.out.println(c.get(i));
            }
        }
                    //声明一个泛型方法,需要声明泛型形参T。
        public static <T> void test2(List<T> c){  
            for (int i = 0; i < c.size(); i++) {
                System.out.println(c.get(i));
            }
        }

    通配符的List仅表示它可以接受指定了任意泛型实参的List,并不能把元素加入其中,例如如下代码将会引起编译错误:

    public static void test(List<?> c, String str){
            c.add(str);
        }

    不知道上面程序中c集合里元素的类型,所以不能向其中添加对象,除了null对象,因为它是所有引用数据类型的实例。

    test2方法带泛型的List,表示该集合的元素类型是T,因此允许T系列的对象加入其中,例如如下代码是可行的:

    public static <T> void test(List<T> c, T t){
            c.add(t);
        }

    如果不涉及添加元素到带泛型的集合中,那么两种方式都可以,如果涉及到添加元素到带泛型的集合中,使用类型通配符<?>的不支持。

    当直接使用List<?>这种形式时,即表明这个List集合接收泛型实参指定为任意类型的List。

    有时候我们只希望接收某些类型的List。

    例如:一个图形的抽象父类Graphic,两个子类Circle和Rectangle。接下来我们想定义一个方法,可以打印不同图形的面积。

    但是,List<Graphic>的形参只能接收List<Graphic>的实参,如果想要接收List<Circle>,List<Rectangle>的集合,可以使用List<?>。

    但是这样有两个问题,一个是List<?>可以接收任意类型,不仅仅图形,第二个是需要强制类型转换。为了解决这个问题,Java允许设定通配符的上限:<? extends Type>,这个通配符表示它必须是Type本身,或是Type的子类。

    public static void printArea(List<? extends Graphic> graphic){
            for (Graphic g : graphic) {
                System.out.println(g.getArea());
            }
        }
        /*与前面的完全相同,因为我们不知道这个受限制的通配符的具体类型,所以我们不能把Graphic对象或其子类对象加入这个泛型集合中。
        如果要需要将Graphic对象或其子类对象加入这个泛型集合,那么就只能用泛型方法了,*/
        
        public static void printArea(List<? extends Graphic> graphics){
            graphics.add(new Circle());//编译错误,因为不知道?的具体类型,也可能是Rectangle
        }

    返回的T是Object,也就是说,程序在复制集合元素的过程中,丢失了src集合元素的类型String。

    对于上面的copy方法,可以这样理解两个集合参数之间的依赖关系:不管src集合元素的类型是什么,只要dest集合元素类型与src的元素类相同或是它的父类即可。为了表示这种约束关系,Java允许设定通配符的下限:<? super Type>,这个通配符表示它必须是Type本身或是Type的父类。

    public static void main(String[] args) {
            ArrayList<String> src = new ArrayList<String>();
            src.add("kris");  //src -- >dest
            ArrayList<Object> dest = new ArrayList<Object>();
        
            String last = copy(dest, src);
            System.out.println(last);
            
        }
            
    /*    实现将src集合里元素复制到dest集合中的功能,因为dest集合需要接受src的所有元素,
        所以dest集合元素的类型应该是src集合元素的父类。
        为了表示两个参数之间的类型依赖,考虑同时使用通配符、泛型形参类实现该方法。*/
    /*    public static <T> void copy (Collection<T> dest, Collection <? extends T> src){
            for (T t : src) {
                dest.add(t);
            }
        }*/
        public static <T> T copy (Collection<? super T> dest, Collection <? extends T> src){
            T last = null;
            for (T t : src) {
                dest.add(t);
            }
            return last;
        }
  • 相关阅读:
    java中字符串类型的比较
    iOS 检测是否插入耳机
    Model-View-Controller (The iPhone Developer's Cookbook)
    Spring Animation
    CoreImage 自动增强滤镜 以及 系统滤镜查询
    UIView Animation
    CoreImage 查询系统滤镜
    CoreImage 的人脸检测
    Smarty 模板操作
    smarty转载(1)
  • 原文地址:https://www.cnblogs.com/shengyang17/p/10354805.html
Copyright © 2011-2022 走看看