• Java不可不知的泛型使用


    前面的文章:

    本文介绍了Java的泛型的基本使用。

    1. 为什么使用泛型

    看下面一个例子:

    为了说明问题,本类写的尽量简陋,请把目光主要放在类型上。

    /**
     * @author Xing Xiaoguan (xingrenguanxue)
     */
    
    public class MyArrayList {
        private int[] elementData;
        private int size = 0;
    
        public MyArrayList(int capacity) {
            elementData = new int[capacity];
        }
        
    	//向数组中添加元素
        public void add(int i) { 
            if (size == elementData.length) {
                throw new IndexOutOfBoundsException("数组已满");
            }
            elementData[size++] = i;
        }
        
    	//从数组中根据下标获取元素
        public int get(int index) { 
            if (index < 0 || index > size - 1) {
                throw new IndexOutOfBoundsException("超出范围");
            }
            return elementData[index];
        }
    
        @Override
        public String toString() {
            return "MyArrayList{" +
                    "elementData=" + Arrays.toString(elementData) +
                    '}';
        }
    }
    

    该类很简单:有两个成员变量,elementData是一个数组,size是数组中元素的数量。addget方法能添加和获取元素。

    下面测试一下:

    public class Test {
        public static void main(String[] args) {
            MyArrayList myArrayList = new MyArrayList(4);
            myArrayList.add(111); //向数组中添加3个int元素
            myArrayList.add(222);
            myArrayList.add(333);
            int i = myArrayList.get(0); //获取
            System.out.println(i);
    		//以上正常运行
            myArrayList.add("行小观"); //添加一个String元素,类型不匹配,报错
        }
    }
    

    向数组中添加3个int类型的元素并能获取,这没问题。

    但是如果我们的场景不再需要int类型的元素,而是需要String类型的,那怎么办?

    很显然,继续使用该类会报错,报错的原因很简单:我们向数组中添加的元素是String类型的,而数组和方法参数类型是int类型。

    此时,就得需要再写一份代码,该份代码较之前的并无大修改,只是把int改为String。如果场景继续变怎么办?那就再写一份新代码!

    这样太麻烦了!有没有解决办法?有!

    我们知道,Object类是所有类的父类,Object类型的变量能够引用任何类型的对象。所以可以将类型改为Object

    /**
     * @author Xing Xiaoguan (xingrenguanxue)
     */
    
    public class MyArrayList {
        private Object[] elementData; 
        private int size = 0;
    
        public MyArrayList(int capacity) {
            elementData = new Object[capacity];
        }
    
        public void add(Object o) { //向数组中添加元素
            if (size == elementData.length) {
                throw new IndexOutOfBoundsException("数组已满");
            }
            elementData[size++] = o;
        }
    
        public Object get(int index) { //从数组中获取元素
            if (index < 0 || index > size - 1) {
                throw new IndexOutOfBoundsException("超出范围");
            }
            return elementData[index];
        }
    
        @Override
        public String toString() {
            return "MyArrayList{" +
                    "elementData=" + Arrays.toString(elementData) +
                    '}';
        }
    }
    

    再测试一下:

    public class Test {
        public static void main(String[] args) {
            //myArrayList 给int元素使用
            MyArrayList myArrayList = new MyArrayList(4);
            myArrayList.add(111); //向数组中添加3个int元素
            myArrayList.add(222);
            myArrayList.add(333);
    
            int i = (int) myArrayList.get(0); //获取
            System.out.println(i);
    
            //myArrayList 给String元素使用
            MyArrayList myArrayList1 = new MyArrayList(4);
            myArrayList1.add("aaa");
            myArrayList1.add("bbb");
            myArrayList1.add("ccc");
    
            String str = (String) myArrayList1.get(1);
            System.out.println(str);
        }
    }
    

    发现可以向数组中添加和获取intString类型的元素,这证明该类的数组和方法同时对各种类型的数据都有用,不必再添加额外代码。

    但是这样又出现了两个问题:

    第一:从数组中获取元素时,需要强制转换类型才行。

    int i = (int) myArrayList.get(0); 
    

    第二:同一个数组可以添加各种类型的元素。

    myArrayList.add(111); //int类型
    myArrayList.add("222"); //String类型
    myArrayList.add(true); //布尔类型
    

    这就导致了当我们从数组中获取某个元素时,很难知道它的确切类型,往往会强转类型失败。

    int i = (int)myArrayList.get(1); //本来是String类型的值,但我提前不知道,拿int变量接收,报错
    

    那这个问题有没有解决办法呢?

    有!用泛型!

    2. 泛型类

    使用泛型改造MyArrayList

    /**
     * @author Xing Xiaoguan (xingrenguanxue)
     */
    
    public class MyArrayList <T> {
        private T[] elementData;
        private int size = 0;
    
        public MyArrayList(int capacity) {
            elementData = (T[]) new Object[capacity];
        }
    
        public void add(T o) { //向数组中添加元素
            if (size == elementData.length) {
                throw new IndexOutOfBoundsException("数组已满");
            }
            elementData[size++] = o;
        }
    
        public T get(int index) { //从数组中获取元素
            if (index < 0 || index > size - 1) {
                throw new IndexOutOfBoundsException("超出范围");
            }
            return elementData[index];
        }
    
        @Override
        public String toString() {
            return "MyArrayList{" +
                    "elementData=" + Arrays.toString(elementData) +
                    '}';
        }
    }
    

    测试:

    public class Test {
        public static void main(String[] args) {
            //myArrayList 给int元素使用
            MyArrayList<Integer> myArrayList = new MyArrayList<>(4);
            myArrayList.add(111); //向数组中添加3个int元素
    //        myArrayList.add("222"); //添加非Integer元素报错
    
            int i = myArrayList.get(1); //无需强制转型
            System.out.println(i);  
        }
    }
    

    经过改造,我们把MyArrayList类改为了一个泛型类,它是一个具有多个类型变量的类。

    泛型类的声明方式:引入一个类型变量,如T,然后使用<>将其括起来,放在类名后。

    public class MyArrayList <T> {
        //......
    }
    

    如何理解类型变量?它就类似于数学中函数中的变量x,用来代替具体的值:

    f(x) = 3x + 1

    类型变量的名称可以随便取,一般使用大写字母表示,比如E、K、V、T等。

    泛型类中的成员变量、方法参数和返回值的类型都使用类型变量代替:

    private T[] elementData;
    
    public void add(T o) {
        //.......
    }
    
    public T get(int index) {
    	//......
    }
    

    当然,一个泛型类可以有多个类型变量:

    public class MyClass <K, V> {
        //......
    }
    

    当我们需要实例化泛型类时,就使用具体的类型来替换类型变量(T):

    MyArrayList<Integer> myArrayList = new MyArrayList<>(4);
    

    该过程就相当于向数学函数中代入数值:

    f(3) = 3*3+1 = 10

    3. 泛型方法

    当我们声明了一个泛型类后,可以很自然地在类内部使用泛型方法。

    其实,当类是普通类时,我们仍然能够使用泛型方法。下面是一个例子:

    /**
     * @author Xing Xiaoguan (xingrenguanxue)
     */
    
    public class PrinterVar {
    
        //该方法接收一个T类型的变量,打印并返回该变量
        public <T> T print(T var) {
            System.out.println(var);
            return var;
        }
    
        public static void main(String[] args) {
            PrinterVar printerVar = new PrinterVar();
            String var = printerVar.print("aa");//String类型
            Integer var1 = printerVar.print(12);//int类型
            System.out.println(var + " " + var1);
        }
    }
    

    4. 关于我

    点击这里认识我 。 (^o^)/

  • 相关阅读:
    【Java基础】static关键字相关
    【Java基础】foreach循环
    【Java基础】可变参数
    Android的启动模式(下)
    Android的启动模式(上)
    Universal-Image-Loader完全解析(下)
    Universal-Image-Loader完全解析(上)
    布局优化之ViewStub、Include、merge使用分析
    人在千锋--网络学习之开发项目爱限免
    4_2网络学习第二天--XML解析
  • 原文地址:https://www.cnblogs.com/xingrenguanxue/p/13453775.html
Copyright © 2020-2023  润新知