You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的访问控制规则针对的是「显式写出的类型名称」,而非编译器内部推断的类型。

咱们对比三种声明方式:

  1. 显式声明Child.InnerChild:self()方法返回的是InnerParent类型,和InnerChild类型不兼容,所以编译报错——这是类型匹配问题,和访问控制无关。
  2. 显式声明Parent.InnerParent:你在Main类中直接写出了Parent.InnerParent这个受保护的类型名称,而Main既不在Parent的包下,也不是Parent的子类,完全没有权限访问这个类型,所以编译报错——这是触发了访问控制检查。
  3. 用var声明:你没有在代码中主动写出Parent.InnerParent这个类型名称,编译器只是根据表达式new Child.InnerChild().self()的结果推断出变量类型,但访问控制规则不会因为“编译器内部用了这个类型”而报错,因为你没有显式引用它。

简单说:只要你不在代码中显式地写Parent.InnerParent,编译器就不会检查你对这个类型的访问权限——哪怕变量的实际静态类型是它。


二、为什么直接调用getClass()不行,转成Object就可以?

这个问题是同一个访问控制逻辑的延伸:

  • var推断出的变量静态类型是Parent.InnerParent,而Main类没有权限访问这个类型(既不同包也不是子类)。
  • 虽然InnerParent继承自Object,getClass()是Object的方法,但当变量的静态类型是你无权限访问的类型时,编译器会限制你调用该类型的任何方法——哪怕是继承来的Object方法也不行。因为编译器认为你没有资格直接操作这个“不可见”类型的实例。
  • 当你把变量强转成Object后,变量的静态类型变成了Object(这是所有类都能访问的公共类型),此时编译器允许你调用Object的方法,自然就能正常执行getClass()了。

总结一下

  1. var能编译通过,是因为Java访问控制只检查显式写出的类型名称,不检查编译器内部推断的类型;
  2. 无法直接调用getClass(),是因为变量的静态类型是不可访问的Parent.InnerParent,编译器禁止对该类型的实例进行任何方法调用;
  3. 强转成Object后可行,是因为Object是公共可访问类型,编译器允许调用其方法。

备注:内容来源于stack exchange,提问作者Lorenzo Yang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 17:27:58