zoukankan      html  css  js  c++  java
  • 十:Java之泛型


    【定义】

    一、泛型的定义主要有下面两种:

           在程序编码中一些包括类型參数的类型,也就是说泛型的參数仅仅能够代表类。不能代表个别对象。(这是当今较常见的定义)

    在程序编码中一些包括參数的类。其參数能够代表类或对象等等。

    (如今人们大多把这称作模板)

           不论使用那个定义。泛型的參数在真正使用泛型时都必须作出指明。

          泛型类。是在实例化类的时候指明泛型的详细类型。

          泛型方法。是在调用方法的时候指明泛型的详细类型。

    二、使用泛型的目的:

          一些强类型程序语言支持泛型,其主要目的是加强类型安全及降低类转换的次数,但一些支持泛型的程序语言仅仅能达到部份目的。

    泛型程序设计(Genericprogramming)意味着编写的代码能够被非常多不同类型的对象所重用。

    是对java语言的类型系统的一种扩展,以支持创建能够按类型进行參数化的类。能够把类型參数看作是使用參数化类型时指定的类型的一个占位符。就像方法的形式參数是执行时传递的值得占位符一样。

    【Java泛型的几种类型代码】

     

    一、不使用泛型的代码

     

         我们定义一个Person类,包括三个属性x。y,z。在我们開始定义地时候,我们也不知道这三个属性是用来干什么的,所以我们定义为Object类型。

    可是在使用的时候,我们分别对x。y。z赋予了int,double。String类型,所以在取出的时候。我们须要把这三个类型值进行强制转换。例如以下代码:

     

       1. Person.java


    <span style="font-size:18px;"> public class Person {
         private Object x;
         private Object y;
         private Object z;
          //使用Object类型。能够转化为不论什么类型
         public Object getX() {
             return x;
          }
         public void setX(Object x) {
            this.x = x;
        }
        public Object getY() {
            return y;
        }
        public void setY(Object y) {
            this.y = y;
        }
        public Object getZ() {
            return z;
        }
        public void setZ(Object z) {
            this.z = z;
        }
     }
    
    2. NoGenericTest.java
     
    
     public class NoGenericTest {
         public static void main(String[]args){
             Person boy=new Person();
             boy.setX(20);
             boy.setY(22.2);
             boy.setZ("帅哥TT");
             //这里依据设置的不同类型的值,我们须要进行强制类型转化。

    int x=(Integer)boy.getX(); double y=(double)boy.getY(); String z=(String)boy.getZ(); System.out.println(x); System.out.println(y); System.out.println(z); } } 3. 执行结果 20 22.2 帅哥TT</span>

    二、使用一个类型变量泛型的代码

     

         我们定义一个泛型类Person,定义三个属性x,y,z,在測试类中,我们设置属性的值,并打印。

    <span style="font-size:18px;">  1. Person.java
     public class Person<T> {
        private T x;
         private T y;
        private T z;
         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;
        }
        public T getZ() {
            return z;
        }
        public void setZ(T z) {
            this.z = z;
        }
     }
    
    2. GenericTest.java
     
     public class GenericTest {
        public static void main(String[]args){
             Person boy=new Person();
             boy.setX(20);
             boy.setY(22.2);
             boy.setZ("帅哥TT");
             //不用进行类型转化
              System.out.println(boy.getX());
             System.out.println(boy.getY());
            System.out.println(boy.getZ());
        }
     }
    
    3. 执行结果
    20
    22.2
    帅哥TT
    三、使用两个类型变量泛型的代码
         我们定义一个泛型类Person,定义两个属性x,y。使用了两种不同的类型变量,在測试类中。我们设置属性的值。并打印。
     
    1. Person.java
     
     public class Person<T1,T2> {
         private T1 x;
         private T2 y;
         public T1 getX() {
             return x;
          }
         public void setX(T1 x) {
             this.x = x;
          }
        public T2 getY() {
            return y;
        }
        public void setY(T2 y) {
           this.y = y;
        }
    }
    
    2. GenericTest.java
     
    public class GenerricTest {
         public static void main(String[] args){
             Person<String,Integer> boy=new Person<String,Integer>();
             boy.setX("帅哥TT");
             boy.setY(20);
             System.out.println(boy.getX());
            System.out.println(boy.getY());
          }
     
     }
     3. 执行结果
      帅哥TT
      20</span>


    四、使用泛型的继承


         我们定义一个泛型类Person,定义两个属性x。y,然后定义还有一个泛型类Boy。定义属性z。Boy继承Person类,在測试类中,我们设置属性的值。并打印。

     

      

     1. Person.java
     
     public class Person<T1,T2> {
         private T1 x;
         private T2 y;
         public T1 getX() {
             return x;
          }
         public void setX(T1 x) {
             this.x = x;
          }
        public T2 getY() {
            return y;
        }
        public void setY(T2 y) {
            this.y = y;
        }
    }
    
     2. Boy
     
     public class Boy<T1,T2,T3>extendsPerson<T1,T2> {
       private T3 z;
        public T3 getZ() {
            return z;
        }
        public void setZ(T3 z) {
            this.z = z;
        }
     }
    
    3. GenericTest.java
     
    
     1public class GenericTest {
     2    public static void main(String[] args){
     3        Boy<String,Integer,Double> boy=new Boy<String,Integer,Double>();
     4        boy.setX("帅哥TT");
     5        boy.setY(20);
     6        boy.setZ(200000.22);
     7        
     8        System.out.println(boy.getX());
     9        System.out.println(boy.getY());
    10        System.out.println(boy.getZ());
    11    }
    12 }
    
     4.执行结果
    1 帅哥TT
    2 20
    3 200000.22

    五、使用泛型的接口

         我们定义一个泛型接口Person,定义两个方法,然后定义还有一个泛型类Boy,实现泛型接口Person。定义属性x,y,z,在測试类中。我们设置属性的值,并打印。


    <span style="font-size:18px;">1. Person.java
    
    1 public interface Person<T1,T2> {
    2    public T1 getX();
    3    public T2 getY();
    4 }
     2. Boy
    
     1public class Boy<T1,T2,T3>implements Person<T1,T2> {
     2    private T1 x;
     3    private T2 y;
     4    private T3 z;
     5    public T1 getX() {
     6        return x;
     7     }
     8    public void setX(T1 x) {
     9        this.x = x;
    10    }
    11    public T2 getY() {
    12        return y;
    13    }
    14    public void setY(T2 y) {
    15        this.y = y;
    16    }
    17    public T3 getZ() {
    18        return z;
    19    }
    20    public void setZ(T3 z) {
    21        this.z = z;
    22    }
    23
    24 }
    
    3. GenericTest.java
     
     1public class GenericTest {
     2    public static void main(String[] args){
     3        Boy<String,Integer,Double> boy=newBoy<String,Integer,Double>();
     4        boy.setX("帅哥TT");
     5        boy.setY(20);
     6        boy.setZ(200000.22);
     7        System.out.println(boy.getX());
     8        System.out.println(boy.getY());
     9        System.out.println(boy.getZ());
    10    }
    11 }
    
    4. 执行结果
    1 帅哥TT
    2 20
    3 200000.22</span>


    六、使用泛型方法

         说明一下。定义泛型方法时,必须在返回值前边加一个<T>。来声明这是一个泛型方法,持有一个泛型T,然后才干够用泛型T作为方法的返回值。

         定义一个普通类Person,定义一个泛型方法,例如以下代码:

    <span style="font-size:18px;">1. Person.java
    
     1public class Person{
     2    public static<T>T getMiddle(T[]a){
     3        return a[a.length/2];
     4     }
     5    public static void main(String [] args){
     6        String[]name={"帅哥TT","帅哥TT1","帅哥TT2"};
     7        String middle=Person.<String>getMiddle(name);
     8        System.out.println(middle);
     9        
    10        Integer[]num={20,22,25};
    11        Integer middle1=Person.<Integer>getMiddle(num);
    12        System.out.println(middle1);
    13        
    14        Double[]num1={20.0,22.2,25.5};
    15        Double middle2=Person.<Double>getMiddle(num1);
    16        System.out.println(middle2);
    17    }
    18 }
    
    2. 执行结果
    
    1 帅哥TT1
    2 22
    3 22.2</span>


    七、类型变量的限定

         例如以下代码,我们在方法min中定义了一个变量smallest类型为T。这说明了smallest能够是不论什么一个类的对象,我们在以下的代码中须要使用compareTo方法, 可是我们没有办法确定我们的T中含有CompareTo方法,所以我们须要对T进行限定,在代码中我们让T继承Comparable类。

    例如以下:

    <span style="font-size:18px;">public static<T extendsComparable>T min(T[]a)
       1.Person.java
     1public class Person{
     2    public static<T extends Comparable>T min(T[]a){
     3        if(a==null||a.length==0){
     4            return null;
     5        }
     6        T smallest=a[0];
     7        for(int i=1;i<a.length;i++){
     8            if(smallest.compareTo(a[i])>0){
     9                 smallest=a[i];
    10             }
    11        }
    12        return smallest;
    13    }
    14    public static void main(String [] args){
    15        Integer[]num={20,25,30,10};
    16        Integer middle=Person.<Integer>min(num);
    17        System.out.println(middle);
    18    }
    19 }
    
     2. 执行结果
     10</span>


    【Java泛型理解】


    一、类型擦除

         正确理解泛型概念的首要前提是理解类型擦除(type erasure)。 Java中的泛型基本上都是在编译器这个层次来实现的。

    在生成的Java字节代码中是不包括泛型中的类型信息的。使用泛型的时候加上的类型參数,会被编译器在编译的时候去掉。这个过程就称为类型擦除。如在代码中定义的List<Object>和List<String>等类型。在编译之后都会变成List。JVM看到的仅仅是List,而由泛型附加的类型信息对JVM来说是不可见的。Java编译器会在编译时尽可能的发现可能出错的地方,可是仍然无法避免在执行时刻出现类型转换异常的情况。


         非常多泛型的奇怪特性都与这个类型擦除的存在有关,包含:

         泛型类并没有自己独有的Class类对象。比方并不存在List<String>.class或是List<Integer>.class,而仅仅有List.class。

    静态变量是被泛型类的全部实例所共享的。对于声明为MyClass<T>的类,訪问当中的静态变量的方法仍然是 MyClass.myStaticVar。

    无论是通过new MyClass<String>还是new MyClass<Integer>创建的对象,都是共享一个静态变量。

    泛型的类型參数不能用在Java异常处理的catch语句中。

    由于异常处理是由JVM在执行时刻来进行的。由于类型信息被擦除,JVM是无法区分两个异常类型MyException<String>和MyException<Integer>的。对于JVM来说,它们都是 MyException类型的。也就无法执行与异常相应的catch语句。

    二、最佳实践

            在使用泛型的时候能够遵循一些主要的原则。从而避免一些常见的问题。

    在代码中避免泛型类和原始类型的混用。比方List<String>和List不应该共同使用。这样会产生一些编译器警告和潜在的执行时异常。当须要利用JDK 5之前开发的遗留代码,而不得不这么做时,也尽可能的隔离相关的代码。

    在使用带通配符的泛型类的时候,须要明白通配符所代表的一组类型的概念。因为详细的类型是未知的,非常多操作是不同意的。

           泛型类最好不要同数组一块使用。你仅仅能创建new List<?>[10]这种数组。无法创建new List<String>[10]这种。这限制了数组的使用能力。并且会带来非常多费解的问题。因此,当须要类似数组的功能时候,使用集合类就可以。

    不要忽视编译器给出的警告信息。


  • 相关阅读:
    算法笔记--支配树
    51Nod 1187 寻找分数
    ACM-ICPC 2018 徐州赛区网络预赛 J. Maze Designer
    ACM-ICPC 2018 徐州赛区网络预赛 A. Hard to prepare
    HDU
    HDU
    Codeforces 1011E
    Codeforces 990D
    Codeforces 989C
    Codeforces 932E
  • 原文地址:https://www.cnblogs.com/yxwkf/p/5177099.html
Copyright © 2011-2022 走看看