• java泛型


    引言

    泛型是指参数化类型的能力,可以定义泛型类型的类、接口或方法,随后编译器会用具体的类型来替换它

    使用泛型的主要优点是:能够在编译时而不是在运行时检测错误

    package java.lang; 
    public interface Comparable { //JDK1.5之前
        public int compareTo(Object o);
    }
    
    package java.lang; 
    public interface Comparable<T> { //JDK1.5之后
        public int compareTo(T o);
    }

    新泛型类型在编译时检测到可能的错误:

    Comparable c = new Date();
    System.out.println(c.compareTo("red"));
    
    Comparable<Date> c = new Date();
    System.out.println(c.compareTo("red"));

    泛型类型必须是引用类型,不能用像int、double或char这样的基本类型来替换泛型类型:

    ArrayList<int> intlist = new ArrayList<int>(); //error

    必须使用:

    ArrayList<Integer> intList = new ArrayList<Integer>();

    java会自动打包操作:

    ArrayList<Integer> intList = new ArrayList<Integer>();
    intList.add(5); //java会自动地将5包装为new Integer(5)

    定义泛型类和接口

    下面看一个泛型类的例子:

    public class GenericStack<E> {
        ArrayList<E> list = new ArrayList<E>();
        
        public int getSize() {
            return list.size();
        }
        
        public E peek() {
            return list.get(getSize() - 1);
        }
        
        public void push(E o) {
            list.add(o);
        }
        
        public E pop() {
            E o = list.get(getSize() - 1);
            list.remove(getSize() - 1);
            return o;
        }
        public boolean isEmpty() {
            return list.isEmpty();
        }
    }
    View Code

    向栈中添加元素:

    GenericStack<String> stack1 = new GenericStack<String>();
    stack1.push("London");
    stack1.push("Paris");
    stack1.push("Berlin");
    GenericStack<Integer> stack2 = new GenericStack<Integer>();
    stack2.push(1);
    stack2.push(2);
    stack2.push(3);

    定义泛型方法

    public static void main(String args[])
    {
        Integer[] integers = {1, 2, 3, 4, 5};
        String[] strings = {"London", "Paris", "New York", "Austin"};
        print(integers);
        print(strings);
    }
        
    public static <E> void print(E[] list) {
        for(int i = 0; i < list.length; i++)
            System.out.print(list[i] + " ");
            System.out.println();
    }

    通配泛型

    下面的例子说明了为什么要使用通配泛型:

    public class Main
    {
        public static void main(String args[])
        {
            GenericStack<Integer> intStack = new GenericStack<Integer>();
            intStack.push(1);
            intStack.push(2);
            intStack.push(-2);
            System.out.print("The max number is " + max(intStack));
        }
        
        public static double max(GenericStack<Number> stack) {
            double max = stack.pop().doubleValue();
            
            while(!stack.isEmpty()) {
                double value = stack.pop().doubleValue();
                if(value > max)
                    max = value;
            }
            return max;
        }
            
    }

    标记行会产生编译错误,因为intStack不是GenericStack<Number>的实例,尽管Integer是Number的子类型,但是GenericStack<Integer> 并不是 GenericStack<Number> 的子类型,为了避免这个问题,可以使用通配泛型类型 ,通配泛型类型有三种形式:

    非受限通配:?  (它和? extends Object是一样的)

    受限通配:? extends T  (表示T或T的一个未知子类型)

    下限通配: ? super T   (表示T或T的一个未知父类型)

    使用受限通配修改后的代码如下:

    public class Main
    {
        public static void main(String args[])
        {
            GenericStack<Integer> intStack = new GenericStack<Integer>();
            intStack.push(1);
            intStack.push(2);
            intStack.push(-2);
            System.out.print("The max number is " + max(intStack));
        }
        
        public static double max(GenericStack<? extends Number> stack) {
            double max = stack.pop().doubleValue();
            
            while(!stack.isEmpty()) {
                double value = stack.pop().doubleValue();
                if(value > max)
                    max = value;
            }
            return max;
        }
            
    }

    再看下面的一个例子:

    public class Main
    {
        public static void main(String args[])
        {
            GenericStack<Integer> intStack = new GenericStack<Integer>();
            intStack.push(1);
            intStack.push(2);
            intStack.push(-2);
            print(intStack);
        }
        public static void print(GenericStack<?> stack) {
            while(!stack.isEmpty()) {
                System.out.print(stack.pop() + " ");
            }
        }    
    }

    什么时候使用下限通配?看下面的例子:

    public class Main
    {
        public static void main(String args[])
        {
            GenericStack<String> stack1 = new GenericStack<String>();
            GenericStack<Object> stack2 = new GenericStack<Object>();
            stack2.push("Java");
            stack2.push(2);
            stack1.push("Sun");
            add(stack1, stack2);
            print(stack2);
        }
        
        public static<T> void add(GenericStack<T> stack1,
                GenericStack<? super T> stack2) {
            while(!stack1.isEmpty()) {
                stack2.push(stack1.pop());
            }
        }
        
        public static void print(GenericStack<?> stack) {
            while(!stack.isEmpty()) {
                System.out.print(stack.pop() + " ");
            }
        }    
    }

    消除泛型和对泛型的限制

    泛型是一种称为类型消除的方法实现的,泛型信息在运行时是不可用的,这种方法可以使泛型代码向后兼容使用原始类型的遗留代码

    泛型存在于编译时。一旦编译器确认泛型类型是安全使用的,就将它转换为原始类型

    例如下面的代码:

    ArrayList<String> list = new ArrayList<String>();
    list.add("Oklahoma");
    String state = list.get(0);

    被翻译成如下的代码的原始类型:

    ArrayList list = new ArrayList();
    list.add("Oklahoma");
    String state = (String)list.get(0);

    使用泛型类型是有一些限制的:

    限制1、不能使用new E()

    不能使用泛型类型参数创建实例: E object = new E()

    限制2、不能使用new E()

    不能使用泛型类型参数创建数组,如:E[] elements = new E[capacity];

    可以通过创建一个Object类型的数组,然后将它的类型转化为E[]来规避这个限制,如:E[] elements = (E[]) new Object[capacity];

    限制3、在静态环境中不允许类的参数是泛型类型

    由于泛型类的所有实例都有相同的运行时类,所以泛型类的静态变量和方法是被它的所有实例所共享的,下面的代码非法:

    public calss Test<E> {
        public  static Void m(E o1) {
        }
        public static E o1;
        static {
            E o2;
        }
    }

    限制4、异常类不能是泛型的

  • 相关阅读:
    腾讯企业微信机器人通知
    python 协程、异步、async
    pytest获取测试用例执行结果(钩子函数:pytest_runtest_makereport)
    pytest hook使用
    sqlalchemy backref解释
    docker 启动mysql 并本地连接
    Flask用paginate实现数据分页
    在flask中使用flaskmigrate管理数据库
    ExtAspNet v2.3.5
    Caliburn Micro下轻松实现ListView的全选功能
  • 原文地址:https://www.cnblogs.com/bigjava/p/3901819.html
Copyright © 2020-2023  润新知