为什么内部类方法会隐藏所有具有相同名称的封闭类方法?

时间:2016-03-18 06:35:55

标签: java inner-classes shadowing

考虑下面的java代码:

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

    class Inner {
        void method(){}
    }   
}

我正在阅读一本书,告诉我Inner.method()会隐藏Enclosing.method()的两个版本,这意味着如果我在课程method(aString)的某处调用Inner,则会出错

为什么语言设计得那样?

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

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

4 个答案:

答案 0 :(得分:2)

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

来自[docs],

  

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

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

我非常喜欢这个问题以及你提出的观点。我的解释是否有助于你理解?

答案 1 :(得分:1)

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

  

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

     

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

     

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

Wikipedia link(虽然提供的内容不多)

  

为什么语言设计得那样?

让我给你一个真实的世界类比,可以帮助你理解。

想想一个建筑(1),它有一个名为打开灯的按钮(3)。当您按下该按钮时,它会打开建筑物中的所有灯光。现在想想那栋楼内的隔间(2)。 隔间中有一盏小灯,还有一个名为打开灯的类似按钮。现在,当您按下该按钮时,您希望它做什么 - 打开建筑物的所有灯光,或者只打开隔间中的灯泡?可能是后者。虽然两个按钮都具有相同的名称(4),但它们的行为取决于地点(5)。

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

  1. 建设 - >附上课程
  2. 隔间 - >内心课
  3. 开灯 - >方法
  4. 名称 - >方法名称/签名
  5. 地方 - >范围
  6. 请注意,这个类比并没有考虑到OOP的许多其他概念,但我认为这可能有助于您理解为什么部分问题。

    回复更新:

      

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

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

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

    当您在内部类中调用method(aString)时出现错误的原因完全不同 - 方法method(String str)甚至不存在于内部类范围内。< / p>

    如果您需要进一步澄清,请随意。

答案 2 :(得分:1)

这称为阴影

根据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),即使他们有不同的方法签名。

如果您想在Enclosing.method(String str)课程内拨打Inner,可以执行以下操作:

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

答案 3 :(得分:0)

因为Scope, Method, Variables and constants 使阴影超过全局方法和变量......