• 【JavaSE】泛型


    Java泛型


    2019-07-05  22:00:24  by冲冲

    1. 泛型的引例

     1 List list = new ArrayList();
     2 list.add(1022);        //向集合中添加一个 Integer 类型的数据
     3 list.add("Yadiel");    //向集合中添加一个 String 类型的数据
     4 
     5 //遍历list元素
     6 for(int i = 0 ; i < list.size() ; i++){
     7     Object obj = list.get(i);  //注意这里每个类型都是 Object
     8     System.out.println(obj);
     9 }
    10  
    11 //如果遍历的时候就想得到自己想要的数据类型
    12 for(int i = 0 ; i < list.size() ; i++){
    13     String obj = (String) list.get(i);  //在取 Integer 的时候会报类型转换错误
    14     System.out.println(obj);
    15 }

     报错信息:

    意思是 集合中第二个数据是 Integer,但是我们取出来的时候将其转换为 String 了,所以报错。

    解决方案:

    ① 我们在遍历的时候,根据每个数据的类型判断,然后进行强转。缺陷是,如果数据成千上万,该方案不可取。

    ② 在往集合中加入数据的时候,就做好限制,比如这个集合只能添加 String 类型的,下一个集合只能添加 Integer 类型的(就像数组一样规定元素类型必须相同),那么在取数据时,由于前面已经限制了该集合的数据类型,那么就很好强转了。这第二种解决办法,也就是我们这篇文章讲的 泛型

    2. 泛型的概念

    泛型是Java SE1.5的新特性。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法

    在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。

    3. 泛型的语法

    (1)引例的解决

     1 List<String> list = new ArrayList<String>();
     2 //list.add(1022);  //向集合中添加一个 Integer 类型的数据时,编译器会报错
     3 list.add("Aoa");   //向集合中添加一个 String 类型的数据
     4 list.add("Loa");   //向集合中添加一个 String 类型的数据
     5  
     6 //如果我们遍历的时候就想得到自己想要的数据类型
     7 for(int i = 0 ; i < list.size() ; i++){
     8     String obj = list.get(i);  //这里就不需要强转了,前面添加的是什么类型,这里获取的就是什么类型
     9     System.out.println(obj);
    10 }

    (2)泛型只在编译阶段有效

    1 List<String> list1 = new ArrayList<String>();
    2 List list2 = new ArrayList();
    3 Class c1 = list1.getClass();
    4 Class c2 = list2.getClass();
    5 System.out.println(c1==c2); //true

    由于反射是在运行时阶段,c1==c2为 true,说明编译之后的 class 文件中是不包含泛型信息。

    结论:Java 泛型只在编译阶段有效,即在编译过程中,程序会正确的检验泛型结果。而编译成功后,class 文件是不包含任何泛型信息的

    (3)泛型类

    在类名后面添加 类型参数 的声明部分,其他跟非泛型类完全一样。

    泛型类的类型参数声明部分,可以包含一个或多个类型参数,参数间用逗号隔开。

     1 public class Box<T> {
     2   private T t;
     3   public void add(T t) {
     4     this.t = t;
     5   }
     6   public T get() {
     7     return t;
     8   }
     9  
    10   public static void main(String[] args) {
    11     Box<Integer> integerBox = new Box<Integer>();
    12     Box<String> stringBox = new Box<String>();
    13  
    14     integerBox.add(new Integer(1022));
    15     stringBox.add(new String("肥猪"));
    16  
    17     System.out.println("整型值为 :"+ integerBox.get());
    18     System.out.println("字符串为 :"+ stringBox.get());
    19   }
    20 }

    输出结果:

    1 整型值为 :1022
    2 字符串为 :肥猪

    (4)泛型方法

    应用场景:假如需要写一个排序方法,能够对整型数组、字符串数组甚至其他任何类型的数组进行排序,该如何实现?答案是泛型。

    定义泛型方法的规则:

    ① 所有泛型方法声明,都有一个 类型参数 的声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子中的<E>)。

    ② 每一个 类型参数 声明部分可以包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。

    ③ 类型参数 能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。

    ④ 泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像int,double,char等)。

     1 public class GenericMethodTest {    //演示如何使用泛型方法打印不同字符串的元素
     2     // 泛型方法 printArray
     3     public static <E> void printArray(E[] inputArray) {
     4         // 输出数组元素
     5         for (E element : inputArray) {
     6             System.out.printf("%s ", element);
     7         }
     8         System.out.println();
     9     }
    10 
    11     public static void main(String args[]) {
    12         // 创建不同类型数组: Integer, Double 和 Character
    13         Integer[] intArray = { 1, 2, 3, 4, 5 };
    14         Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
    15         Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };
    16 
    17         System.out.println("整型数组元素为:");
    18         printArray(intArray); // 传递一个整型数组
    19 
    20         System.out.println("
    双精度型数组元素为:");
    21         printArray(doubleArray); // 传递一个双精度型数组
    22 
    23         System.out.println("
    字符型数组元素为:");
    24         printArray(charArray); // 传递一个字符型数组
    25     }
    26 }

    输出结果:

    1 整型数组元素为:
    2 1 2 3 4 5 
    3 
    4 双精度型数组元素为:
    5 1.1 2.2 3.3 4.4 
    6 
    7 字符型数组元素为:
    8 H E L L O 

    (5)类型通配符?

    ① 类型通配符 通常使用 ? 代替具体的类型参数。例如 List<?> 在逻辑上是 List<String>,List<Integer> 等所有List<具体类型实参>的父类。

     1 import java.util.*;
     2  
     3 public class GenericTest {
     4      
     5     public static void main(String[] args) {
     6         List<String> name = new ArrayList<String>();
     7         List<Integer> age = new ArrayList<Integer>();
     8         List<Number> number = new ArrayList<Number>();
     9         
    10         name.add("yadiel");
    11         age.add(18);
    12         number.add(999);
    13  
    14         getData(name);
    15         getData(age);
    16         getData(number);
    17        
    18    }
    19  
    20    public static void getData(List<?> data) {
    21       System.out.println("data :" + data.get(0));
    22    }
    23 }

    输出结果:

    1 data :yadiel
    2 data :18
    3 data :999

    ② 类型通配符的上限和下限

    上限:<? extends T> 表示该通配符所代表的类型是 T类型 或者 T类型的子类。

    下限:<? super T>  表示该通配符所代表的类型是 T类型 或者 T类型的父类。

     1 import java.util.*;
     2  
     3 public class GenericTest {
     4      
     5     public static void main(String[] args) {
     6         List<String> name = new ArrayList<String>();
     7         List<Integer> age = new ArrayList<Integer>();
     8         List<Number> number = new ArrayList<Number>();
     9         
    10         name.add("yadiel");
    11         age.add(18);
    12         number.add(999);
    13  
    14         //getUperNumber(name);//String类型表示Number类型的子类,报错
    15         getUperNumber(age);   //输出18
    16         getUperNumber(number);//输出999
    17        
    18    }
    19    
    20    public static void getUperNumber(List<? extends Number> data) {
    21           System.out.println("data :" + data.get(0));
    22        }
    23 }

    设计泛型方法,返回三个可比较对象的最大值。

     1 public class MaximumTest
     2 {
     3    // 比较三个值并返回最大值
     4    public static <T extends Comparable<T>> T maximum(T x, T y, T z)
     5    {                     
     6       T max = x; // 假设x是初始最大值
     7       if ( y.compareTo( max ) > 0 ){
     8          max = y; //y 更大
     9       }
    10       if ( z.compareTo( max ) > 0 ){
    11          max = z; // 现在 z 更大           
    12       }
    13       return max; // 返回最大对象
    14    }
    15    public static void main( String args[] )
    16    {
    17       System.out.printf( "%d, %d 和 %d 中最大的数为 %d
    
    ",
    18                    3, 4, 5, maximum( 3, 4, 5 ) );
    19  
    20       System.out.printf( "%.1f, %.1f 和 %.1f 中最大的数为 %.1f
    
    ",
    21                    6.6, 8.8, 7.7, maximum( 6.6, 8.8, 7.7 ) );
    22  
    23       System.out.printf( "%s, %s 和 %s 中最大的数为 %s
    ","pear",
    24          "apple", "orange", maximum( "pear", "apple", "orange" ) );
    25    }
    26 }

    输出结果:

    1 3, 4 和 5 中最大的数为 5
    2 
    3 6.6, 8.8 和 7.7 中最大的数为 8.8
    4 
    5 pear, apple 和 orange 中最大的数为 pear

    4. 注意事项

    ① 不能用基本类型来定义泛型,如 int、float。

    1 List<int> list = new ArrayList<int>(); //不能用 int 这样的基本类型定义泛型

    因为集合中只能存放引用类型的数据,即使你存入基本类型,Java还是会通过自动拆箱和自动装箱机制将其转换为引用类型。

    ② 如果使用 ? 接收泛型对象时,则不能设置被泛型指定的内容。

    1 List<?> list = new ArrayList<>();
    2 list.add("aa");  //错误,无法设置

    ③ 泛型方法的定义与其所在的类是否是 泛型类 是没有任何关系的,所在的类可以是泛型类,也可以不是泛型类。

    ④ 泛型类没有继承关系,即 String 为 Object 类的子类,则 List<String> 是 List<Object> 的子类这句话是错误的。

    正确:List<String>==List<Object>。 原因:泛型只是规定了List的元素类型,如果不符合,会在编译阶段报错。在运行阶段,无论List的元素是什么类型,List的类型都属于List。

    假设上面那句话是正确的,那么由于泛型的产生机制就是放什么类型的数据进去,取出来的就是什么类型,而不用进行类型转换。
    这里把 String 类型的数据放入Object 类的泛型集合中,那么取出来的应该就是 String 类的数据,而实际上取出来的是 Object 类的数据,这与泛型的产生机制相违背,故不成立!

    参考:

    https://www.cnblogs.com/ysocean/p/6826525.html

    https://www.runoob.com/java/java-generics.html

  • 相关阅读:
    git config (21)
    狗狗染头,一举天下成名
    K2使用总结K2简介
    K2使用总结流程设计
    如何组建开发团队谈面试
    如何组建开发团队谈谈团队组成
    如何组建开发团队谈招聘中的牛人
    作业题有感
    js对象学习笔记Function类型和对象
    非常认同的《SEO优化大全》
  • 原文地址:https://www.cnblogs.com/yadiel-cc/p/11141952.html
Copyright © 2020-2023  润新知