Почему добавление открытого поля в анонимный класс на Java не работает?

У меня есть примерный класс, определенный ниже:

public class FooBar {

  void method1(Foo foo){ // Should be overwritten
    ...
  }

}

Позже, когда я попробую это:

FooBar fooBar = new FooBar(){
  public String name = null;
  @Override
  void method1(Foo foo){
    ...
  }
};

fooBar.name = "Test";

Я получаю сообщение о том, что поле имени не существует. Почему?

Ответ 1

Поскольку тип переменной "fooBar" равен FooBar (тип времени выполнения объекта в указанной переменной соответствует типу реализации анонимного класса FooBar, который также является подтипом FooBar)..

... и тип FooBar не имеет указанного члена. Следовательно, ошибка компиляции. (Помните, что переменная "fooBar" может содержать любой объект, соответствующий FooBar, даже те, у кого нет name, и, следовательно, компилятор отклоняет код, который не является безопасным для типа.)

Изменить: для одного решения см. неповторимый ответ, который использует "Декларация локального класса" для создания нового именованного типа (для замены анонимный тип в сообщении).

Java не поддерживает способ сделать это (в основном: Java не поддерживает полезный вывод типа), хотя следующее работает, даже если это не очень полезно:

(new foobar(){
  public String name = null;
  @Override
  void method1(Foo foo){
    ...
  }
}).name = "fred";

Счастливое кодирование.


Оба Scala и С# поддерживают требуемый тип вывода и, таким образом, анонимные типы специализированных локальных переменных. (Хотя С# не поддерживает анонимное распространение существующих типов). Java, однако, не делает.

Ответ 2

Локальный класс будет делать

{
    class MyFooBar extends FooBar{
        String name = null;
        ...
    };

    MyFooBar fooBar = new MyFooBar();

    fooBar.name = "Test";
}

Ответ 3

Попробуй это.

@SafeVarargs
public static <T> void runWithObject(T object, Consumer<T>... progs) {
    for (Consumer<T> prog : progs)
        prog.accept(object);
}

а также

runWithObject(
    new FooBar() {
        String name = null;
        @Override
        void method1(Foo foo) {
            System.out.println("name=" + name);
        }
    },
    object -> object.name = "Test",
    object -> object.method1(new Foo())
);

результат:

name=Test

Или вы можете использовать var как это в Java 10 или более поздней версии.

var fooBar = new FooBar() {
    public String name = null;

    @Override
    void method1(Foo foo) {
        System.out.println("name=" + name);
    }
};
fooBar.name = "Test";
fooBar.method1(new Foo());

Ответ 4

fooBar является ссылкой на объект типа fooBar, и у таких объектов нет поля name. Просто как тот. А поскольку это анонимный тип, единственный способ ссылаться на это поле через ссылку this.

Ответ 5

Создается объект типа foobar. Компилятор знает только о членах, определенных для класса/интерфейса foobar.

Помните, что java - это статический язык, а не динамический. Он не проверяет объект во время выполнения для того, что существует, он проверяет время компиляции на основе объявления типа.

Ответ 6

fooBar type fooBar, который не имеет такой переменной, поэтому код не может быть скомпилирован. Вы можете получить к нему доступ путем отражения.

Ответ 7

Как говорили все, это связано с статическим типом, а класс FooBar не содержит name. Так что это не сработает.

Я хотел указать на предлагаемое использование класса Anonymous.

Анонимный класс (или близко к Closures, возможно, lambdas. Подобный, но не тот же) происходит из парадигмы функционального программирования, где состояния должны быть неизменными.

Говоря, почему вы должны использовать такие классы? Когда вам нужна короткая и короткая вещь, которую нужно делать, которая не обязательно должна идти в полном классе. Пример:

MyTask() //This is a method
{
    new Thread(new Runnable() { //Anonymous class
        public void run()
        {}
    }).start();
}

Важное значение имеет понимание включения вашей реализации только в функцию/класс.

scope of the variables defined in the Anonymous class (or closed-over function) should only be used inside the Anonymous class, он не может быть доступен из другого программного кода.

Следовательно, вы не должны (и в любом случае не можете) установить fooBar.name = "Test";

Ответ 8

Вы также можете сделать это следующим образом

Boolean var= new anonymousClass(){
    private String myVar; //String for example
    @Overriden public Boolean method(int i){
          //use myVar and i
    }
    public String setVar(String var){myVar=var; return this;} //Returns self instane
}.setVar("Hello").method(3);