考虑下面的java代码:
class Enclosing {
void method(){}
void method(String str){}
class Inner {
void method(){}
}
}
我正在阅读一本书,告诉我Inner.method()
会隐藏Enclosing.method()
的两个版本,这意味着如果我在课程method(aString)
的某处调用Inner
,则会出错
为什么语言设计得那样?
更新:
根据@Debosmit Ray给出的答案,它与阴影有关。我已经阅读了文档,并了解它是什么。
让我感到困惑的是,为什么方法阴影基于方法名称而非方法签名?
答案 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。再看一遍斜体中的单词,然后匹配!
请注意,这个类比并没有考虑到OOP的许多其他概念,但我认为这可能有助于您理解为什么部分问题。
回复更新:
让我感到困惑的是为什么方法阴影是基于方法名称而不是方法签名?
你的问题似乎不是一个有效的问题。你很快就会明白这一点。
你说阴影不是基于方法签名。如果你的意思是:"如果内部类方法与封闭类方法具有相同的签名,那么阴影就不会发生" ,那么你就错了。通过在内部类中创建另一个方法(如void method(String str)
)然后在内部类中调用该方法来尝试它。你会看到它显然是阴影。
当您在内部类中调用method(aString)
时出现错误的原因完全不同 - 方法method(String str)
甚至不存在于内部类范围内。< / p>
如果您需要进一步澄清,请随意。
答案 2 :(得分: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
使阴影超过全局方法和变量......