为什么内部类可以覆盖私有的final方法?

66

我想知道将一个私有方法声明为final是否有意义,我认为没有意义。但是我想象中存在一种独特的情况,并编写了代码来解决:

public class Boom {

    private void touchMe() {
        System.out.println("super::I am not overridable!");
    }

    private class Inner extends Boom {

        private void touchMe() {
            super.touchMe();
            System.out.println("sub::You suck! I overrided you!");
        }
    }

    public static void main(String... args) {
        Boom boom = new Boom();
        Boom.Inner inner = boom.new Inner();
        inner.touchMe();
    }
}

代码编译并正常运行。我想:"我应该将touchMe()声明为final",于是我这么做了:

public class Boom {

    private final void touchMe() {
        System.out.println("super::I am not overridable!");
    }

    private class Inner extends Boom {

        private void touchMe() {
            super.touchMe();
            System.out.println("sub::You suck! I overrided you!");
        }
    }

    public static void main(String... args) {
        Boom boom = new Boom();
        Boom.Inner inner = boom.new Inner();
        inner.touchMe();
    }
}

它也可以工作并告诉我

chicout@chicout-linlap:~$ java Boom
super::I am not overridable!
sub::You suck! I overrided you!

为什么?


这是“隐藏”或“影子”之类的东西。这确实显示了为什么扩展外部类有点邪恶(即使在枚举中也是如此)。/ 尝试 Boom inner = boom.new Inner(); - Tom Hawtin - tackline
23
为什么应该使用 @Override 注解的好例子。确保你实际上是在覆盖该方法。 - Shawn
它不应该写两次 super... 吗?更新 不需要。我的错。我明白了。 - DerMike
这里给出的解释还有待改进。其中大部分陈述内部类的方法是与包含类中的方法“分离的”,只是恰好共享相同的名称(我可以补充,是相同的签名)。然而,重写方法也是一个独立的方法,只是恰好具有相同的名称。重写的魔力不在于某种基础的“相似性”,而在于调度机制的行为。 - Nate C-K
这个 super.touchMe();... - Yousha Aleayoub
5个回答

87

私有方法不能被重写(私有方法不会被继承!)实际上,声明私有方法是否为final没有区别。

你声明的两个方法Boom.touchMeBoom.Inner.touchMe是两个完全独立的方法,只是恰好共享相同的标识符。 super.touchMe指向不同于touchMe的方法,仅仅是因为Boom.Inner.touchMe 遮蔽了 Boom.touchMe(而不是覆盖它)。

可以通过多种方式证明这一点:

  • 正如你自己发现的那样,如果将方法更改为public,则编译器会抱怨,因为你会突然尝试覆盖一个final方法。

  • 如果保持方法为private并添加@Override注释,编译器会报错。

  • 如alpian所指出的那样,如果将Boom.Inner对象强制转换为Boom对象(((Boom) inner).touchMe()),则会调用Boom.touchMe(如果它确实被覆盖,转换将不起作用)。

相关问题:


13
我可能对此有误解,但我认为一个关键的误解是 super.touchMe() 的意思是“调用该方法覆盖的方法”。虽然它经常以这种方式工作,但它实际上的意思是“在该类继承的类中调用 touchMe() 方法”。这就是为什么你可以调用 super.touchMe() - 它碰巧也具有相同签名的方法。 - corsiKa
1
是的,谢谢!我现在明白了。当我将其公开时,出现了编译时错误。 - chicout
4
很容易看出 Inner 没有覆盖 Boom,给该方法添加 @Override 会生成编译器错误。 - TDJoe
4
根据《Java 语言规范》中的描述:“在一个类中声明为 private 的成员不会被该类的子类继承”。在内部类Inner.touchMe()调用super.touchMe()时,编译器会在外部类中生成一个默认访问级别的、具有唯一名称的访问器方法,以转发对私有方法的调用。然后,它将super.touchMe()更改为调用访问器方法。 - Ted Hopp

8
我认为这里确实有两种不同的方法,可以通过将主函数更改如下来很好地证明这一点:

我认为,通过以下更改你的主要函数,可以很好地展示这里确实存在两种不同的方法:

public static void main(String... args) {
    Boom boom = new Boom();
    Boom.Inner inner = boom.new Inner();
    inner.touchMe();
    System.out.println("And now cast it...");
    ((Boom)(inner)).touchMe();
}

现在输出的是:

super::I am not overridable!
sub::You suck! I overrided you!
And now cast it...
super::I am not overridable!

Inner 中调用 super 的原因是因为你在搜索一个叫做 touchMe 的方法,这个方法存在于你的父类 (Boom) 中,并且对于 Inner 可见,因为它与同一类中。


6
私有方法对子类或其他类不可见,因此它们可以具有相同的名称,但不会互相覆盖。
尝试添加 @Override 注解,你会得到一个编译错误。

1
私有方法对于除了它所在的类以外的任何其他类都是不可见的,除非你考虑反射。 - user
真的 - 但我认为这超出了原始问题的范围,并且不会影响主要问题。 - DNA

2
您可以覆盖该方法,因为它对每个类都是私有的。

1

您刚刚声明了一个同名的方法。由于内部类本身就是类的成员,因此您可以调用该类的私有成员。希望这个修改能够详细解释它。

public class Boom {

    private final void touchMe() {
        System.out.println("super [touchMe] ::I am not overridable!");
    }

    public void overrideMe(){
        System.out.println("super [overrideMe]::I am overridable!");
    }

    private class Inner extends Boom {

        private void touchMe() {
            super.touchMe();
            System.out.println("sub [touchMe]::You suck! I overrided you!");
        }

        public void overrideMe(){
            System.out.println("sub [overrideMe] ::I overrided you!");
        }
    }

    public static void main(String... args) {
        Boom boom = new Boom();
        Boom.Inner inner = boom.new Inner();

        inner.touchMe();

        Boom newBoom = inner;
        newBoom.touchMe();
        newBoom.overrideMe();
    }
}



super [touchMe] ::I am not overridable!
sub [touchMe]::You suck! I overrided you!
super [touchMe] ::I am not overridable!
sub [overrideMe] ::I overrided you!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接