扩展 class(例如枚举)的匿名 .class 能否以实现接口的方式被黑客入侵?

Can an anonymous .class that extends a class (such as an enum) be hacked in such a way as to implement an interface?

我有这样的界面:

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

我想创建一个实现它的匿名枚举,就好像这是有效的 Java:

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

(注意只有 A 实现了 Foo,而不是 B 或 C。)


(这与 "What can do you in Java bytecode that you can't do in Java?" 个问题有关。)

确实可以通过字节码操作创建这样的 class 文件。这是一个使用 ASM 库执行此操作的示例程序:

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

它打印

A
    implements Foo
Hello!
B
C

当 运行ning 在 Oracles 的 Java 8 下时 class 文件驻留在文件系统中。当 运行ning 在不同的 JVM 下或捆绑在 Jar 文件中时,写回修改后的 class 文件可能会失败,或者 enum 可能已经被急切地加载并且没有反映修改(但它会下一个 运行 then).

但它表明,通常情况下,字节码操作是可能的,并且在启动应用程序之前执行时,即使在更复杂的情况下也能正常工作。


不过,我认为这只是一个有趣的 hack,但您不应该在其上构建应用程序。没有问题,这个 hack 将解决你无法使用实现接口的委托对象解决的问题......