4

I have an interface such as:

public interface Foo() {
    public void bar();
}

And I want to make an anonymous enum that implements it, as if this was valid Java:

public enum MyEnum {
    A implements Foo {
        public void bar() {
            System.out.println("Hello!");
        }
    },
    B,
    C;
}

(Notice how only A implements Foo, but not B or C.)


(This is related to those "What can do you in Java bytecode that you can't do in Java?" questions.)

SoniEx2
  • 1,864
  • 3
  • 27
  • 40
  • Given the amount of trouble you would have to go through to get this to work, what's wrong with a named type? – Sergey Kalinichenko Aug 13 '15 at 00:35
  • You can use [`Proxy`](http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Proxy.html), too, but it's going to be slow. – Sergey Kalinichenko Aug 13 '15 at 00:38
  • Anonymous type declarations can't have `extends` or `implements` clauses, so that syntax will never fly. Provide an example of the Java code you'd actually want to be able to write. For example, maybe you really want to use it like this: `Foo theFoo = makeAnArrayListThatImplementsFoo(() -> { /* Do you stuff here. */ });` – erickson Aug 13 '15 at 00:39
  • I don't think this is a duplicate of http://stackoverflow.com/questions/23769208/is-it-possible-to-implement-an-interface-at-runtime-in-java at all. This is a question about Java syntax. – Brett Kail Aug 13 '15 at 01:08
  • It's in fact NOT about Java syntax either. – SoniEx2 Aug 13 '15 at 01:10
  • Forgive me for asking, but how *isn't* this a syntax question? The syntax you want isn't supported and you won't be able to achieve this by hacking the bytecode either. – Dave Cooper Aug 13 '15 at 06:30
  • @dasblinkenlight Doesn't work for enums. – SoniEx2 Aug 13 '15 at 09:55
  • @DaveCooper If I can't achieve it by hacking the bytecode then show me why, at least. You're the one making claims, I'm the one asking questions, thus you must back your claims as I don't have any. – SoniEx2 Aug 13 '15 at 16:16

1 Answers1

4

It is indeed possible to create such class files via byte-code manipulation. Here is an example program which does this using the ASM library:

import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import org.objectweb.asm.*;

public class EnumHack {
    interface Foo {
        public void bar();
    }
    enum MyEnum {
        A {
            public void bar() {
                System.out.println("Hello!");
            }
        },
        B,
        C;
    }
    public static void main(String... arg) {
        try {
            patch();
        } catch (IOException|URISyntaxException ex) {
            System.err.println("patching failed: "+ex);
            return;
        }
        test();
    }
    static void test() {
        for(MyEnum e: MyEnum.values()) {
            System.out.println(e.name());
            if (e instanceof Foo) {
                System.out.println("\timplements Foo");
                ((Foo)e).bar();
            }
        }
    }
    static void patch() throws IOException, URISyntaxException {
        URL url = MyEnum.class.getResource("EnumHack$MyEnum$1.class");
        ClassReader cr=new ClassReader(url.openStream());
        ClassWriter cw=new ClassWriter(cr, 0);
        cr.accept(new ClassVisitor(Opcodes.ASM5, cw) {
            @Override
            public void visit(int version, int access, String name,
                    String signature, String superName, String[] interfaces) {
                if(interfaces.length==0) // not patched yet
                    interfaces=new String[] { Foo.class.getName() };
                super.visit(version, access, name, signature, superName, interfaces);
            }
        }, 0);
        Files.write(Paths.get(url.toURI()), cw.toByteArray());
    }
}

It prints

A
    implements Foo
Hello!
B
C

When running under Oracles’s Java 8 with the class files residing in the file system. When running under different JVMs or bundled in a Jar file writing back the modified class file may fail or the enum might have been loaded eagerly and not reflect the modification (but it would on the next run then).

But it shows that generally, that byte-code manipulation is possible and when performed before starting the application, it will work even in the more complicated scenarios.


Still, I see this only as an interesting fun hack, but nothing you should build applications on. There is no problem this hack will solve that you couldn’t solve using a delegation object which implements the interface…

Holger
  • 285,553
  • 42
  • 434
  • 765