Why does adding a public field to an anonymous class in Java not work?
I have an example class defined like below:
public class FooBar {
void method1(Foo foo){ // Should be overwritten
...
}
}
Later, when I try this:
FooBar fooBar = new FooBar(){
public String name = null;
@Override
void method1(Foo foo){
...
}
};
fooBar.name = "Test";
I get an error sayi开发者_StackOverflow社区ng that the name field does not exist. Why?
Because the type of the variable "fooBar"
is FooBar
(the run-time type of the object in said variable is that of the anonymous class implementing FooBar
which is also a subtype of FooBar
)...
...and the type FooBar
does not have said member. Hence, a compile error. (Remember, the variable "fooBar"
can contain any object conforming to FooBar
, even those without name
, and thus the compiler rejects the code which is not type-safe.)
Edit: For one solution, see irreputable's answer which uses a Local Class Declaration to create a new named type (to replace the anonymous type in the post).
Java does not support a way to do this (mainly: Java does not support useful type inference), although the following does work, even if not very useful:
(new foobar(){
public String name = null;
@Override
void method1(Foo foo){
...
}
}).name = "fred";
Happy coding.
Both Scala and C# support the required type inference, and thus anonymous type specializations, of local variables. (Although C# does not support extending existing types anonymously). Java, however, does not.
A local class would do
{
class MyFooBar extends FooBar{
String name = null;
...
};
MyFooBar fooBar = new MyFooBar();
fooBar.name = "Test";
}
You're creating an object of type foobar
. The compiler only knows about the members defined for the class/interface foobar
.
Remember, java is a static language, not dynamic. It doesn't check the object at runtime for what exists, it checks at compile time based on the type declaration.
Try this.
@SafeVarargs
public static <T> void runWithObject(T object, Consumer<T>... progs) {
for (Consumer<T> prog : progs)
prog.accept(object);
}
and
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())
);
result:
name=Test
Or you can use var
like this in Java 10 or later.
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());
fooBar
is a reference to an object of type foobar
, and such objects do not have a field name
. Simple as that. And since it’s an anonymous type, the only way to reference that field is through its this
reference.
fooBar
type is foobar
which has not such variable and therefore the code can not be compiled. You can access it by reflection.
As everyone said, this is due to static typing and FooBar
class does not contain name
. So it won't work.
I wanted to point out the suggested usage of Anonymous class.
Anonymous class (or close to Closures, maybe lambdas. Similar but not same) come from functional programming paradigm, where the states should be immutable.
That being said, why should you user such classes? When you need a quick and short thing to be done which should not necessarily go in a complete class. Example:
MyTask() //This is a method
{
new Thread(new Runnable() { //Anonymous class
public void run()
{}
}).start();
}
The understanding of enclosing your implementation only to a function/class is important.
The scope of the variables defined in the Anonymous class (or closed-over function) should only be used inside the Anonymous class
, it cannot be accessed from other program code.
Therefore, you should not (and anyway cannot) set fooBar.name = "Test";
You can also do it like this
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);
精彩评论