Android面试题之Java 泛型和Kotlin泛型

简介: **Java泛型是JDK5引入的特性,用于编译时类型检查和安全。泛型擦除会在运行时移除类型参数,用Object或边界类型替换。这导致几个限制:不能直接创建泛型实例,不能使用instanceof,泛型数组与协变冲突,以及在静态上下文中的限制。通配符如<?>用于增强灵活性,<? extends T>只读,<? super T>只写。面试题涉及泛型原理和擦除机制。

本文首发于公众号“AntDream”,欢迎微信搜索“AntDream”或扫描文章底部二维码关注,和我一起每天进步一点点

定义:JDK5引入的一种参数化类型特性

继承和实现接口可以多个
static class A{
   }
static interface B{
   }
static interface C{
   }

//类必须在接口的前面
static class D<T extends A & B & C>{
   }
泛型原理

泛型擦除:

  • 做类型检查,T如果有做类型限制,会转化为第1种限制,否则会擦除为object
  • 生成桥方法,里面调用对应的接口方法,调用的时候会进行类型的强转,转为T的限制类型
  • 泛型擦除后,字节码中没有泛型信息了,但是类的常量池里保留了泛型信息。反射的时候提供了一套API可以拿到,比如getGenericType()
泛型带来的问题
  1. 泛型类型变量不能使用基本类型
比如没有ArrayList<int>,只有ArrayList<Integer>,
当泛型擦除后,ArrayList的原始类中的类型变量T替换成了Object,但Object不能存放基本数据类型
  1. 不能使用instanceof运算符
因为泛型擦除后,ArrayList<String>只剩下原始类型,泛型信息String不存在了
  1. 泛型在静态方法和静态类中的问题

    因为泛型类中的泛型参数的实例化是在定义泛型类型对象时候指定的,而静态成员是不需要使用对象来调用的,所有对象都没创建,无法确定泛型参数;

    静态方法中是可以的,因为调用的时候可以确定参数中的泛型类型

  2. 泛型类型中的方法冲突

    因为擦除后2个equales方法变成一样的了,参数都会变成object

@Override
public boolean equals(Object o) {
   
    super.equals(o);
}
@Override
public boolean equals(T o) {
   
    super.equals(o);
}
  1. 没法创建泛型实例,因为类型不确定
public static <E> void append(List<E> list){
   
    //编译会报错
    E element = new E();
    list.add(element);
}
不过可以通过反射来创建
 public static <E> void append(List<E> list, Class<E> cls) throws Exception {
   
    E element = cls.newInstance();
    list.add(element);
}
  1. 没有泛型数组,因为数组遵循协变原则
协变:Apple extend Fruit,Apple[] 的父类是Fruit[]
泛型,继承和子类
  • 给定两种具体的类型 A和B,无论A和B是否相关,MyClass和MyClass都没有半毛钱关系;
比如Apple继承自Fruit,那Plate<Fruit>和Plate<Apple>也没有任何关系;
也就是说苹果是水果,但装苹果的盘子不是装水果的盘子
  • 继承关系中,泛型可以有多个,但如果有一个泛型参数是一样的,继承关系就存在的,不会因为有的类多个个泛型参数,继承关系就不在了
比如Plate<Apple> <--AIPlate<Apple> <--BIgPlate<Apple> <-- ColorPlate<String, Apple>
泛型和通配符

通配符让泛型转型更灵活

  • Plate<?> 非限定通配符,是一个泛型类型 ?表示未知,等价于 Plate<? extends Object>;副作用是既不能读也不能写;可以促使进行安全检查

  • List和List<?>,前者不会进行安全检查,后者会进行类型的安全检查

限定通配符

  • Plate<? extends T> 限定上届,能读不能写,类似于生产者
  • Plate<? super T> 限定下届,能写不能读,类似于消费者
通配符总结
  • 如果只需要从集合中获得类型T,使用<? extends T>;

  • 如果只需要将类型T放到集合中,使用<? super T>;

  • 既要获取又要放置元素,则不使用通配符

用<? extends Fruit>的后遗症

<? extends Fruit>是上届通配符,相当于“只读”

Plate<? extends Fruit> fruitPlate = plate(Plate<Apple> 类型)
//下面会报错,因为具体类型丢失了,只能是Fruit
fruitPlate.set(new Apple())

解决方案:可以用反射来set,但是安全性降低

Method set = friutPlate.getClass().getMethod("set", Object.class);
set.invoke(fruitPlate, new Banana())
//什么都能set,安全性降低
set.invoke(fruitPlate, new Beef())

面试题

1、Java泛型原理?什么是泛型擦除机制?

Java泛型是JDK5新引入的特性,为了向下兼容,虚拟机其实不支持泛型,所以Java实现的是伪泛型机制,也就是说Java在编译期擦除了所有的泛型信息,这样Java就不需要产生新的类型到字节码,所有的泛型类型最终都是一种原始类型,在Java运行时根本就不存在泛型信息

2、Java编译器具体是如何擦除泛型的

  • 检查泛型类型,获取目标类型

  • 擦除类型变量,并替换为限定类型。如果泛型类型的类型变量没有限定(\),则Object为原始类型;

 如果有限定(\<T extends XClass>),则用XClass作为限定类型;
 如果有多个限定(T extends XClass1 & XClass2),则使用第一个边界XClass1作为原始类。
  • 在必要时插入类型转换以保持类型安全

  • 生成桥方法以在扩展时保持多态性

Kotlin泛型

Kotlin的泛型可以看文章:Android面试题之Kotlin泛型和reified关键字


欢迎关注我的公众号AntDream查看更多精彩文章!

目录
相关文章
|
9月前
|
安全 Java
Java之泛型使用教程
Java之泛型使用教程
473 10
|
11月前
|
安全 Java API
在Java中识别泛型信息
以上步骤和示例代码展示了怎样在Java中获取泛型类、泛型方法和泛型字段的类型参数信息。这些方法利用Java的反射API来绕过类型擦除的限制并访问运行时的类型信息。这对于在运行时进行类型安全的操作是很有帮助的,比如在创建类型安全的集合或者其他复杂数据结构时处理泛型。注意,过度使用反射可能会导致代码难以理解和维护,因此应该在确有必要时才使用反射来获取泛型信息。
338 11
|
安全 Java Android开发
为什么大厂要求安卓开发者掌握Kotlin和Jetpack?深度解析现代Android开发生态优雅草卓伊凡
为什么大厂要求安卓开发者掌握Kotlin和Jetpack?深度解析现代Android开发生态优雅草卓伊凡
495 0
为什么大厂要求安卓开发者掌握Kotlin和Jetpack?深度解析现代Android开发生态优雅草卓伊凡
|
IDE Java 开发工具
JetBrains IntelliJ IDEA 2025.1 发布 - 领先的 Java 和 Kotlin IDE
JetBrains IntelliJ IDEA 2025.1 (macOS, Linux, Windows) - 领先的 Java 和 Kotlin IDE
888 2
|
编译器 Android开发 开发者
Android经典面试题之Kotlin中Lambda表达式和匿名函数的区别
Lambda表达式和匿名函数都是Kotlin中强大的特性,帮助开发者编写简洁而高效的代码。理解它们的区别和适用场景,有助于选择最合适的方式来解决问题。希望本文的详细讲解和示例能够帮助你在Kotlin开发中更好地运用这些特性。
442 9
|
数据采集 前端开发 JavaScript
如何利用Java和Kotlin实现动态网页内容抓取
如何利用Java和Kotlin实现动态网页内容抓取
|
Java 编译器 测试技术
Kotlin31 协程如何与 Java 进行混编?
Kotlin31 协程如何与 Java 进行混编?
379 2
Kotlin31 协程如何与 Java 进行混编?
|
安全 Java 编译器
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
|
Java 开发工具 Android开发
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
|
安全 Java 编译器
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
167 2