37

I learned the other day that you can do this

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

This seems really weird to me. Surely the static type of the object created is Object, so there isn't a method hello()? Isn't it almost completely pointless (it isn't possible to invoke hello twice for example).

I have 2 questions about this.

  1. Can somebody point me to the part of the specification that addresses this?
  2. Am I right in thinking that the only way you can invoke hello is immediately like this. What about reflection?

Thanks

Paul Boddington
  • 35,031
  • 9
  • 56
  • 107
  • [JLS-15.9.5. Anonymous Class Declarations](http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.9.5) – Elliott Frisch Oct 05 '15 at 23:56
  • You should be able to get to it via reflection. But yes, this is almost completely pointless. If you want to call methods, you usually implement an Interface in your anonymous class. – Thilo Oct 06 '15 at 00:00
  • 4
    Well, if you create an anonymous class of a more specific type (like, an anonymous implementation of an *interface*), you can hold onto the reference in a variable that would let you call those methods at will. – Ian McLaird Oct 06 '15 at 00:01
  • 2
    The only way to access `hello` *through static typing* is to access it on the immediate expression, yes. – user2864740 Oct 06 '15 at 00:02
  • 2
    Also note that Java 8 has a nice new compact syntax for anonymous classes that implement a single method interface. – Thilo Oct 06 '15 at 00:02
  • You may want to take note of the extra `.class` file generated by the above code. Anonymous classes are statically typed and generated by the compiler. – jpmc26 Oct 06 '15 at 02:56

4 Answers4

17

Can somebody point me to the part of the specification that addresses this?

This will mostly be defined in the section concerning Method invocation expressions:

The first step in processing a method invocation at compile time is to figure out the name of the method to be invoked and which class or interface to search for definitions of methods of that name.

For the class or interface to search, there are six cases to consider, depending on the form that precedes the left parenthesis of the MethodInvocation:

  • [...]
  • If the form is Primary . [TypeArguments] Identifier, then let T be the type of the Primary expression. The class or interface to search is T if T is a class or interface type, or the upper bound of T if T is a type variable.

Here, the Primary expression is the class instance creation expression. So the type to search is the anonymous type.

Am I right in thinking that the only way you can invoke hello is immediately like this. What about reflection?

As long as an expression evaluates to the anonymous type T, whether through direct access like you have, or through generics, you have access (regular access rules apply) to the members that T declares. This isn't limited to methods. You can access fields or types, though it's not as useful for types. For example,

Object var = new Object() {
    class Nested {
    }
}.new Nested();

Since there's no way to refer to the nested type without the enclosing type, you can't declare a variable of that nested type. The usefulness declines very quickly. (Presumably, that's also why you can't have a static nested type within this anonymous class.)

Reflection also exposes this method. The generated anonymous class contains this method, so you can retrieve it and invoke it. The process is the same. The fact that the instance is from an anonymous class doesn't matter. The same strategy as presented in How do I invoke a Java method when given the method name as a string? applies.

For example,

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

Don't ever write code like this.

Community
  • 1
  • 1
Sotirios Delimanolis
  • 252,278
  • 54
  • 635
  • 683
  • 2
    @ElliottFrisch That shouldn't be the case. `new Object() {}.getClass()` returns `class com.example.Example$1` for me. – Sotirios Delimanolis Oct 06 '15 at 00:06
  • Oracle Java 8, Linux. `o.getClass().getMethods()` didn't yield a `hello` method. That's where I stopped. – Elliott Frisch Oct 06 '15 at 00:07
  • 4
    @elliot If you used their code, that won't work because method isn't public. – Sotirios Delimanolis Oct 06 '15 at 00:08
  • 2
    I tried `getDeclaredMethods` and I was able to invoke it. I still have no idea why anybody thought this was a good thing to allow. – Paul Boddington Oct 06 '15 at 00:10
  • 1
    Yep. Thanks! I made the method `public`, and then it was visible. So I suppose you'd have to know it was there to make it visible; seems to make it opaque as to reflection. – Elliott Frisch Oct 06 '15 at 00:10
  • 2
    @ElliottFrisch It could "be useful" in a scenario where some annotation processor would discover methods based on some annotation. Meh. My preference is explicitness. – Sotirios Delimanolis Oct 06 '15 at 00:12
  • I think the reason you can't have a static nested class is the same reason you can't have a static nested class inside any non-static nested class. (Anonymous classes are non-static nested classes) – user253751 Oct 06 '15 at 05:17
  • 10
    Oh my. `Object var = new Object() { class Nested {} }.new Nested() { class Unnecessary {}}.new Unnecessary() { class Really {} }.new Really() { class StopIt {} }.new StopIt();` – Pokechu22 Oct 06 '15 at 14:25
12

As posted, there isn't a way to get the anonymous methods from the Object instance. And, it makes Anonymous classes look pointless. But, you could (and I usually would) use it to implement an interface. Something like,

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

Or, more commonly, for call-backs with JFC/Swing and ActionListener.

Elliott Frisch
  • 183,598
  • 16
  • 131
  • 226
11

Adding to Sotirios' answer, here's how to invoke the method through reflection:

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

Method hello = o.getClass().getDeclaredMethod("hello");
hello.invoke(o);

This allows you to call the method more than once, but other than that, makes little sense.

Community
  • 1
  • 1
Mick Mnemonic
  • 7,403
  • 2
  • 23
  • 27
9

Anonymous class is for the benefit of lazy programmers - naming things is too hard :)

Anonymous class is very much like a local class. If a local class is just for creating one object, which is then used only as a supertype, we can create an anonymous class instead which is more succinct.

Anonymous class is undenotable (to programmer), which is fine because we don't need to reference it again. However, to the compiler, the class is very much named, and there is no reason to treat it differently from explicitly named classes. The static type of the expression is the concrete subclass, and the members of the object is the members of that class. Is this feature (being able to call hello()) pointless? Only if you consider local class pointless (and indeed local class is rarely used). Here's an example where I used that feature (for fun).

Although the type is undenotable, the type can survive as itself through APIs. For example,

    Objects.requireNonNull( new Object(){ void hello(){} } ).hello();

Even though we cannot name the type, it does not need to be named where it can be inferred.

    Collections.singleton( new Object(){ void hello(){} } )
      .forEach( o->{ o.hello(); o.hello(); } );

And we can create puzzlers for people who didn't expect the static type

    Collections.singleton( new Object(){} ).add( new Object() ); // does not compile! why?
ZhongYu
  • 18,232
  • 5
  • 28
  • 55
  • 1
    So my premise is completely wrong - you can call `hello()` twice without reflection. This is cool, but also a bit ridiculous. Inspired by your answer, I managed it with `Collections.nCopies(2, new Object() {...}).forEach(o -> o.hello());` – Paul Boddington Oct 06 '15 at 02:21
  • 2
    yeah, yours is a better demonstration. – ZhongYu Oct 06 '15 at 02:24
  • 1
    While the programmer cannot name the concrete type of the expression, java also provides lots of places where you don't need to name the type, e.g. lambda parameter. – ZhongYu Oct 06 '15 at 02:26
  • That's true. I guess this isn't such a weird anomaly after all. – Paul Boddington Oct 06 '15 at 02:27
  • 2
    undenotable types in java - https://groups.google.com/forum/#!topic/java-lang-fans/eOW80YxPcY4 – ZhongYu Oct 06 '15 at 04:08