Почему добавление открытого поля в анонимный класс на 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);