【问题标题】:Why does an inner class method hide all the enclosing class methods with the same name?为什么内部类方法会隐藏所有同名的封闭类方法?
【发布时间】:2016-07-04 19:30:06
【问题描述】:

考虑下面的java代码:

class Enclosing {
    void method(){}
    void method(String str){}

    class Inner {
        void method(){}
    }   
}

我正在读一本书,它告诉我Inner.method() 将隐藏Enclosing.method() 的两个版本,这意味着如果我在课堂上的某处调用method(aString) Inner 是错误的。

为什么语言是这样设计的?

更新
根据@Debosmit Ray 给出的答案,它与shadowing 有关。我已阅读文档并了解它是什么。

让我感到困惑的是,为什么方法阴影基于方法名称而不是方法签名

【问题讨论】:

  • 它不仅限于内部类。派生类具有相同的行为。这些只是 Java 的规则。 C++ 也是一样。

标签: java inner-classes shadowing


【解决方案1】:

非静态嵌套类或内部类被用作对仅在一个地方使用的类进行逻辑分组的一种方式;它使代码更具可读性并促进封装

来自 [docs],

如果一个类型的声明(如成员变量或参数 名称)在特定范围内(例如内部类或方法 定义)与封闭中的另一个声明具有相同的名称 范围,则声明遮蔽了封闭的声明 范围。

这里的阴影意味着如果你在外部类中有一个变量x,而在内部类中有另一个变量x,那么在内部类中修改x不会影响 x 在外部类中。

我非常喜欢这个问题和你提出的观点。我的解释能帮助你理解吗?

【讨论】:

  • 仍然令人困惑。文档似乎描述了 variables 阴影,同样的规则是否适用于 methods
  • @free6om 是的。完全一样。
  • 好的。可能是另一个问题,为什么方法阴影基于方法名称而不是方法签名
  • @free6om 是的。这将是一个完全不同的话题。更具体地说,我会说它是基于签名的,因为如果两个方法具有相同的名称,那么它的签名将确保唯一性。
  • @free6om 如果您的问题得到解决,如果您能接受/支持答案,那就太好了。来自here,“接受答案很重要,因为它既奖励海报解决您的问题,又通知其他人您的问题已解决。”
【解决方案2】:

如果您为您的问题找到了正确的标签,您就会更好地理解这个概念!在 SO 上查看shadowing 的标签信息。

在计算机编程中,在声明变量时会发生阴影 在一定范围内(决策块、方法或内部类)具有 与在外部作用域中声明的变量同名。这可能导致 混淆,因为可能不清楚后续使用哪个变量 阴影变量名称引用,这取决于名称解析 语言规则。

最早引入变量阴影的语言之一是 ALGOL, 它首先引入了块来建立范围。这也是 许多衍生编程语言都允许,包括 C++ 和 Java

C# 语言打破了这一传统,允许变量阴影 内部类和外部类之间,方法和它的方法之间 包含类,但不在 if 块与其包含之间 方法,或在 switch 块中的 case 语句之间。

Wikipedia link(虽然提供的不多)


为什么语言是这样设计的?

让我给你一个现实世界的类比来帮助你理解。

想象一个建筑物 (1),它有一个名为打开灯 (3) 的按钮。当您按下该按钮时,它会打开建筑物中的所有灯。现在想想该建筑物内的隔间 (2)。 隔间里有一盏小灯,还有一个类似的按钮,名为打开灯。现在,当您按下那个按钮时,您希望它做什么——打开建筑物的所有灯,还是只打开隔间中的灯?大概是后者。尽管这两个按钮具有相同的名称 (4),但它们的行为会根据它们的位置 (5)而有所不同。

现在将这个类比应用于 OOP。再看一遍斜体中的单词,然后匹配!

  1. 建筑 --> 封闭类
  2. 隔间 --> 内部类
  3. 开灯 --> 方法
  4. name --> 方法名/签名
  5. 地点 --> 范围

请注意,该类比并未考虑 OOP 的许多其他概念,但我认为它可能有助于您理解问题的为什么部分。


回复更新:

让我感到困惑的是,为什么方法阴影是基于方法名称而不是方法签名?

您的问题似乎不是一个有效的问题。你很快就会明白这一点。

你说阴影不是基于方法签名的。如果您的意思是:“如果内部类方法与封闭类方法具有相同的签名,则不会发生阴影”,那么你就错了。通过在内部类中创建另一个方法来尝试它,例如void method(String str),然后在内部类中调用该方法。你会看到它明显被遮住了。

而在内部类中调用method(aString) 时出现错误的原因完全是另外一回事——方法method(String str) 甚至不存在于内部类范围内。

如果您需要进一步说明,请随意。

【讨论】:

  • 我阅读了阴影的标签信息,它都在谈论变量,同样的规则是否适用于方法
  • @free6om 没错。阴影也适用于方法。看看更新的答案。我相信它会让你更清楚!如果是这样,您可能也想接受这个答案。 :)
  • 关于方法阴影,我的意思是如果我们在类Inner的其他方法中调用method(aString),说caller(),编译器会报错。即使方法 method(String str) 具有不同的签名,似乎 Inner 类中的方法 method() 和 Enclosure 类中的 method()method(String str) 都存在阴影。
  • @free6om 我已经解释了那部分。您无法在Inner 类中从caller() 调用method(aString) 并不意味着Inner 类中的method() 已经覆盖了封闭类中的method()method(String str),而是您是无法从类Inner 中调用method(aString),因为Inner 范围内没有带有签名method(String str) 的方法。查看当您尝试从Inner 中调用method(aString) 时收到的错误消息。你会得到this error
  • @free6om 我的回答对你有帮助吗?如果您需要进一步澄清,我们可以在聊天室继续。如果确实如此,请考虑将这个费力的答案标记为已接受,以向其作者提供一些功劳! :)
【解决方案3】:

这称为阴影

根据JLS 6.4.1

某些声明可能在其部分范围内被另一个同名声明所遮蔽,在这种情况下,不能使用简单名称来引用声明的实体。

在编程语言中有阴影很方便。比如在构造函数中,可以让参数和类字段变量同名,用this区分。

class Person {
    private String name;
    Person (String name) {
        this.name = name;
    }
}

在其他一些语言中,变量可能会被代码块遮挡,例如,在 C++ 中,您可以编写如下代码:

// C++ code:
int i = 10;

for(int i = 0; i != 5; ++i) {
    // use i from 0 to 4 here
}

for(int i = 100; i > 0; --i) {
    // use i from 100 to 1 here
}

// the first i is still 10 and can be used here

循环内部的变量i与外部的i不同。

为什么语言是这样设计的?

正如您在构造函数示例中看到的,有时变量可能真的意味着相同的东西。阴影使您可以使用相同的名称而无需创建新名称,这有点麻烦,因为命名变量并不简单。

在循环示例中,在 Java 中不受支持,但它是展示遮蔽优势的绝佳示例,有时在代码块中您可以声明一些临时变量,而无需修改外部的其他变量块。


为什么方法阴影是基于方法名称而不是方法签名?

JLS 15.12,有关于方法调用的解释。

可以看到,在第 1 步中,编译器会搜索可以调用该方法的作用域。最后找到Enclosing.Inner

在第 2 步,编译器将检查方法的签名。

因此,编译器会将Enclosing.Inner.method() 作为唯一可以调用的方法。这就是为什么你不能直接调用Enclosing.method(String str),即使它们有不同的方法签名。

如果你想在Inner 类中调用Enclosing.method(String str),你可以这样做:

class Inner {
    void method(){
        Enclosing.this.method("test");
    }
}

【讨论】:

  • for 循环永远不会编译。您已经在同一范围内初始化了 2 个具有相同名称的变量。此外,这也不能解释转发。
  • @DebosmitRay 那 for 循环是用 C++ 编译的,我的回答中提到了这一点。而且我不明白我的答案中没有解释什么?请告诉我,我可以改进我的答案,谢谢。
  • 我错过了那部分。如果你能把它加粗就太好了,因为问题是关于 java,而不是 cpp。
  • @DebosmitRay 我修改了我的答案。感谢您的建议。
  • 这是实际上回答问题的唯一答案
【解决方案4】:

因为 Scope, Method, Variables and constants ma​​ke shadow 覆盖了全局方法和变量...

【讨论】:

  • 感谢您的快速回复,我似乎没有理解make shadow的含义。还有更多细节吗?
猜你喜欢
  • 2011-09-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多