Java 泛型

泛型

泛型即参数化类型,即实际使用时再确定参数的类型。就像我们在定义方法时可以声明形参,在实际调用方法时传入实参,那么我们同样可以把类型也参数化,只有在实际使用时才确定数据的类型 。

泛型包括泛型接口、类、方法、变量等。Java的泛型只在编译时存在,编译完成后会进行擦除。即在字节码文件中是没有泛型存在的。

泛型优缺点

优点:

  • 在编译时提供了类型检查的机制防止非法类型,同时避免了显示的类型转换。

  • 在使用泛型的时候可以根据传入泛型类型的不同而使用对应类型的API。

缺点:

  • 泛型不支持基本类型所以会涉及自动拆装箱问题有性能损耗

  • 泛型变量不能当做真实类型去使用(比如通过泛型变量调用方法T.getXx())

  • 泛型会自动进行类型强转这也有性能损耗

如何判断是否需要使用泛型

  1. 判断当前这个类中的方法是否需要在返回值中使用泛型,如果需要则可以使用泛型。

  2. 使用泛型的时候根据传入的不同类型而使用对应类型的API 而去使用泛型。

  3. 如果你的类型确定的话是完全不需要使用泛型的

泛型擦除

Java中的泛型类型在代码编译时会被擦除掉(一般情况下会被擦成Object类型,如果使用了上限通配符的话会被擦成extends右边的类型,如T extends View则最终会被擦成View类型)。

泛型擦除的原因:出于兼容性的考虑,泛型是JDK1.5以后才引入的,为了兼容之前的JDK版本所以在运行时将泛型类型都擦掉以保证和之前JDK版本的java字节码相同。

不过泛型的擦除会保留部分泛型信息,在 signature 属性中保存了泛型的参数类型信息,不过这仅限于类中的属性,对于方法中的局部变量无能为力。

泛型数组

数组中的元素为泛型,那么这个数组就是泛型类型的数组,泛型数组在java中使用GenericArrayType接口来表示,但是Java中不能创建一个确切的泛型类型的数组,但是使用通配符创建型数组是可以的。


  ArrayList<String> [] a=new ArrayList<String>[10];//error

  ArrayList<String> [] a=new ArrayList[10];//success



  List<?>[] ls = new ArrayList<?>[10];

某种特定的场合,你仍然要使用泛型数组,推荐的方式是使用 类型标签+Array.newInstance 来实现,并用注解 @SuppressWarnings(“unchecked”) 抑制住警告:

image-20200708115506445.png

运行时获取泛型信息

Signature属性

类型擦除是有范围的,编译时类中定义的泛型类型是不会被擦除的,对应的泛型类型会被保存在Signature中。即与类及其字段和方法的类型参数相关的元数据都会被保留下来通过反射获取到。

额外保存参数类型

比如 Jackson 反序列化泛型类型,将参数类型信息显式保存下来。此外还可以使用注解处理器,在编译期间获取泛型真实类型,并保存到类文件中。

通配符

由于泛型不是协变的,它不支持继承,比如在使用 List< Number> 的地方不能传递 List< Integer>,所以引入了通配符去解决对应的问题,可以理解通配符是对泛型功能的扩展和增强。

  • extends 上限通配符 可以接收extend后的类型及子类

  • super 下限通配符 可以接收super后的类型及父类

  • ? 通配符 代表了接收未知类型的数据

extends


    List<? extends Fruit> fruits = new ArrayList<>();



        fruits.add(new Food());    // compile error

        fruits.add(new Fruit());    // compile error

        fruits.add(new Apple());    // compile error

        Fruit object = fruits.get(0);    // compile success

        fruits = new ArrayList<Fruit>(); // compile success

        fruits = new ArrayList<Apple>(); // compile success

        fruits = new ArrayList<Food>(); // compile error

        fruits = new ArrayList<? extends Fruit>(); // compile error: 通配符类型无法实例化

可以看到add操作全部编译失败因为 fruits 集合并不知道实际类型是 Fruit、Apple 还是 Food,所以无法对其赋值。

super


      List<? super Fruit> fruits1 = new ArrayList<>();



        fruits1.add(new Food());    // compile error

        fruits1.add(new Fruit());    // compile success

        fruits1.add(new Apple());    // compile success

        fruits1 = new ArrayList<Fruit>(); // compile success

        fruits1 = new ArrayList<Apple>(); // compile error

        fruits1 = new ArrayList<Food>(); // compile success

        fruits1 = new ArrayList<? super Fruit>(); // compile error: 通配符类型无法实例化



        Fruit object = fruits1.get(0); // compile error

add 时 Fruit 及其子类均可成功,为啥呢?因为已知 fruits 的参数化类型必定是 Fruit 或其超类 T,那么 Fruit 及其子类肯定可以赋值给 T。

归根到底,还是“子类对象可以赋值给超类引用,而反过来不行”这一规则导致 extends 和 super 通配符在 add 操作上表现如此的不同。同样地,也导致 super 限定的 fruits 中 get 到的元素不能赋值给 Fruit 引用,而 extends 则可以。

使用规范:super只能用作实参不能用于形参,?和extends实参形参都可以

形参:public class Shop< T>的那个T,即在声明泛型时的类型参数(类或方法声明的类型参数)

实参:除了形参其他都是实参,比如声明变量(Shop < Apple> appleShop;此处的Apple)、方法参数中的泛型(public int getTotalWeight(List<Fruit> list) {}此处的Fruit)

当我们使用上限通配符时对应方法参数中使用到泛型的方法将都无法调用,因为我们不能确定具体传入的是哪种类型。

当我们使用下限通配符时对应方法返回值中使用到泛型的方法将都无法调用,因为我们不能确定具体返回的是哪种类型。

  • 静态方法使用泛型必须把方法生命为泛型方法
  • 构造泛型实例时,如果省略了填充类型,则默认填充为无边界通配符!

泛型,有点难度,会让很多人懵逼,那是因为你没有看这篇文章!

深入理解 Java 泛型

面试重灾区-泛型攻克

Java 的泛型擦除和运行时泛型信息获取

java泛型你需要知道的一切

Java泛型超详细解读 : super和extend

https://blog.csdn.net/s10461/article/details/53941091

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,658评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,482评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,213评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,395评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,487评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,523评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,525评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,300评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,753评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,048评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,223评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,905评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,541评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,168评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,417评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,094评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,088评论 2 352