为什么在Java中向匿名类添加公共字段不起作用?

为什么在Java中向匿名类添加公共字段不起作用?,java,anonymous-class,Java,Anonymous Class,我有一个定义如下的示例类: 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";

我有一个定义如下的示例类:

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";
我收到一个错误,说明名称字段不存在。为什么?

因为变量
“fooBar”
的类型是
fooBar
(所述变量中的对象的运行时类型是实现
fooBar
的匿名类的运行时类型,该类也是
fooBar
的子类型)

…并且类型
FooBar
没有所述成员。因此,出现编译错误。(请记住,变量
“fooBar”
可以包含符合
fooBar
的任何对象,甚至包括那些没有
名称的对象,因此编译器会拒绝类型不安全的代码。)

编辑:对于一种解决方案,请参阅Unreputable的答案,该答案使用创建新的命名类型(以替换帖子中的匿名类型)。

Java不支持这样做(主要是:Java不支持有用的类型推断),尽管以下方法确实有效,即使不是很有用:

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



Scala和C#都支持所需的局部变量类型推断,从而支持匿名类型专门化。(尽管C#不支持匿名扩展现有类型)。但是Java没有。

fooBar
是对类型为
fooBar
的对象的引用,这些对象没有字段
名称。就这么简单。由于它是匿名类型,引用该字段的唯一方法是通过它的
引用。

您正在创建一个类型为
foobar
的对象。编译器只知道为类/接口
foobar
定义的成员


记住,java是一种静态语言,而不是动态语言。它不会在运行时检查对象是否存在,而是在编译时根据类型声明进行检查。

fooBar
类型是
fooBar
,它没有此类变量,因此无法编译代码。您可以通过反射来访问它。

本地类就可以了

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

    MyFooBar fooBar = new MyFooBar();

    fooBar.name = "Test";
}

正如大家所说,这是由于静态类型和
FooBar
类不包含
name
。所以它不会起作用

我想指出匿名类的建议用法

匿名类(或接近闭包的类,可能是lambdas。类似但不相同)来自函数式编程范式,其中的状态应该是不变的

也就是说,为什么要使用这样的类?当你需要做一件短而快的事情时,不一定要在一节完整的课上完成。例如:

MyTask() //This is a method
{
    new Thread(new Runnable() { //Anonymous class
        public void run()
        {}
    }).start();
}
理解只将实现封装到函数/类是很重要的

匿名类(或闭包函数)中定义的变量的
范围只能在匿名类内部使用,不能从其他程序代码访问


因此,您不应该(无论如何也不能)设置
fooBar.name=“Test”

您也可以这样做

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);
试试这个

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

name=Test
或者您可以在Java10或更高版本中像这样使用
var

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());

Java支持的“无用”类型推断是什么?@unreputable参见示例。表达式(
new foobar…
)具有“正确的专用类型[推断]”,但一旦将表达式分配给变量(因为缺少有用的局部类型推断),则只有命名类型(没有一种类型可以精确描述匿名对象)可以描述对象的类型。因此,没有用处:(@unjutable几个月前,我会说Java没有类型推断,但我读到了Tony Morris(我想)的一篇关于这个主题的好文章,这篇文章展示了上述有限的情况。不幸的是,我似乎再也找不到它了……:(请不要使用小写类名。我花了太长时间才破译“foobar foobar”。代码样式约定的存在是有原因的。+1非常聪明。我不知道命名类型(类)可以在块中声明。对于那些感兴趣的人来说,它是一个。
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());