美文网首页
夯实JAVA基础之 - 泛型

夯实JAVA基础之 - 泛型

作者: xue57233 | 来源:发表于2017-08-08 17:16 被阅读31次

    泛型的定义及使用

    1. 定义泛型:
    Student<T>
    
    2. 类中使用泛型
    Student<T> 这个 T 表示派生自object的任何类,比如 string、 intenger、 Double 等。 但是要注意的是, T 一定要
    是派生自 object 类。
    
    Class Student<T> {
        private T name ;
    
        public T getName(){
            return name;
        }
    
        public void setName(T name){
            this.name = name;
        }
    }
    
    3. 使用泛型类
    // 构造实例
    Student<String> s = new Student<String>();
    s.setName("张三");
    system.out.printin(s.getName());
    

    4. 使用泛型的优势?

    I.  为什么不用 object 代替泛型?就是因为强制转换会出现意想不到的错误。
    II. 在编译期就能报错。
    

    多泛型变量的定义及字母规范

    1. 多泛型变量定义
    上面我们只定义了一个泛型变量,那如果需要多个变量怎么办?  只需要类似下面这样就行了。
    新加的变量和上例中的 T 用法一样, 这样的变量想加几个加几个。只要用 "," 隔开就行。
    
    class Student<T,E>{
        Private T name;
        Private E age;
        ......
    }
    
    class Student<T,E,U,K,V>{}
    

    2. 字母规范

    指定泛型的可以是任意一个大写字母,咩有特定的含义。 但是为了提高可读性,大家还是用比较有意义的字母为好。

    • E, Element 常用在 collection 中,如: List<E>, iterator<E>, Set<E>
    • K,V key-value 键值对
    • N, Number 数字
    • T, Type 类型,如 string integer 等

    泛型接口的定义和使用

    • 非泛型类
    • 泛型类

    泛型函数的定义和使用

    不管是静态还是非静态函数,不管是有没有返回值,都在返回值类型前加符号<T> ,以用来标识泛型。

    • 静态泛型函数
    • 非静态泛型函数
    public class StaticFans {
        //静态函数
        public static <T> void StaticMethod(T a){
            Log.d("harvic","StaticMethod: "+a.toString());
        }
        //普通函数
        public <T> void OtherMethod(T a){
            Log.d("harvic","OtherMethod: "+a.toString());
        }
        // 有返回值的泛型函数
        public static <T> List<T> parseArray(String response,Class<T> object){
            List<T> modelList = JSON.parseArray(response, object);
            return modelList;
        }
    
    }
    

    其他用法:Class<T> 类传递及泛型数组

    Class<T> 其实也是一种泛型,用来装载 class 对象的。

    public final class Class<T> implements Serializable {  
       …………  
    }  
    

    泛型高级知识: 类型绑定和通配符

    泛型的类型绑定: extends

    1. <T extends BoundingType> 就是给泛型参数加一个界限。
    2. 此时的 extends 不等同于 继承。和继承没有任何联系。
    3. BoundingType 可以是类,也可以是接口。 T 代表的类型是被包含的意思。具有 BoundingType的功能。
    4. 能提前调用 T 的父类或父接口中的方法。
    • 绑定接口
      public interface Comparable<T> {
          boolean compareto(T i);
      }
      
      public class StringCompare implements Comparable<StringCompare> {
          String mStr;
          public StringCompare(String string) {
              this.mStr = string;
          }
          
          @Override
          public boolean compareto(StringCompare i) {
              if (mStr == null) {
                  throw new NullPointerException();
              }
              if (mStr.length() > i.mStr.length()) {
                  return true;
              }
              return false;
          }
      }
      
      public static <T extends Comparable> T min(T... a) {
          T smallest = a[0];
          for (T item : a) {
              if (smallest.compareto(item)) {
                  smallest = item;
              }
          }
      }
      
      // 调用时候
      // 可以看出类型绑定有两个作用:
      // 1、对填充的泛型加以限定 
      // 2、使用泛型变量T时,可以使用BoundingType内部的函数。
      
      public static <T extends Comparable> T min(T... a) {
          T smallest = a[0];
          for (T item : a) {
              if (smallest.compareto(item)) {
                  smallest = item;
              }
          }
          return smallest;
      }
      
      StringCompare result = min(new StringCompare("1"),new StringCompare("123"));
      Log.e("xyd","result = " + result.mStr);
         
      
    • 绑定类
      class Fruit{
          private String name;
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      }
      
      public static <T extends Fruit> String getFruitName(T t){
          return t.getName();
      }
      
      class Banana extends Fruit{
          public Banana() {
              setName("banana");
          }
      }
      
      class Apple extends Fruit{
          public Apple() {
              setName("apple");
          }
      }
      // 最后使用;
      Log.e("xyd","FruitName = " + getFruitName(new Banana()));
      Log.e("xyd","FruitName = " + getFruitName(new Apple()));
      
    • 绑定多个绑定,用 & 连接
      public static <T extends Fruit&Serializable> String getFruitName(T t){
          ...
      }
      加深难度,如果有多个泛型,每个泛型都带绑定?
      public static <T extends Comparable & Serializable, U extends Runnable> T foo(T a, U b){
          ...
      }
      

    通配符

    无边界通配符
    • 无边界通配符初识
      有没有办法,只生成一个变量,可以将不同的实例赋值给它呢?
      
      首先定义定义一个泛型类:
      class Point<T>{
          private T x;
          private T y;
      
          public Point() {
          }
      
          public Point(T x, T y) {
              this.x = x;
              this.y = y;
          }
      
          public T getX() {
              return x;
          }
      
          public void setX(T x) {
              this.x = x;
          }
      
          public T getY() {
              return y;
          }
      
          public void setY(T y) {
              this.y = y;
          }
      }
      
      Point<?> point;
      point = new Point<Integer>(3,3);
      point = new Point<Float>(2.3f,5.6f);
      point = new Point<Double>(6.3d,46.5d);
      point = new Point<Long>(10l,58l);
      这里的 ? 就是无边界通配符。就是一个任意的类,一个未知的符号。
      point = new Point<String>("","");
      point = new Point<Object>();
      不光能将 T 填充为数值类,任意的 Object 的子类都可以匹配给通配符。
      
    • 通配符 ? 和 T 的区别
      他们俩没有任何联系;
      泛型变量T不能在代码中用于创建变量,只能在类、接口、函数中声明后使用。
      无界通配符只能用于填充泛型变量T,表示通配任何类型。它是用来填充 T 的,只是填充方式的一种!!
      
      // 无边界通配符填充
      Box<?> box;
      // 其他类型填充
      Box<String> stringBox;
      
      
    • 通配符只能用于填充泛型变量T,不能用于定义变量
      只能用来填充变量T的位置,不能用于定义变量。通配符的使用位置只有:
      Box<?> box;
      box = new Box<String>();
      
      而不能用于定义变量:
      box = new Box<?>(); // 错误
      ? x;                // 错误
      

    通配符 ? 的 extends 绑定 // 上边界限定通配符

    通配符?可以代表任意类型,但跟泛型一样,如果不加以限定,在后期的使用中编译器可能不会报错。所以我们同样要对通配符加以限定。
    绑定的形式,同样是通过extends 关键字,意义和使用方法都和泛型变量一致。
    为了保证泛型类有意义,需要对泛型类的参数做限制,不能超出限制之外。
    如有的参数只有是数字类型才有意义。

    Point<? extends Number> point;
    point = new Point<Number>();
    point = new Point<Float>(3.4f,4.3f);
    point = new Point<Double>(3.4d,4.3d);
    point = new Point<Long>(3.4l,4.3l);
    point = new Point<String>("","");       //  报错
    point = new Point<Object>();            //  报错
    
    //----
    编译时候,最后两行会报错。
    new Point<Number>(); 不会报错,说明无边界通配符的extends绑定包括边界自身。
    无边界通配符只是泛型T的填充方式,给他加上限定,只是限定了赋值给它的实例类型。
    如果想从根本上解决乱填充Point的问题,需要从 Point 泛型类定义时候就加上 <T extends Number>
    

    注意: 利用<? extends Number> 定义的变量,只可取其中的值,不可修改。

    Point<? extends Number> point;
    point = new Point<Integer>(3,33);
    Number Integer_x = point.getX();
    point.setX(new Integer(222));       // 报错
    
    // 为什么会报错???
    因为 Point 的类型是由 Point<? extends Number> 决定的,并不会因为 point = new Point<Integer>(3,33)而改变类型。
    即便 point = new Point<Integer>(3,3) 之后, point 的类型依然是 Point<? extends Number>(),
    即派生自 Number 的未知类型!!! 
    怎么理解? 如果在 point = new Point<Integer>(3,3) 之后, point 就变成了 Poin<Integer> 类,那后面
    point = new Point<Long>(2l,22l); 肯定会因为类型不匹配而报编译错误了。正因为 point 的类型始终是 
    Point<? extends Number> 因此能继续被各种类型的实例赋值。
    
    继续正题,为什么会报错?
    point 的类型为 Point<? extends Number> , 那就是说填充 point 泛型变量 T 的是 <? extends Number>,
    这是一个什么类型? 未知类型。怎么可能用一个未知类型来给设置内部值! 这是不合理的。
    但是取值时, 正由于泛型变量T被填充为<? extends Number> 所以编译器能确定 T 肯定是 Number 的子类。编译器就会用 Number 来填充 T。
    
    也就是说,编译器,只要能确定通配符类型,就会允许,如果无法确定通配符的类型,就会报错。
    

    通配符 ?的 supper 绑定 // 下边界限定通配符

    总结:
    通配符的使用可以对泛型参数做出某些限制,使的代码更安全,对于上边界和下边界限定的通配符总结如下:

    • 使用 List<? extends C> list 这种形式,表示 list 可以引用一个 ArrayList ( 或者其它 List 的 子类 ) 的对象,这个对象包含的元素类型是 C 的子类型 ( 包含 C 本身)的一种。
    • 使用 List<? super C> list 这种形式,表示 list 可以引用一个 ArrayList ( 或者其它 List 的 子类 ) 的对象,这个对象包含的元素就类型是 C 的超类型 ( 包含 C 本身 ) 的一种。

    参考资料:
    Java 泛型总结(一):基本用法与类型擦除
    Java 泛型总结(二):泛型与数组
    Java 泛型总结(三):通配符的使用
    夯实JAVA基本之一 —— 泛型详解(1):基本使用
    夯实JAVA基本之一——泛型详解(2):高级进阶

    什么是协变?为什么数组是支持协变的?为什么泛型不支持协变?
    参考知乎 - 胖胖 的答案

    相关文章

      网友评论

          本文标题:夯实JAVA基础之 - 泛型

          本文链接:https://www.haomeiwen.com/subject/rhxxrxtx.html