【问题标题】:Why does adding a public field to an anonymous class in Java not work?为什么向 Java 中的匿名类添加公共字段不起作用?
【发布时间】:2011-06-23 19:02:35
【问题描述】:

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

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”。存在代码样式约定是有原因的。

标签: java anonymous-class


【解决方案1】:

因为变量"fooBar"的类型是FooBar(该变量中object的运行时类型是实现FooBar 的匿名类也是FooBar 的子类型)...

...而FooBar 类型没有说成员。因此,编译错误。 (请记住,变量"fooBar" 可以包含任何符合FooBar 的对象,即使是那些没有name 的对象,因此编译器会拒绝非类型安全的代码。)

编辑:对于一个解决方案,请参阅不可靠的答案,它使用 Local Class Declaration 创建一个新的命名类型(替换帖子中的匿名类型 )。

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

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

编码愉快。


Scala 和 C# 都支持所需的类型推断,因此支持局部变量的匿名类型特化。 (尽管 C# 不支持匿名扩展现有类型)。然而,Java 没有。

【讨论】:

  • Java 支持哪些“无用”类型推断?
  • @irreputable 查看示例。 表达式 (new foobar ...) 具有“正确的专门类型[推断]”,但是一旦将表达式分配给变量(因为缺少 useful local类型推断)那么只有主格类型(它们都不能精确地描述匿名对象)可以描述对象的类型。因此,没用 :(
  • @irreputable 几个月前我会争辩说 Java 没有 no 类型推断,但是 Tony Morris 有一篇关于这个主题的好文章(我认为)我认为阅读这证明了上述有限的情况。不幸的是,我似乎再也找不到它了...... :(
【解决方案2】:

本地类就可以了

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

    MyFooBar fooBar = new MyFooBar();

    fooBar.name = "Test";
}

【讨论】:

【解决方案3】:

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

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

【讨论】:

    【解决方案4】:

    试试这个。

    @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
    

    或者您可以在 Java 10 或更高版本中像这样使用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());
    

    【讨论】:

      【解决方案5】:

      fooBar 是对foobar 类型对象的引用,并且此类对象没有name 字段。就那么简单。由于它是匿名类型,因此引用该字段的唯一方法是通过其 this 引用。

      【讨论】:

        【解决方案6】:

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

        【讨论】:

          【解决方案7】:

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

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

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

          话虽如此,你为什么要使用这样的类?当您需要做一件快速而简短的事情时,不一定要参加完整的课程。示例:

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

            【讨论】:

              猜你喜欢
              • 2023-03-28
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-09-10
              相关资源
              最近更新 更多