Android 高级工程师面试:泛型与反射 近1年高频追问 22 题
文章目录
- 学习建议
- 基础层(8 题)
- #1 为什么需要泛型?Android 里解决什么问题? 🔥
- 标准回答
- 面试官可能继续追问
- #2 什么是类型擦除?擦成什么? 🔥
- 标准回答
- 面试官可能继续追问
- #3 类型擦除带来哪些局限? ⭐
- 标准回答
- 面试官可能继续追问
- #4 泛型形参 `T` 和通配符 `?` 有什么区别? ⭐
- 标准回答
- 面试官可能继续追问
- #5 `List<?>` 和 `List<Object>` 一样吗? ⭐
- 标准回答
- 面试官可能继续追问
- #6 运行时如何获取泛型的真实类型? 🔥
- 标准回答
- 面试官可能继续追问
- #7 泛型能使用基本类型吗? ⭐
- 标准回答
- 面试官可能继续追问
- #8 反射是什么?在 Android 中能做什么? ⭐
- 标准回答
- 面试官可能继续追问
- 进阶层(7 题)
- #9 PECS 原则是什么?怎么记? 💡
- 标准回答
- 面试官可能继续追问
- #10 Gson 为什么需要 `TypeToken`? 🔥
- 标准回答
- 面试官可能继续追问
- #11 Retrofit 的 `Call<T>` 泛型是怎么工作的? 🔥
- 标准回答
- 面试官可能继续追问
- #12 获取 `Class` 对象有哪几种方式? ⭐
- 标准回答
- 面试官可能继续追问
- #13 Android 对反射有哪些限制? 🔥
- 标准回答
- 面试官可能继续追问
- #14 什么是隐藏 API?和反射什么关系? 🔥
- 标准回答
- 面试官可能继续追问
- #15 `RecyclerView.Adapter` 为什么要用泛型? ⭐
- 标准回答
- 面试官可能继续追问
- 核心层(7 题)
- #16 `Method.invoke` 的原理与注意点? 💡
- 标准回答
- 面试官可能继续追问
- #17 R8 / ProGuard 对反射有什么影响? ⭐
- 标准回答
- 面试官可能继续追问
- #18 Kotlin `reified` 和 Java 泛型有什么区别? 🔥
- 标准回答
- 面试官可能继续追问
- #19 为什么不能创建泛型数组 `new T[]`? 💡
- 标准回答
- 面试官可能继续追问
- #20 反射如何破坏单例? 💡
- 标准回答
- 面试官可能继续追问
- #21 Lambda 与泛型擦除、desugar 的关系? ⭐
- 标准回答
- 面试官可能继续追问
- #22 工程上什么时候该用反射?什么时候禁止? 🔥
- 标准回答
- 面试官可能继续追问
- 面试策略速查
- 必背知识(🔥)
- 高频追问
- 加分项(💡)
- 容易踩坑
- 完整链路一句通
- 相关推荐
学习建议
| 目标层级 | 建议 |
|---|---|
| 初级 | 掌握基础层 8 题:为何用泛型、类型擦除、T 与?区别;工程联想看追问第 1 条 |
| 中级 | 通读全文;进阶层能讲清 Retrofit 泛型解析、Android 反射限制、Adapter 泛型设计 |
| 高级 | 核心层加分项必答:PECS、reified、R8 keep 规则;能串联「擦除→Token→动态代理→混淆」链路 |
基础层(8 题)
#1 为什么需要泛型?Android 里解决什么问题? 🔥
标准回答
泛型在编译期约束类型,减少强转与ClassCastException,让 API 自描述。编译器在调用点做类型检查,擦除前已拦截大部分类型错误。
面试官可能继续追问
Android 里泛型解决什么问题?
List<User>、Retrofit Call<ApiResponse>、LiveData<T>都靠泛型表达数据形态;无泛型则到处强转,埋运行时崩溃风险。泛型能提升运行时性能吗?
主要提升类型安全与可读性,运行时因擦除几乎无性能差。Kotlin 还需要关心 Java 泛型吗?
需要,SDK 与三方库大量 Java 泛型 API,互操作必考。
#2 什么是类型擦除?擦成什么? 🔥
标准回答
Java 泛型是编译期语法糖,运行时泛型参数被擦除,替换为边界类型或Object;运行时List<String>与List<Integer>都是List。
面试官可能继续追问
Android 里理解类型擦除有什么用?
读 Gson、Retrofit 源码或写TypeToken都必须先理解擦除;部分方法签名仍保留供反射读取。擦除后还能做
instanceof List<String>吗?
不能,编译报错;只能instanceof List。Kotlin 也擦除吗?
同样擦除,除非inline reified在编译期展开具体类型。
#3 类型擦除带来哪些局限? ⭐
标准回答
不能new T();不能创建T[];运行时难区分List<String>与List<Int>;静态字段不能用类自身泛型参数。工程绕过:工厂模式、TypeToken、Class<T>入参。
面试官可能继续追问
Android 里擦除局限怎么绕?
Gson 解析List<Bean>、Retrofit 解析Call<T>都依赖 TypeToken 或父类签名还原类型。为什么要有擦除这种设计?
兼容 Java 5 前字节码与迁移成本,历史权衡非技术最优。getClass()能区分泛型吗?
不能,只能拿到原始List.class。
#4 泛型形参T和通配符?有什么区别? ⭐
标准回答
T:命名类型参数,类/方法内可多处一致引用。?:通配符表未知类型,常配extends/super表上/下界。
面试官可能继续追问
Android 里
T和?怎么用?
Repository 返回List<? extends Item>可安全只读遍历;只写场景用? super T。方法泛型
<T>和类泛型<T>能混用吗?
可以,注意遮蔽;阅读 Retrofit 接口时区分方法级与接口级。?能当T用吗?
不能随意赋值给T,通配符捕获需辅助方法。
#5List<?>和List<Object>一样吗? ⭐
标准回答
List<?>:未知元素类型的只读安全列表,不能add(除null)。List<Object>:可添加任意对象,但无法接受List<String>赋值(逆变不成立)。
面试官可能继续追问
Android 里
List<?>怎么用?
工具方法只遍历展示用List<?>;实际业务更常用具体泛型或 Kotlin 集合。为什么
List<String>不能赋给List<Object>?
若允许,放入非 String 会破坏 String 列表类型安全。Kotlin
List<Any>对应哪个?
更接近可读写的顶层类型,但仍与 Java 泛型逆变规则互操作时需注意。
#6 运行时如何获取泛型的真实类型? 🔥
标准回答
局部变量泛型运行时拿不到。可行路径:读父类/接口ParameterizedType;getGenericSuperclass()可还原ApiResponse<User>中的User。
面试官可能继续追问
Android 里怎么获取泛型真实类型?
GsonTypeToken<T>{}匿名子类固化签名;Retrofit 解析Call<T>同理——面试最高频泛型题之一。字段上的
List<User>能反射拿到吗?
字段Type用getGenericType()可读ParameterizedType。ProGuard 会影响 ParameterizedType 吗?
若混淆改类名,反射类名需 keep 规则配合。
#7 泛型能使用基本类型吗? ⭐
标准回答
泛型T只能是引用类型,基本类型需用包装类。JVM 泛型基于对象;Kotlin 基本类型在泛型处仍会装箱。
面试官可能继续追问
Android 里基本类型泛型怎么优化?
SparseArray用int键避免Integer装箱,是替代Map<Integer, V>的常用优化。自动装箱有性能问题吗?
热点路径频繁装箱增加分配,可用Sparse*数组或原始类型集合库。Kotlin
Int和 Javaint在泛型里?
泛型处都用包装/对象表示。
#8 反射是什么?在 Android 中能做什么? ⭐
标准回答
反射是运行时检视/操作类、方法、字段的 API(Class、Method、Field)。能做什么:依赖注入、序列化、动态代理、测试 Mock。
面试官可能继续追问
Android 里反射能做什么?
Retrofit、Gson 内部大量使用;应用层反射系统私有 API 受限,应优先公开 API 或 Jetpack。反射比直接调用慢多少?
有开销,热点路径避免;框架初始化阶段可接受。Kotlin 还要学 Java 反射吗?
要,互操作与读库源码离不开java.lang.reflect。
进阶层(7 题)
#9 PECS 原则是什么?怎么记? 💡
标准回答
Producer Extends:只读生产者用? extends T,如List<? extends Fruit>安全遍历。Consumer Super:只写消费者用? super T,如List<? super Apple>可addApple。
面试官可能继续追问
Android 里 PECS 怎么用?
工具方法设计集合入参时按读写方向选通配符,避免无脑List<T>导致调用方协变失败。Collections.copy签名体现了 PECS 吗?
是,copy(List<? super T> dest, List<? extends T> src)是教科书例子。Kotlin 有 PECS 吗?
声明处型变(out/in)编译期处理,思想对应。
#10 Gson 为什么需要TypeToken? 🔥
标准回答
解析List<User>时擦除使运行时只剩List.class,Gson 不知元素类型。new TypeToken<List<User>>(){}把泛型写在匿名子类父类签名,getType()得完整ParameterizedType。
面试官可能继续追问
Android 里 Gson TypeToken 典型坑?
网络层 JSON 解析必考;错误写法会反序列化成LinkedTreeMap导致强转崩溃。不用 TypeToken 会怎样?
fromJson(json, List.class)得到List<Map>,强转崩溃或逻辑错。Moshi/Kotlinx.serialization 还要 TypeToken 吗?
Moshi 用Types/KotlinJsonAdapterFactory;Kotlin 反射或代码生成路径不同。
#11 Retrofit 的Call<T>泛型是怎么工作的? 🔥
标准回答
接口声明方法返回Call<User>,泛型写在方法签名。Retrofit 反射读Method的ParameterizedType得User,再交Converter.Factory选 Gson/Moshi。
面试官可能继续追问
Android 里 Retrofit
Call<T>怎么工作?
动态代理生成接口实现,无手写类——解释「注解 + 泛型为何能自动解析 JSON」。suspend fun返回值怎么处理?
挂起适配器读返回类型(含Response<T>包装),仍依赖泛型签名。混淆后 Retrofit 失败常见原因?
数据模型或接口方法签名被混淆,需 keep model 与接口。
#12 获取Class对象有哪几种方式? ⭐
标准回答
三种方式:类名.class;对象.getClass();Class.forName("全限定名")。区别:.class不触发静态初始化;forName默认会触发。
面试官可能继续追问
Android 里获取
Class怎么用?Activity跳转常用Xxx.class;forName注意类加载器与 ProGuard keep,插件化才常碰。.class会触发静态初始化吗?
不会;forName默认会触发。Kotlin 的
User::class.java是什么?
等价User.class,Kotlin 反射还有KClass。
#13 Android 对反射有哪些限制? 🔥
标准回答
随版本收紧,非 SDK 接口(灰名单/黑名单)反射会抛异常或警告。隐藏ActivityThread等内部 API 不可靠;Android 9+ 更严,应使用公开 API。
面试官可能继续追问
Android 反射限制工程影响?
反射自己代码需配 R8 keep;反射系统私有字段是技术债,上架与兼容性风险高。如何检测用了非 SDK API?
strictmode、lint、veridex、灰名单日志。Android 16 还要背黑名单吗?
知道趋势「越来越严」+ 工程替代方案比背列表重要。
#14 什么是隐藏 API?和反射什么关系? 🔥
标准回答
@hide或未公开 SDK 中的类/方法,Google 用灰名单/黑名单限制反射调用。反射是调用手段;隐藏 API 是调用目标——二者叠加形成「越来越严」的管控。
面试官可能继续追问
Android 里隐藏 API 正确做法?
用公开 API、AndroidX、厂商文档能力;Hook 框架在新版系统极易失效。灰名单和黑名单区别?
限制强度不同,黑名单基本不可用,随版本迁移。开发插件化为什么难?
类加载、隐藏 API、签名与组件注册都受系统策略约束。
#15RecyclerView.Adapter为什么要用泛型? ⭐
标准回答
Adapter<VH extends ViewHolder>让onCreate/onBind返回/接收具体VH,避免强转。常配合List<Item>或DiffUtil.ItemCallback<Item>保持一致。
面试官可能继续追问
Android 里 Adapter 泛型怎么用?
多类型列表用 sealed class + 多 ViewHolder;是业务代码里最常见的泛型场景之一。多 ViewType 怎么保持类型安全?
sealed/when分支绑定不同 VH,或泛型委托给多个 adapter。Compose
LazyColumn还要 Adapter 泛型吗?
UI 层换 Compose,但 diff/列表数据模型泛型思想相同。
核心层(7 题)
#16Method.invoke的原理与注意点? 💡
标准回答
invoke通过Method对象调用目标;实例方法传对象,静态方法传null。内部做访问检查与装箱;setAccessible(true)可破坏封装。
面试官可能继续追问
Android 里
Method.invoke注意什么?
Retrofit 动态代理、测试框架会用到;Android 9+ 对非公开成员更严格,热点路径避免反射。invoke 能传基本类型吗?
用包装类或MethodType,注意重载匹配。反射调用能被内联优化吗?
难,热点路径避免反射。
#17 R8 / ProGuard 对反射有什么影响? ⭐
标准回答
混淆改名/删除「看似未使用」的类与方法,导致Class.forName、getDeclaredField失败。keep 范围:Gson 模型、Retrofit 接口、序列化字段、JNI 类。
面试官可能继续追问
Android 里 R8 对反射怎么配?
AGP 默认 R8;面试结合项目说「模型 keep、反射入口 keep、release 包跑核心链路验证」。Gson 要 keep 哪些?
数据类字段名、无参构造、泛型签名相关类。怎么验证混淆正确?
release 包跑核心链路 + 反射/JSON 单测。
#18 Kotlinreified和 Java 泛型有什么区别? 🔥
标准回答
Java 泛型擦除,运行时无法用T::class拿具体类型。Kotlininline+reified T编译期嵌入具体类型,运行时可用T::class。
面试官可能继续追问
Android 里
reified怎么用?inline fun <reified T> gson()可简化 TypeToken;跨模块非 inline 边界仍擦除。reified 能用在类里吗?
不能,只能 inline 函数或伴生对象 inline。和 TypeToken 比哪个好?
Kotlin 项目优先 reified 简化;Java 库仍靠 TypeToken。
#19 为什么不能创建泛型数组new T[]? 💡
标准回答
数组运行时保留元素类型且协变;String[]可当Object[]写入Object,与擦除后的泛型不一致。若允许new T[],会破坏类型安全(堆污染)。
面试官可能继续追问
Android 里泛型数组替代方案?
改用ArrayList<T>;toArray(T[])需传入具体类数组实例。List<String>[]为什么也不建议?
堆污染(heap pollution),编译警告,运行可能ArrayStoreException。Kotlin
Array<T>呢?
运行时仍是对象数组,元素类型检查在 JVM 层。
#20 反射如何破坏单例? 💡
标准回答
单例靠私有构造,反射setAccessible(true)+newInstance可造第二个实例。防御:枚举单例;构造器内防二次创建抛异常。
面试官可能继续追问
Android 面试里反射破单例考什么?
考反射权力边界与枚举单例优势;SDK 单例不要求你破坏。序列化也能破坏单例吗?
能,需readResolve;枚举单例免疫。依赖注入还要单例吗?
用 Hilt 作用域管理,少手写 DCL。
#21 Lambda 与泛型擦除、desugar 的关系? ⭐
标准回答
Java 8 lambda 在 Android 经 desugar 转匿名类或invokedynamic,非 JVM 原生 Java 8。捕获的泛型变量仍受擦除限制;Comparator<User>的比较逻辑在User类型上下文。
面试官可能继续追问
Android 里 Lambda 与擦除关系?
读字节码或排查低版本 API 时需知 AGP 脱糖;Stream 部分 API 可用但 UI 热路径慎用。Stream 在 Android 能用吗?
部分 API 需 desugar,且注意性能与 API 级别。Kotlin lambda 和 Java lambda 互操作?
SAM 转换,Function0等接口注意 null 与 inline。
#22 工程上什么时候该用反射?什么时候禁止? 🔥
标准回答
该用:框架层(序列化、DI、动态代理)、测试、读自身模块且已 keep 的类。禁止/慎用:调系统隐藏 API、替代简单工厂、热点路径、无 keep 的 release 反射。
面试官可能继续追问
Android 工程上反射怎么取舍?
现代栈优先注解处理器(Room、Moshi codegen)、Hilt、公开 API;反射是备胎,非默认架构。注解处理器和反射优劣?
编译期生成代码,无运行时开销、无混淆坑,首选。插件化为什么依赖反射?
动态加载 Dex 需ClassLoader加载未知类,复杂度高。
面试策略速查
| 面试等级 | 建议掌握 |
|---|---|
| 初级 | ★★★☆☆ |
| 中级 | ★★★★☆ |
| 高级 | ★★★★★ |
必背知识(🔥)
类型擦除、获取泛型真实类型、TypeToken、RetrofitCall<T>、Android 反射限制、隐藏 API、reified 对比、工程反射取舍
高频追问
「擦除后怎么解析 List」「Gson 为何用 TypeToken」「反射为何被限制」「R8 怎么 keep」
加分项(💡)
PECS、invoke 原理、泛型数组禁令、反射破单例、Lambda desugar
容易踩坑
以为运行时能instanceof List<String>;fromJson不传 TypeToken;release 未 keep 模型;调灰名单 API;滥用反射替代简单工厂
完整链路一句通
泛型反射一条线:编译期泛型保类型安全,擦除后靠父类签名/TypeToken/reified 找回真实类型,Retrofit 与 Gson 据此解析 JSON;反射能读写字节码元数据但受隐藏 API 与 R8 约束,工程上注解处理 + 公开 API 优先,反射只作框架与备胎。
相关推荐
Android 高级工程师面试:Java 基础知识 近1年高频追问 22 题
Android 高级工程师面试:Java 多线程与并发 近1年高频追问 22 题