Метод локального внутреннего класса и внутреннего класса
Ниже приведен код вывода middle
. Может ли кто-нибудь объяснить подробно, как это происходит?
Это потому, что объявление "внутренней" версии class A
происходит после создания экземпляра class A
в методе go()
?
class A {
void m() {
System.out.println("outer");
}
}
public class MethodLocalVSInner {
public static void main(String[] args) {
new MethodLocalVSInner().go();
}
void go() {
new A().m();
class A {
void m() {
System.out.println("inner");
}
}
}
class A {
void m() {
System.out.println("middle");
}
}
}
Ответы
Ответ 1
Я предполагаю, что вы ожидали вызова метода локального класса. Этого не произошло, потому что вы используете new A()
вне области локального класса. Таким образом, он обращается к ближайшему ближайшему кандидату в области, который будет внутренним классом. Из JLS & sect; 6.3:
Объем объявления локального класса, сразу же заключенного в блок (§14.2), представляет собой остальную часть непосредственно вложенного блока, включая его собственное объявление класса.
Таким образом, new A()
в первой строке метода не будет обращаться к локальному классу, появляющемуся после него. Если вы переместите объявление класса до этого, вы получите ожидаемый результат.
Также см. JLS & sect; 14.3, который содержит аналогичный пример.
Ответ 2
Вы получаете результат "средний" из-за порядка, в котором у вас есть код. Так как область class A
с областью метода имеет после ваш вызов new A()
, вы получаете результат "средний". Если вы переключитесь на порядок следующим образом, вы получите результат "внутренний":
void go() {
class A {
void m() {
System.out.println("inner");
}
}
new A().m();
}
Вывод:
inner
Порядок приоритета для экземпляра class A
, от высокого к низкому, равен:
Дополнительную информацию можно найти в официальной Спецификации языка Java, обсуждающей внутренние классы.
Ответ 3
Причина inner
не печатается (6.3):
Объем локального объявления класса, заключенного в блоке, является остальной частью непосредственно вложенного блока, включая его собственное объявление класса.
(Класс, объявленный внутри метода, называется локальным классом.)
Так что A
не может ссылаться на локальный класс, потому что выражение new A()
происходит до его объявления. Другими словами, локальные классы имеют сходную область с локальными переменными.
Причина middle
печатается вместо outer
заключается в том, что внутренний класс A
затеняет класс верхнего уровня A
(6.4.1):
Объявление d
типа с именем n
затеняет объявления любых других типов с именем n
, которые находятся в области [& hellip;] из d
.
Это означает, что в любом месте тела MethodLocalVSInner
неквалифицированный A
должен ссылаться на внутренний класс.
Если вы знакомы с затенением переменных-членов, например:
class Example {
int x;
void setX(int x) {
// ┌ 'x' refers to the local method parameter
this.x = x;
}
}
По существу то же самое происходит с объявлениями класса.
Ответ 4
Случай 1:
void go() {
new A().m();
class A {
void m() {
System.out.println("inner");
}
}
}
В этом случае, если вы используете свой метод вне области локального класса. Поэтому для печати middle
Случай 2:
void go() {
class A {
void m() {
System.out.println("inner");
}
}
new A().m();
}
В этом случае он напечатает inner
класс becase теперь находится в области.
Ответ 5
в методе:
void go() {
new A().m();
class A {
void m() {
System.out.println("inner");
}
}
}
когда метод запускается, первая строка будет выполнена
new A().m();
и потому что внутренний класс уже в области видимости, поэтому объект для этого класса будет создан, и метод m
будет вызываться для inner class
не для local method class
, потому что он все еще не в области видимости. поэтому вы получаете middle
как вывод.
но если вы измените свой метод как:
void go() {
class A {
void m() {
System.out.println("inner");
}
}
new A().m();
}
Ваш локальный класс методов теперь будет в области видимости и будет иметь более высокое предпочтение, так что теперь вы получите вывод inner
.
Ответ 6
Вы вызываете метод go
, используя экземпляр MethodLocalVSInner
Внутри метода go
вы создаете экземпляр A()
здесь, поскольку вы явно не импортируете внешний A class
, а непосредственный внутренний класс - после оператора вызова метода, JVM выбирает inner class A
, который находится на уровне класса MethodLocalVSInner
, и выполняет метод go внутри этого