Why does adding a public field to an anonymous class in Java not work?
我有一个如下定义的示例类:
1 2 3 4 5 6 7
| public class FooBar {
void method1(Foo foo){ // Should be overwritten
...
}
} |
后来,当我尝试这个:
1 2 3 4 5 6 7 8 9
| FooBar fooBar = new FooBar (){
public String name = null;
@Override
void method1 (Foo foo ){
...
}
};
fooBar. name ="Test"; |
我收到一个错误,说明名称字段不存在。 为什么?
-
请不要使用小写的类名。 我花了太长时间来破译"foobar fooBar"。 代码样式约定存在是有原因的。
因为变量"fooBar"的类型是FooBar(所述变量中对象的运行时类型是实现FooBar的匿名类的运行时类型,也是FooBar的子类型)...
...并且类型FooBar没有所述成员。因此,编译错误。 (请记住,变量"fooBar"可以包含符合FooBar的任何对象,即使没有name的对象,因此编译器会拒绝非类型安全的代码。)
编辑:对于一个解决方案,请参阅不可响应的答案,该答案使用本地类声明来创建新的命名类型(以替换帖子中的匿名类型)。
Java不支持这样做的方法(主要是:Java不支持有用的类型推断),尽管以下方法确实有效,即使不是很有用:
1 2 3 4 5 6 7
| (new foobar (){
public String name = null;
@Override
void method1 (Foo foo ){
...
}
}). name ="fred"; |
快乐的编码。
Scala和C#都支持所需的类型推断,因此支持局部变量的匿名类型特化。 (尽管C#不支持匿名扩展现有类型)。但是,Java没有。
-
Java支持的"无用"类型推断是什么?
-
@irreputable参见示例。 表达式(new foobar ...)具有"正确的专用类型[推断]",但只要表达式被赋值给变量(因为缺乏有用的局部类型推断),那么只有主格类型(没有一个可以描述 精确的匿名对象)可以描述对象的类型。 因此,没用:(
-
@irreputable几个月前我会争辩说Java没有类型推断,但是Tony Morris(我认为)有一篇关于这个主题的文章很好,我读到的文章证明了上面有限的案例。 不幸的是,我似乎无法再找到它...... :(
本地班级会这样做
1 2 3 4 5 6 7 8 9 10
| {
class MyFooBar extends FooBar {
String name = null;
...
};
MyFooBar fooBar = new MyFooBar ();
fooBar. name ="Test";
} |
-
+1非常聪明。 我不知道可以在块中声明命名类型(类)。 对于那些感兴趣的人,它是JLS 14.3中讨论的本地类声明。
试试这个。
1 2 3 4 5
| @SafeVarargs
public static < T > void runWithObject(T object, Consumer< T >... progs) {
for (Consumer< T > prog : progs)
prog.accept(object);
} |
和
1 2 3 4 5 6 7 8 9 10 11
| 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 ())
); |
结果:
或者您可以在Java 10或更高版本中使用var。
1 2 3 4 5 6 7 8 9 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 ()); |
你也可以这样做
1 2 3 4 5 6 7
| 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); |
正如大家所说,这是由于静态类型和FooBar类不包含name。所以它不会起作用。
我想指出Anonymous类的建议用法。
匿名类(或接近闭包,也许是lambdas。相似但不相同)来自函数式编程范例,其中状态应该是不可变的。
话虽这么说,你为什么要用这样的课程?当你需要快速而简短的事情时,不一定要完整的课程。例:
1 2 3 4 5 6 7
| 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";
FooBar类型是FooBar,它没有这样的变量,因此无法编译代码。您可以通过反射访问它。
您正在创建FooBar类型的对象。编译器只知道为类/接口FooBar定义的成员。
请记住,java是一种静态语言,而不是动态语言。它不会在运行时检查对象是否存在,它会在编译时根据类型声明进行检查。
FooBar是对类型为FooBar的对象的引用,并且此类对象没有字段name。就那么简单。由于它是一个匿名类型,引用该字段的唯一方法是通过其this引用。