zoukankan      html  css  js  c++  java
  • 泛型的由来

    泛型设计源于我们的编写类时的一个刚需:想让我们编写的处理类能够更加"通用", 而不是只能处理某些特定的对象或场景。或者说:我们希望我们的类能实现尽可能多的复用。举个栗子:一般来说,你并不想要编写多个分别处理不同数据类型,但内在逻辑代码却完全一样的类。因为这些处理类可能除了数据类型变换了一下外,所有代码都完全一致。“只要写一个模板类就OK了嘛~ 等要使用的时候再传入具体的类型,多省心”, 当你这么思考的时候:浮现在你脑海里的,就是泛型程序设计(Generic pogramming)的思想

    在介绍Java的泛型机制之前, 先让我们来看看, 还没加入泛型机制的“泛型程序设计”是怎样子的

    泛型程序设计1.0: 不用Java泛型机制

    下面我们编写一个存储不同的对象的列表类,列表有设置(set)和取值(get)两种操作。
    假设这个列表类为ObjArray,同时尝试存储的值为String类型,则:
    1.在ObjArray类里我们维护一个数组arr, 为了将来能容纳不同的对象, 将对象设为Object类型(所有对象的父类)
    2.在实例化ObjArray后, 通过调用set方法将String存入Object类型的数组中; 而在调用get方法时, 要对取得的值做强制类型转换—从Object类型转为String类型


    ObjArray.java:

    public class ObjArray  {
      private Object [] arr;
      public ObjArray(int n) {
        this.arr = new Object[n];
      }
      public void set (int i, Object o) {
        this.arr[i] = o;
      }
      public Object get (int i) {
        return this.arr[i];
      }
    }


    Test.java:

    /**
     * @description: 测试代码
     */
    public class Test {
      public static void main (String args[]) {
        ObjArray arr = new ObjArray(3);
        arr.set(0, "彭湖湾");
        // get操作时要做强制类型转换
        String n =(String)arr.get(0);
        // 输出 "彭湖湾"
        System.out.print(n);
      }
    }

    如果不使用泛型机制,但又想要实现泛型程序设计,就会编写出类似这样的代码。

    泛型程序设计2.0: 使用Java泛型机制

    让我们来看看使用泛型机制改进后的结果。
    看起来大约是这样:


    GenericArray.java

    public class GenericArray<T>  {
      public void set (int i, T o) {
        // ...
      }
      public T get (int i) {
        // ...
      }
    }

    【具体代码下面给出】

    Test.java:

    public class Test {
      public static void main (String args[]) {
        GenericArray<String> arr = new <String>GenericArray(3);
        arr.set(0, "彭湖湾");
        // 不用做强制类型转换啦~~
        String s =arr.get(0);
        // 输出: 彭湖湾
        System.out.print(s);
      }
    }

    我们发现,改进后的设计有以下几点好处:

    1. 规范、简化了编码: 我们不用在每次get操作时候都要做强制类型转换了
    2. 良好的可读性:GenericArray<String> arr这一声明能清晰地看出GenericArray中存储的数据类型
    3. 安全性:使用了泛型机制后,编译器能在set操作中检测传入的参数是否为T类型, 同时检测get操作中返回值是否为T类型,如果不通过则编译报错

    泛型并非无所不能

    了解到了泛型的这些特性后, 也许你会迫不及待地想要在ObjArray类里大干一场。
    例如像下面这样, 用类型参数T去直接实例化一个对象, 或者是实例化一个泛型数组

    可惜的是 ......

    public class GenericArray<T>  {
      private T obj = new T (); // 编译报错
      private T [] arr = new T[3]; // 编译报错
      // ...
    }

    没错, 泛型并不是无所不能的, 相反, 它的作用机制受到种种条框的限制。

    这里先列举泛型机制的两个限制:

    1.不能实例化类型变量, 如T obj = new T ();

    2. 不能实例化泛型数组,如T [] arr = new T[3];

    【注意】这里不合法仅仅指实例化操作(new), 声明是允许的, 例如T [] arr

    我们现在来继续看看上面泛型设计中, GenericArray类的那部分代码:

    没错, 泛型并不是无所不能的, 相反, 它的作用机制受到种种条框的限制。
    
    这里先列举泛型机制的两个限制:
    
    1.不能实例化类型变量, 如T obj = new T ();
    
    2. 不能实例化泛型数组,如T [] arr = new T[3];
    
    【注意】这里不合法仅仅指实例化操作(new), 声明是允许的, 例如T [] arr
    
     
    
    我们现在来继续看看上面泛型设计中, GenericArray类的那部分代码:

    没错, 在ObjArray类内部我们仍然还是用到了强制转型。看到这里也许令人有那么一点点的小失望, 毕竟还是没有完全跳出
    初始的泛型设计的边界。 但是, 泛型的优点仍然是显而易见的, 只不过要知道的是:它并没有无所不能的魔法, 并受到诸多限制。

    泛型的编写规则

    1.泛型类和泛型方法的定义

    泛型类
    如前面所说,可以像下面一样定义一个泛型类
    类型变量T放在类名的后面

    public class Foo <T> {
      // 约定实例变量的类型
      private T data;
      // 约定返回值的类型
      public T getData () {
        return this.data;
      }
      // 约定传入参数的类型
      public void setData (T data) {
        this.data = data;
      }
    }

    泛型方法
    也可以定义一个泛型方法:

    泛型变量T放在修饰符(这里是public static)的后面, 返回类型的前面

    public class Foo {
      public static <T> T getSelf (T a) {
        return a;
      }
    }

    泛型方法可以定义在泛型类当中,也可以定义在一个普通类当中

    2.可以使用多个类型变量

    public class Foo<T, U> {
      private T a;
      private U b;
    }

    【注意】在Java库中,常使用E表示集合的元素类型, K和V分别表示关键字和值的类型, T(U,S)表示任意类型

    3.JavaSE7以后,在实例化一个泛型类对象时,构造函数中可以省略泛型类型

    ObjArray<Node> arr = new <Node>ObjArray();

    可简写成:

    ObjArray<Node> arr = new <>ObjArray();
  • 相关阅读:
    Google搜索引擎如何运作:不会手动调整结果
    一个Ruby脚本
    IE灭绝!?
    除Windows之外的其他操作系统
    刚收到的新书
    奇怪的计算机语言
    小巧的menuetOS
    Ruby学习笔记(1)
    一个通知
    总结 asp.net 和 javascript获取本地IP(MAC)和服务器IP(MAC)的方法
  • 原文地址:https://www.cnblogs.com/coder-ahao/p/14219528.html
Copyright © 2011-2022 走看看