V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Febers
V2EX  ›  Java

Java 中通过 ClassName.super 获取直接父类的引用范围的疑惑

  •  
  •   Febers · 2019-12-11 14:05:24 +08:00 · 4417 次点击
    这是一个创建于 1841 天前的主题,其中的信息可能已经有所发展或是发生改变。

    在一个类的实例方法中,可以直接通过 ClassName.super ( ClassName 可省略)获取其直接父类的实例,但是在其他类中,使用 classInstance.super 引用其父类却报错,IDE 报错信息为:Class name expected here,Java 这么做的考虑是什么呢?

    学习 Java 的方法引用的时候遇到这个问题。方法引用有一种情况是,可以引用超类的方法,代码如下

    public abstract class Animal {
    
        public void action() {
            System.out.println("The animal is running");
        }
    }
    
    public class Bird extends Animal {
    
        public void action() {
            
            System.out.println("The bird is flying");
    
            //引用父类方法
            Runnable r = super::action;
            r.run();
        }
    }
    
    

    方法调用

    public class Main {
    
        public static void main(String[] args) {
    
            Bird bird = new Bird();
    
            //正常运行
            Runnable r = bird::action;
            r.run();
    
            //报错
            Runnable r1 = bird.super::action;
        }
    }
    
    17 条回复    2019-12-24 17:09:28 +08:00
    anakinsky
        1
    anakinsky  
       2019-12-11 14:55:44 +08:00
    我理解的是 super 本质是关键字 而非某对象的某成员变量 /函数 所以你无法使用对象调用关键字
    Febers
        2
    Febers  
    OP
       2019-12-11 15:17:37 +08:00
    @anakinsky #1 有这种可能,我的疑惑是,既然方法引用有这种形式,把父类的方法引用限定在子类内部,其他地方无法调用,会不会没什么必要

    无法使用对象调用关键字这件事还是存疑,实例化非静态内部类的时候,就会用到 OutClass.InnerClass innerClass = outClass.new OutClass.InnerClass() 这种形式,那么 outClass 后面的 .new 是不是调用关键字呢
    gotonull
        3
    gotonull  
       2019-12-11 16:08:26 +08:00
    super 是当前类的一个私有成员
    guxingke
        4
    guxingke  
       2019-12-11 16:36:51 +08:00
    1. super 是个关键字 , 用处就是标志当前类的父类, 此处可以 反编译 class 推断.
    2. 外部为什么无法使用 xxx.super.yyy , 编译器约束.
    3. 方法引用是个语法糖. 跟这个问题没有直接关系.

    4. 个人推断, 不保证正确, 并没有找到官方说明.
    chendy
        5
    chendy  
       2019-12-11 16:38:00 +08:00
    Febers
        6
    Febers  
    OP
       2019-12-11 19:34:30 +08:00
    @guxingke #4 反编译之后,super 确实是被替换成 animal 的实例
    Febers
        7
    Febers  
    OP
       2019-12-11 19:38:35 +08:00
    @chendy #5 里面好像也没有更多的信息~
    guyeu
        8
    guyeu  
       2019-12-11 21:00:37 +08:00
    因为不存在父类的实例这么个东西,你实例化一个类的时候,实例化出的就是一个对象,并不是父类有个对象子类有个对象。
    但是在类内部有需求访问这个类继承自父类的方法、属性,所以有了 super 这么个关键字,用来区分当前类和它的父类,不会引起混淆的情况下你省略这个关键字也没关系。
    但是在类外面不能使用这个关键字,这会破坏对象的封装和继承。
    KentY
        9
    KentY  
       2019-12-11 21:15:10 +08:00
    可能是我见的代码太少了, 好像我没见过 object.super.method()这种 super 夹在俩点儿中间的情况.
    "object.super"是什么呢, super 应该是没有这个用法吧? 通常"super"只在子类自身里用
    janus77
        10
    janus77  
       2019-12-11 21:29:42 +08:00
    好像是在类的内部才能正常使用 super,直接用点去调用是不行的
    mxalbert1996
        11
    mxalbert1996  
       2019-12-11 22:05:16 +08:00 via Android
    这样做有什么意义?允许在类外访问超类成员完全违反了 OOP 的原则,并且不经过子类直接调用父类方法会导致子类无法正常工作。
    Febers
        12
    Febers  
    OP
       2019-12-11 23:33:07 +08:00 via Android
    @guyeu #8 @guyeu #8 确实是这样的,其实问题在于 Function Reference 有一种形式是 TypeName.super::function,然后我又是在 Main 类中引用,想写出这种形式发现没法做到。
    Febers
        13
    Febers  
    OP
       2019-12-11 23:36:12 +08:00 via Android
    @KentY #9 确实是没有这种用法的,不过可以在类的实例方法中调用 SubClass.super.function。
    而我又想在外部 Main 类中进行方法引用,方法引用又有一一种父类方法引用的方式,所以才有这个问题
    Febers
        14
    Febers  
    OP
       2019-12-11 23:36:49 +08:00 via Android
    @janus77 #10 对的,还得是实例方法
    KentY
        15
    KentY  
       2019-12-12 00:08:07 +08:00
    @Febers 没太懂你说的"在类的实例方法中"SubClass.super.function...
    你是说你的例子 //报错 那行可以写 Bird.super.action() ?
    Febers
        16
    Febers  
    OP
       2019-12-12 11:52:54 +08:00
    @KentY #15 意思是 在 Bird 的非静态方法中可以调用 Bird.super.action() 实现调用父类方法的目的,但是在其他地方,想调用就不行
    比如在 Main 中,第一种调用形式,Bird.super.action 明显不行,因为 Bird 没有实例,无法调用它自身或者它父类(如果有的话)的 action 这一个实例方法;第二种调用形式,Bird bird = new Bird(); bird.super.action(),报错,才有了我的疑惑
    hitsmaxft
        17
    hitsmaxft  
       2019-12-24 17:09:28 +08:00
    写上 super 的地方, 会被 jvm 编译成 invokespecial 指令,
    如果用字节码 INVOKESPECIAL 可以对一个 object 调用任意合法方法, 用反射是能做到的,我没验证,只是搜了一下有现成例子。
    至于为什么 java 不允许, 继承时存在方法 override,不想被从外界胡乱使用。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5590 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 03:33 · PVG 11:33 · LAX 19:33 · JFK 22:33
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.