Java 11中var为何能绕过protected访问限制?附getClass()访问异常问题
Java 11中var为何能绕过protected访问限制?附getClass()访问异常问题
嘿,这个问题挺有意思的,刚好戳中了Java访问控制规则和var类型推断的细节点,咱们一步步拆解明白:
先回顾你的代码结构
首先确认下你定义的类结构,方便后续分析:
Parent类(所在包:假设为parentpackage)
public abstract class Parent { protected abstract static class InnerParent { public InnerParent self() { return this; } } }
Child类(所在包:anotherpackage)
public class Child extends Parent { public static class InnerChild extends InnerParent {} }
Main类(所在包:与上述两个包不同)
import anotherpackage.Child; /** * Compiling with Java 11: */ public class Main { public static void main(String[] args) { // 预期报错:类型不匹配 // Child.InnerChild innerChild = new Child.InnerChild().self(); // 预期报错:无权限访问protected的Parent.InnerParent // Parent.InnerParent innerChild = new Child.InnerChild().self(); // 为什么能正常编译运行? var innerChild = new Child.InnerChild().self(); System.out.println(innerChild); // 为什么直接调用getClass()会提示无法访问? // System.out.println(innerChild.getClass().getName()); // 强转成Object后就能正常调用 System.out.println(((Object) innerChild).getClass().getName()); } }
一、为什么var能正常编译通过?
你之前的疑惑点在于:var只是语法糖,推断类型应该是Parent.InnerParent,为什么不会触发protected访问限制?
核心原因是:Java的访问控制规则针对的是「显式写出的类型名称」,而非编译器内部推断的类型。
咱们对比三种声明方式:
- 显式声明
Child.InnerChild:self()方法返回的是InnerParent类型,和InnerChild类型不兼容,所以编译报错——这是类型匹配问题,和访问控制无关。 - 显式声明
Parent.InnerParent:你在Main类中直接写出了Parent.InnerParent这个受保护的类型名称,而Main既不在Parent的包下,也不是Parent的子类,完全没有权限访问这个类型,所以编译报错——这是触发了访问控制检查。 - 用
var声明:你没有在代码中主动写出Parent.InnerParent这个类型名称,编译器只是根据表达式new Child.InnerChild().self()的结果推断出变量类型,但访问控制规则不会因为“编译器内部用了这个类型”而报错,因为你没有显式引用它。
简单说:只要你不在代码中显式地写Parent.InnerParent,编译器就不会检查你对这个类型的访问权限——哪怕变量的实际静态类型是它。
二、为什么直接调用getClass()不行,转成Object就可以?
这个问题是同一个访问控制逻辑的延伸:
var推断出的变量静态类型是Parent.InnerParent,而Main类没有权限访问这个类型(既不同包也不是子类)。- 虽然
InnerParent继承自Object,getClass()是Object的方法,但当变量的静态类型是你无权限访问的类型时,编译器会限制你调用该类型的任何方法——哪怕是继承来的Object方法也不行。因为编译器认为你没有资格直接操作这个“不可见”类型的实例。 - 当你把变量强转成
Object后,变量的静态类型变成了Object(这是所有类都能访问的公共类型),此时编译器允许你调用Object的方法,自然就能正常执行getClass()了。
总结一下
var能编译通过,是因为Java访问控制只检查显式写出的类型名称,不检查编译器内部推断的类型;- 无法直接调用
getClass(),是因为变量的静态类型是不可访问的Parent.InnerParent,编译器禁止对该类型的实例进行任何方法调用; - 强转成
Object后可行,是因为Object是公共可访问类型,编译器允许调用其方法。
备注:内容来源于stack exchange,提问作者Lorenzo Yang
相关产品推荐
相关产品推荐

