zoukankan      html  css  js  c++  java
  • Java泛型的基本介绍与使用

    为什么要使用泛型?

      在Java中增加泛型之前,泛型程序设计是用继承来实现的,例如ArrayList,只维护Object引用的数组:

    1 public class ArrayList{
    2   private Object[] elementData;
    3 
    4   public Object get(int i){...}
    5   public void add(Object o){...}        
    6 }

      在这里有一个问题,就是每次使用的时候,都必须进行一次强制类型转换

    1 ArrayList list = new ArrayList();
    2 ...
    3 String name = (String)list.get(0);

      可以发现还没有错误检查,如果是其他类型会检测不到,当添加的时候,可以是任何类的对象

      每次都转换一次很麻烦,用什么解决呢,类型参数!

    ArrayList<String> list = new ArrayList<String>();
    //在JAVASE 7以后的版本中,可以直接使用一下方法来声明了
    ArrayList<String> list = new ArrayList<>();

    简单泛型类

    public class Pair<T>{
      private T first;
      private T second;
    
      public Pair(){first = null; second = null;}
      public Pair(T first, T second){this.first = first; this.second = second}
    
      //get set
    ...
    }

      现在,我们可以像现在这样定义了

    Pair<String> test1 = new Pair<>();
    Pair<Integer> test2 = new Pair<>();

    泛型方法

    public class test{
      public static <T> T first(T...a){
       //...
      }
    }

      但是需要注意的是,下面这几种,并不是泛型方法,不要混淆

     1 public class test<T>{
     2   private T t;
     3 
     4   //这个不是,只是一个普通的成员的方法
     5   public test(T t){
     6     this.t = t;
     7   }
     8   //这个也不是,只是泛型类是形参
     9   public void add(test<T> object){...}
    10 }

    通配符

      通配符是什么,为什么要使用通配符?试想一下,如果我们在一个方法中要传入的参数可能是一个类的子类,这该怎么办呢?

    1 public void test(Persion<Number> p){...}
    2 
    3 x.test(Intger); //错误
    4 x.test(Double);

      不妨自己在编译器上试一试,这里是识别不了的,泛型中这两个没有关联关系,so,可以使用通配符

    1 public void test(List<? extends T>){...}

      这样子类和父类就联系起来了,?是类型实参,不是类型形参

      如果像上面这样使用,我们可以add元素进去吗?反着思考一下,一个父类派生出很多个子类,我们在实例化的时候,很可能new出来的不是一个东西,这可不行呀,要存就得存一种,哪有Int,float都存进去的道理,如果还不明白可以看看代码

    1 List<? extends Person> = new ArrayList<Student>();
    2 List<? extends Person> = new ArrayList<Teacher>();

      这样的话我们只能Get,并不能add,想要add那我们现在可以这样

    1 public void test(List<? superT>){...}

      如此之后,只能add,不能get,又是为什么呢,还是反着思考,既然我们能够add元素进去,但是每一个元素的实际类型不相同

    1 List<? super Student> = new ArrayList<Student>();
    2 List<? super Student> = new ArrayList<Person>();

      当我们想get到Student的时候,可能是一个Person类型,这个Person可能是个Teacher

    说了这么多,终于到了类型擦除

    类型擦除

      在JVM中不存在什么泛型,只有基本的类,当我们定义了一个泛型类的时候

     1 public class Node<T> {
     2     private T data;
     3     private Node<T> next;
     4 
     5     public Node(T data, Node<T> next) {
     6         this.data = data;
     7         this.next = next;
     8     }
     9 
    10     // ...
    11 }

      在做完类型检查后,会变成这样

     1 public class Node{
     2     private Object data;
     3     private Node next;
     4 
     5     public Node(Object data, Node next) {
     6         this.data = data;
     7         this.next = next;
     8     }
     9     // ...
    10 }

      惊不惊喜意不意外!如果我们不想变成Object怎么办,可以自己设置

     1 public class Node<T extneds Comparable<T>> {
     2       private T data;
     3       private Node<T> next;
     4   
     5       public Node(T data, Node<T> next) {
     6           this.data = data;
     7           this.next = next;
     8       }
     9      // ...
    10  }
    11 
    12 public class Node {
    13       private Comparable data;
    14       private Node next;
    15   
    16       public Node(Comparable data, Node next) {
    17           this.data = data;
    18           this.next = next;
    19       }
    20      // ...
    21  }

      看似其实就是这样嘛!但是这样的话会引起一些问题

      1、不允许创建泛型数组

        因为数组中的元素必须统一类型,如若使用了泛型数组,类型都被擦除成Object后,我们不会知道插入的数据是否都是同一个类型的,出错很难排查,可以运行一下下面的代码

    Class c1 = new ArrayList<Integer>().getClass();
    Class c2 = new ArrayList<Double>().getClass();
    System.out.println(c1 == c2);

        最终会得到true的结果,这里可以联想到,泛型无法使用instanceof

      2、桥方法的合成用来保持多态  

     1 public class Node<T> {
     2     public T data;
     3     public Node(T data) { this.data = data; }
     4     public void setData(T data) {
     5         System.out.println("Node.setData");
     6         this.data = data;
     7     }
     8 }
     9 public class MyNode extends Node<Integer> {
    10     public MyNode(Integer data) { super(data); }
    11     public void setData(Integer data) {
    12         System.out.println("MyNode.setData");
    13         super.setData(data);
    14     }
    15 }

        类型擦除后

     1 public class Node {
     2     public Object data;
     3     public Node(Object data) { this.data = data; }
     4     public void setData(Object data) {
     5         System.out.println("Node.setData");
     6         this.data = data;
     7     }
     8 }
     9 public class MyNode extends Node {
    10     public MyNode(Integer data) { super(data); }
    11     public void setData(Integer data) {
    12         System.out.println("MyNode.setData");
    13         super.setData(data);
    14     }
    1 MyNode mn = new MyNode(5);
    2 Node n = mn; 
    3 n.setData("Hello"); 

        实际上不是这样的,这会抛出ClassCastExeption,在哪里?

     1 class MyNode extends Node {
     2     // 桥方法
     3     public void setData(Object data) {
     4         setData((Integer) data);
     5     }
     6     public void setData(Integer data) {
     7         System.out.println("MyNode.setData");
     8         super.setData(data);
     9     }
    10     // ...
    11 }

        setData方法里面有一个强制类型转换,String没办法转换成Integer,记住一句话,桥方法被合成用来保持多态

      3、反射和泛型

        反射允许你在运行时分析任意的对象,如果对象是泛型类的实例,关于泛型类型参数则得不到太多信息,因为它们会被擦除

     

      

  • 相关阅读:
    pdflatex, xelatex, texstudio中文编码问题
    secure erase 时必须umount
    浪潮不能进bios解决过程
    ycsb-命令及参数-与生成的负载类型相关
    zt-Simple source policy routing
    oracle 分页查找数据
    前台调用后台的过程
    初识 java script
    java中的BigDecimal和String的相互转换
    一级缓存、二级缓存、延迟加载、hibernate session 域 pojo三种状态
  • 原文地址:https://www.cnblogs.com/ccxka/p/9507312.html
Copyright © 2011-2022 走看看