Почему этот статический внутренний класс не может использовать нестатический метод для внешнего класса?
Я сейчас читаю "Эффективную Java" Джошуа Блоха, и мне это нравится! Но на стр. 112 (Пункт 24) Блох пишет:
Статический класс-член - это самый простой тип вложенного класса. Его лучше всего воспринимать как обычный класс, который, как считается, объявляется внутри другого класса и имеет доступ ко всем членам входящих классов, даже к тем, кто объявлен частным.
И это меня действительно смущает. Я бы сказал:
Статический класс-член - это самый простой тип вложенного класса. Это лучше всего воспринимается как обычный класс, который, как считается, объявляется внутри другого класса и имеет доступ ко всем включенным классам статическим членам, даже объявленным частным.
Вот фрагмент, который иллюстрирует мое понимание цитаты:
public class OuterClass {
public void printMessage(String message) {
System.out.println(message);
}
private static class InnerClass {
public void sayHello() {
printMessage("Hello world!"); //error: Cannot make a static reference to the non-static method printMessage(String)
}
}
}
Вы можете видеть, что метод InnerClass sayHello не имеет доступа к методу printMessage OuterClass, поскольку он объявлен в статическом внутреннем классе, тогда как метод printMessage является методом экземпляра. Похоже, автор предлагает, чтобы статический класс-член мог получить доступ к нестатическим полям окружающего класса. Я убежден, что в своем последнем предложении я что-то не понял, но я не могу понять, что. Любая помощь будет оценена!
edit: Я изменил видимость двух методов, потому что это не имеет отношения к моему вопросу. Меня интересуют статические члены, а не частные члены.
Ответы
Ответ 1
Просто потому, что InnerClass
является static
, не означает, что он не мог получить ссылку на экземпляр OuterClass
помощью других средств, чаще всего как параметр, например
public class OuterClass {
private void printMessage(String message) {
System.out.println(message);
}
private static class InnerClass {
private void sayHello(OuterClass outer) {
outer.printMessage("Hello world!"); // allowed
}
}
}
Если InnerClass
не был вложен в OuterClass
, у него не было бы доступа к private
методу.
public class OuterClass {
private void printMessage(String message) {
System.out.println(message);
}
}
class InnerClass {
private void sayHello(OuterClass outer) {
outer.printMessage("Hello world!"); // ERROR: The method printMessage(String) from the type OuterClass is not visible
}
}
Ответ 2
Обратите внимание на сообщение об ошибке. Это не говорит, что у вас нет доступа. Он говорит, что метод не может быть вызван. Методы экземпляра ничего не значат, если экземпляр не вызывает их. Сообщение об ошибке сообщает вам, что у вас нет этого экземпляра.
Что Блох говорит вам, так это то, что если бы этот экземпляр существовал, код во внутреннем классе мог бы вызвать на нем методы частного экземпляра.
Скажем, у нас есть следующий класс:
public class OuterClass {
public void publicInstanceMethod() {}
public static void publicClassMethod() {}
private void privateInstanceMethod() {}
private static void privateClassMethod() {}
}
Если мы попытаемся вызвать эти частные методы из некоторого случайного класса, мы не сможем:
class SomeOtherClass {
void doTheThing() {
OuterClass.publicClassMethod();
OuterClass.privateClassMethod(); // Error: privateClassMethod() has private access in OuterClass
}
void doTheThingWithTheThing(OuterClass oc) {
oc.publicInstanceMethod();
oc.privateInstanceMethod(); // Error: privateInstanceMethod() has private access in OuterClass
}
}
Обратите внимание, что эти сообщения об ошибках говорят о закрытом доступе.
Если мы добавим метод OuterClass
к OuterClass
, мы можем вызвать эти методы:
public class OuterClass {
// ...declarations etc.
private void doAThing() {
publicInstanceMethod(); // OK; same as this.publicInstanceMethod();
privateInstanceMethod(); // OK; same as this.privateInstanceMethod();
publicClassMethod();
privateClassMethod();
}
}
Или добавим статический внутренний класс:
public class OuterClass {
// ...declarations etc.
private static class StaticInnerClass {
private void doTheThingWithTheThing(OuterClass oc) {
publicClassMethod(); // OK
privateClassMethod(); // OK, because we're "inside"
oc.publicInstanceMethod(); // OK, because we have an instance
oc.privateInstanceMethod(); // OK, because we have an instance
publicInstanceMethod(); // no instance -> Error: non-static method publicInstanceMethod() cannot be referenced from a static context
privateInstanceMethod(); // no instance -> Error: java: non-static method privateInstanceMethod() cannot be referenced from a static context
}
}
}
Если мы добавим нестатический внутренний класс, похоже, что мы можем делать магию:
public class OuterClass {
// ...declarations etc.
private class NonStaticInnerClass {
private void doTheThing() {
publicClassMethod(); // OK
privateClassMethod(); // OK
publicInstanceMethod(); // OK
privateInstanceMethod(); // OK
}
}
}
Однако здесь происходит обман: нестатический внутренний класс всегда связан с экземпляром внешнего класса, и то, что вы действительно смотрите:
private class NonStaticInnerClass {
private void doTheThing() {
publicClassMethod(); // OK
privateClassMethod(); // OK
OuterClass.this.publicInstanceMethod(); // still OK
OuterClass.this.privateInstanceMethod(); // still OK
}
}
Здесь OuterClass.this
является специальным синтаксисом для доступа к этому внешнему экземпляру. Но вам это нужно только в том случае, если оно неоднозначно, например, если внешний и внутренний классы имеют методы с тем же именем.
Заметим также, что нестатический класс все еще может делать то, что может сделать статический:
private class NonStaticInnerClass {
private void doTheThingWithTheThing(OuterClass oc) {
// 'oc' does *not* have to be the same instance as 'OuterClass.this'
oc.publicInstanceMethod();
oc.privateInstanceMethod();
}
}
Короче говоря, public
и private
всегда имеют доступ. То, что делает Блох, заключается в том, что внутренние классы имеют доступ к другим классам. Но никакая доступность не позволяет вам вызвать метод экземпляра, не сообщая компилятору, к какому экземпляру вы хотите его называть.
Ответ 3
То, как вы показали это, требует наследования. Но методы и поля могут быть доступны таким образом:
public class OuterClass {
private void printMessage(String message) {
System.out.println(message);
}
private static class InnerClass {
private void sayHello() {
OuterClass outer = new OuterClass();
outer.printMessage("Hello world!");
}
}
}
Ответ 4
Но, что статический внутренний класс не имеет доступа к функции printMessage, не связан с тем, что он является внутренним классом, но является статичным и не может вызывать нестатический метод. Я думаю, что использование слова "статические", которое вы предложили, было явным в первом предложении. То, что он указывает или предпочитает подчеркнуть, заключается только в том, что внутренний класс все же может получить доступ к закрытым методам своего родительского класса. Возможно, он просто хотел, чтобы в статическом/нестатическом различии в одном и том же предложении было излишним или запутанным.
Ответ 5
Как я это вижу, текст абсолютно прав. Статические классы-члены могут обращаться к закрытым членам закрывающих классов (вроде). Позвольте мне показать вам пример:
public class OuterClass {
String _name;
int _age;
public OuterClass(String name) {
_name = name;
}
public static OuterClass CreateOuterClass(String name, int age) {
OuterClass instance = new OuterClass(name);
instance._age = age; // Notice that the private field "_age" of the enclosing class is visible/accessible inside this static method (as it would also be inside of a static member class).
return instance;
}
}