调用匿名类的方法

Pau*_*ton 36 java class

前几天我才知道你可以做到这一点

new Object() {
    void hello() {
        System.out.println("Hello World!");
    }
}.hello();
Run Code Online (Sandbox Code Playgroud)

这对我来说似乎很奇怪.当然,创建的对象的静态类型是Object,所以没有方法hello()?它几乎完全没有意义(hello例如,不可能调用两次).

我有2个问题.

  1. 有人能指出我解决这个问题的规范部分吗?
  2. 我是否正确地认为你可以调用的唯一方法hello就是这样.反思怎么样?

谢谢

Sot*_*lis 16

有人能指出我解决这个问题的规范部分吗?

这将主要在有关方法调用表达式的部分中定义:

在编译时处理方法调用的第一步是确定要调用的方法的名称以及要搜索该名称的方法定义的类或接口.

对于要搜索的类或接口,有六种情况需要考虑,具体取决于MethodInvocation左括号前面的表单:

  • [...]
  • 如果表单是Primary . [TypeArguments] Identifier,那么让我们T成为主表达式的类型.要搜索的类或接口是Tif T类还是接口类型,或者Tif 的上限T 是类型变量.

这里,Primary表达式类实例创建表达式.所以要搜索的类型是匿名类型.

我是否正确地认为你可以调用你好的唯一方法就是这样.反思怎么样?

只要表达式评估为匿名类型T,无论是通过直接访问还是通过泛型,您都可以访问(定期访问规则适用于)T声明的成员.这不仅限于方法.您可以访问字段或类型,但它对类型没有用.例如,

Object var = new Object() {
    class Nested {
    }
}.new Nested();
Run Code Online (Sandbox Code Playgroud)

由于无法在没有封闭类型的情况下引用嵌套类型,因此无法声明该嵌套类型的变量.实用性很快下降.(据推测,这也是为什么你不能static在这个匿名类中使用嵌套类型.)

反射也暴露了这种方法.生成的匿名类包含此方法,因此您可以检索它并调用它.过程是一样的.实例来自匿名类的事实并不重要.当将方法名称作为字符串时,如何调用Java方法中提供的策略相同适用.

例如,

Object ref = new Object() {
    public void method() {
        System.out.println("hidden");
    }
};
Class<?> anonymousClass = ref.getClass();
Method method = anonymousClass.getMethod("method");
method.invoke(ref, new Object[0]);
Run Code Online (Sandbox Code Playgroud)

不要写这样的代码.

  • 天啊.`Object var = new Object(){class Nested {}} .new Nested(){class Unnecessary {}}.new Unnecessary(){class Really {}} .new Really(){class StopIt {}} .new StopIt ();` (9认同)
  • @elliot如果您使用了他们的代码,那将无效,因为方法不公开. (4认同)
  • @ElliottFrisch应该不是这样的.`new Object(){}.getClass()`为我返回`class com.example.Example $ 1`. (2认同)
  • 我尝试了`getDeclaredMethods`并且我能够调用它.我仍然不知道为什么有人认为这是一件好事. (2认同)
  • @ElliottFrisch在一些注释处理器基于某些注释发现方法的场景中它可能"有用".咩.我的偏好是明确的. (2认同)

Ell*_*sch 12

发布后,没有办法从Object实例中获取匿名方法.并且,它使匿名类看起来毫无意义.但是,您可以(我通常会)使用它来实现接口.就像是,

static interface Hello {
    void hello();
}
public static void main(String[] args) {
    Hello o = new Hello() {
        public void hello() {
            System.out.println("Hello World!");
        }
    };
    o.hello();
}
Run Code Online (Sandbox Code Playgroud)

或者,更常见的是,使用JFC/Swing和ActionListener进行回调.


Mic*_*nic 11

添加到 Sotirios的答案,这里是如何通过反射调用方法:

Object o = new Object() {
    void hello() {
        System.out.println("Hello World!");
    }
};

Method hello = o.getClass().getDeclaredMethod("hello");
hello.invoke(o);
Run Code Online (Sandbox Code Playgroud)

这允许您不止一次调用该方法,但除此之外,没有多大意义.


Zho*_*gYu 9

匿名类是为了懒惰程序员的利益 - 命名事情太难了:)

匿名类非常像本地类.如果本地类只是用于创建一个对象,然后只用作超类型,我们可以创建一个更简洁的匿名类.

匿名类是不可否认的(程序员),这很好,因为我们不需要再次引用它.但是,对于编译器而言,该类的名称非常多,并且没有理由将它与显式命名的类区别对待.表达式的静态类型是具体的子类,对象的成员是该类的成员.这个功能(能够通话hello())毫无意义吗?只有当你认为本地课程毫无意义时(实际上很少使用本地课程).这是我使用该功能的一个例子(为了好玩).

虽然类型是不可否认的,但类型可以通过API自行存活.例如,

    Objects.requireNonNull( new Object(){ void hello(){} } ).hello();
Run Code Online (Sandbox Code Playgroud)

即使我们无法命名类型,也不需要将其命名为可以推断的位置.

    Collections.singleton( new Object(){ void hello(){} } )
      .forEach( o->{ o.hello(); o.hello(); } );
Run Code Online (Sandbox Code Playgroud)

我们可以为那些不期望静态类型的人创建益智游戏

    Collections.singleton( new Object(){} ).add( new Object() ); // does not compile! why?
Run Code Online (Sandbox Code Playgroud)

  • 是的,你的是一个更好的示范. (2认同)
  • java中的undenotable类型 - https://groups.google.com/forum/#!topic/java-lang-fans/eOW80YxPcY4 (2认同)