理解Java泛型和类型擦除

Posted 泡^泡

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了理解Java泛型和类型擦除相关的知识,希望对你有一定的参考价值。

目录

介绍

泛型标记

泛型限定

上限

下限

泛型方法

泛型类

泛型接口

类型擦除

  泛型擦除的体现

对象放进List
​​​​​​​

介绍

泛型的本质是参数化类型,泛型提供了编译时类型的安全检测机制,该机制允许程序在编译时检测非法的类型,比如要实现一个能够对字符串(String)、整形(Int)、浮点型(float)、对象(Object)进行大小比较的方法,就可以使用泛型。

泛型标记

序号泛型标记说明
1E-Element在集合中使用,表示在集合中存放的元素
2T-Type表示Java类,包括基本的类和我们自定义的类
3K-Key表示键,比如Map中的Key
4V-value表示值
5N-Number表示数值类型
6?表示不确定的Java类型(类型通配符也可以使用)

泛型限定

上限<? extends T>

使用通配符"?"和"extends"关键字指定泛型的上限,具体用法是<? extends T>,表示该通配符所代表的类型是T类的子类或者接口T的子接口。

下限<? super T>

使用通配符"?"和"super"关键字指定泛型的上限,具体用法是<? super T>,表示该通配符所代表的类型是T类型的父类或者父接口。

https://www.cnblogs.com/lucky_dai/p/5485421.html

泛型方法

泛型方法指将方法的参数类型定义为泛型,以便在调用的时候接收不同类型的参数。

package fanxing;

import java.util.Date;

public class GenericClassTest 
    public static void main(String[] args) 
        genericMethod(1,1.1D,1.2F);
    

    public static <T> void genericMethod(T... param)
        for (T  element : param)
            if(element instanceof Integer)
                System.out.println("处理 Integer 类型数据中...");
            
            else if(element instanceof Double)
                System.out.println("处理 Double 类型数据中...");
            
            else if(element instanceof Float)
                System.out.println("处理 Float 类型数据中...");
            
            else if(element instanceof Long)
                System.out.println("处理 Long 类型数据中...");
            
            else if(element instanceof Date)
                System.out.println("处理 Date 类型数据中...");
            
            
        
    

泛型类

泛型类指在定义类时在类上定义了泛型,以便类在使用时可以根据传入的不同参数类型实例化不同的对象。

package fanxing;

public class GenericClassTest<T> 
    public static void main(String[] args) 
        GenericClassTest<Integer> list = new GenericClassTest<>();
        list.add(11);
        GenericClassTest<String> strList = new GenericClassTest<>();
        strList.add("111");
    
    private T t;
    
    public void add(T t)
        this.t = t;
    
    
    public T get()
        return t;
    

泛型接口

泛型接口和泛型类类似,通过在接口名后面添加类型参数的声明部分来实现。

package fanxing;

/**
 * 泛型接口
 * @param <T>
 */
public interface TGeneric<T> 
    T getId();

package fanxing;

import java.util.Random;

/**
 * 泛型接口实现类
 * @param <T>
 */
public class GenericIntergerTest implements TGeneric<Integer> 
    @Override
    public Integer getId() 
        Random random = new Random();
        return random.nextInt();
    

    public static void main(String[] args) 
        GenericIntergerTest test = new GenericIntergerTest();
        System.out.println(test.getId());
    


类型擦除

     泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。 Java语言引入泛型的好处是安全简单。

    泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,以提高代码的重用率。

      JVM并不知道泛型的存在,因为泛型在编译阶段就已经被处理成普通的类和方法; 
处理机制是通过类型擦除,擦除规则:

  • 若泛型类型没有指定具体类型,用Object作为原始类型;
  • 若有限定类型< T exnteds XClass >,使用XClass作为原始类型;
  • 若有多个限定< T exnteds XClass1 & XClass2 >,使用第一个边界类型XClass1作为原始类型;

  泛型擦除的体现

在写代码时,无法把一个 String 类型的实例加到 ArrayList<Integer> 中,因为ArrayList<Integer> 和 ArrayList<String> 在编译的时候是完全不同的类型,但是运行结果却是true。这就Java泛型的类型擦除造成的。

因为不管是 ArrayList<Integer> 还是 ArrayList<String>,在编译完成后都会被编译器擦除成了 ArrayList。

Java 泛型擦除是 Java 泛型中的一个重要特性,其目的是避免过多的创建类而造成的运行时的过度消耗。所以,想 ArrayList<Integer> 和 ArrayList<String> 这两个实例,其类实例是同一个。


对象放进List<String>

当我们正常的将 Activity 对象放进 List<String> list = new ArrayList<>( ) 这样的集合去,会报一个错:

 泛型的约束让我们无法将 Activity 对象放进泛型为 String 的集合中去。那么咋整呢?

通过泛型擦除的体现已经知道ArrayList<String>(或者ArrayList<Integer>)编译后都是java.util.ArrayList,泛型的约束是在编译时约束的,真正运行的 class 是没有泛型约束的。所以只要在运行时将 Activity 对象加入List<String>中就可以了:

以上是关于理解Java泛型和类型擦除的主要内容,如果未能解决你的问题,请参考以下文章

Java 之 泛型擦除

泛型(java菜鸟的课堂笔记)

深入理解 Java 泛型擦除机制

泛型-类型擦除

泛型(10)-泛型擦除与转换

java泛型泛型的内部原理:类型擦除以及类型擦除带来的问题