482. Java 反射 - 访问私有方法 & 泛型擦除的影响
1. 访问私有方法 (Accessing Private Methods)
直接调用的问题
如果你在类外部通过反射尝试调用一个 private 方法,会抛出 IllegalAccessException。
class User {
private void secret() {
System.out.println("Top Secret!");
}
}
User user = new User();
Method secretMethod = User.class.getDeclaredMethod("secret");
secretMethod.invoke(user); // ❌ 会抛 IllegalAccessException
解决办法:setAccessible(true)
我们可以通过 关闭访问检查 来绕过限制:
secretMethod.setAccessible(true); // 关闭 Java 语言访问检查
secretMethod.invoke(user); // ✅ 正常执行
输出结果:
Top Secret!
📌 注意点:
-
setAccessible(true)只是关闭了 访问控制检查,它 不会改变方法的修饰符。 - 在 Java 模块系统(Java 9+) 下,如果类属于其他模块,且模块没有开放包,调用可能依然会失败(需要
--add-opens)。
2. 泛型与类型擦除 (Generics and Type Erasure)
在 Java 中,泛型是 编译期特性,运行时会发生 类型擦除。
这意味着:反射看到的其实是擦除后的类型,而不是源代码中的泛型。
错误示例
List<String> strings = new ArrayList<>();
Class<?> clazz = strings.getClass();
// ❌ 尝试获取 add(String) 方法
Method addString = clazz.getMethod("add", String.class);
运行结果:
Exception in thread "main" java.lang.NoSuchMethodException: java.util.ArrayList.add(java.lang.String)
📌 原因:
-
ArrayList<E>.add(E e)在运行时擦除为ArrayList.add(Object o) - 所以找不到参数是
String.class的方法
正确示例
List<String> strings = new ArrayList<>();
Class<?> clazz = strings.getClass();
// ✅ 使用 Object.class 才能找到 add()
Method addObject = clazz.getMethod("add", Object.class);
addObject.invoke(strings, "Hello Reflection!");
System.out.println(strings);
输出结果:
[Hello Reflection!]
📌 注意点:
- 反射操作是 基于擦除后的类型,因此需要用
Object.class。 - 这样会失去泛型带来的 类型安全(你甚至可以通过反射往
List<String>里塞一个Integer)。
3. 总结
- ✅ 私有方法
- 默认不可访问 → 需要
setAccessible(true) - 在模块化环境中可能需要
--add-opens
- 默认不可访问 → 需要
- ✅ 泛型与擦除
- 编译期有泛型,运行期被擦除
- 反射看到的参数类型通常是 Object
- 使用反射会丢失泛型检查,可能破坏类型安全