gpt4 book ai didi

java - 在运行时实现抽象方法?

转载 作者:塔克拉玛干 更新时间:2023-11-03 04:26:15 27 4
gpt4 key购买 nike

假设我有一个抽象类:

abstract class Foo extends Bar {

public abstract int foo();

}

我想在运行时扩展以创建一个类对象。希望我可以有一个动态生成的类:

class FooImpl extends Foo {

@Override
public int foo() {
return 5;
}

}

这将由一个 Class 对象表示,然后我可以使用反射来创建它的新实例。关键是我想在运行时决定方法 foo() 的返回值。我的想法是使用 ASM 为类创建字节码,然后在 ClassLoader 对象上使用反射来定义类。

使用 ASM 然后在生成的字节上反射方法 ClassLoader#defineClass 是否是在运行时使用非硬编码值实现抽象方法的最佳方式?

如果是,我将如何去做。我的直觉是利用 ASMifierClassVisitor,但我不太确定这样做的确切方法。我知道,如果所有其他方法都失败了,我可以手动完成定义特定类所需的 JVM 指令,但我觉得一定有更简单的方法。

如果不是,最好的方法是什么?我将如何使用最好的方法?

编辑:我检查了所有答案,但我认为没有一个正是我要找的。我最终用 ASM 创建了一个小的实现。我想我应该把它贴在这里:

import org.objectweb.asm.*;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;

/**
* Created by IntelliJ IDEA.
* User: Matt
* Date: 9/17/11
* Time: 12:42 PM
*/
public class OverrideClassAdapter extends ClassAdapter {

private final HashMap<String, Object> code;
private final String className;

private final ClassWriter writer;

private String superName;

public OverrideClassAdapter(ClassWriter writer, String className, Queue<int[]> constructorCode, HashMap<String, Object> code) {
super(writer);
this.writer = writer;
this.className = className;
this.code = code;
}

@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
this.superName = name;
if((access & Opcodes.ACC_ABSTRACT) != 0)
access &= ~Opcodes.ACC_ABSTRACT;
if((access & Opcodes.ACC_INTERFACE) != 0)
access &= ~Opcodes.ACC_INTERFACE;
cv.visit(version, access, className, signature, name, null);
}

@Override
public void visitSource(String source, String debug) {
}

@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
boolean isAbstract = (access & Opcodes.ACC_ABSTRACT) != 0;
if(isAbstract)
access &= ~Opcodes.ACC_ABSTRACT;
MethodWriter mw = (MethodWriter) cv.visitMethod(access, name, desc, signature, exceptions);
Object value = code.get(name);
if(isAbstract || value != null) {
if(value instanceof BytecodeValue) {
BytecodeValue returnableValue = (BytecodeValue) value;
int[] byteCode = new int[returnableValue.getValueCode().length + 1];
System.arraycopy(returnableValue.getValueCode(), 0, byteCode, 0, returnableValue.getValueCode().length);
if(returnableValue.getValueCode().length > 1 && returnableValue.getValueCode()[1] == 0) {
byteCode[1] = writer.newConst(returnableValue.getValue());
}
byteCode[byteCode.length - 1] = returnableValue.getReturnCode();
value = byteCode;
}
return new OverrideMethodAdapter(mw, (int[]) value);
}
return mw;
}

private class OverrideMethodAdapter extends MethodAdapter {

private final int[] code;

private final MethodWriter writer;

public OverrideMethodAdapter(MethodWriter writer, int[] code) {
super(writer);
this.writer = writer;
this.code = code;
}

@Override
public void visitEnd() {
try {
Field code = MethodWriter.class.getDeclaredField("code");
code.setAccessible(true);
ByteVector bytes = new ByteVector();
for(int b : this.code)
bytes.putByte(b);
code.set(writer, bytes);
} catch (Exception e) {
e.printStackTrace();
}
}
}

public static byte[] extendClassBytes(Class clazz, String className, HashMap<String, Object> methodImpls) throws IOException {
ClassReader cr = new ClassReader(clazz.getName());
ClassWriter cw = new ClassWriter(0);
cr.accept(new OverrideClassAdapter(cw, className, methodImpls), ClassReader.SKIP_DEBUG);
cr = new ClassReader(cw.toByteArray());
cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
cr.accept(cw, ClassReader.SKIP_DEBUG);
//CheckClassAdapter.verify(new org.objectweb.asm.ClassReader(cw.toByteArray()), true, new PrintWriter(System.out));
/*File file = new File(className + ".class");
new FileOutputStream(file).write(cw.toByteArray());*/
return cw.toByteArray();
}


public static Class extendClass(Class clazz, String className, HashMap<String, Object> methodImpls) throws IOException {
return defineClass(extendClassBytes(clazz, className, methodImpls), className);
}

public static Class defineClass(byte[] code, String name) {
try {
Method method = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
method.setAccessible(true);
return (Class) method.invoke(Thread.currentThread().getContextClassLoader(), name, code, 0, code.length);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}

最佳答案

您可能想看看使用 CGLib .它可以做 Java 的动态代理可以做的事情,除了抽象类和接口(interface)之外,它还有一个类似于 java.lang.reflect.Proxy 的 API 来做这件事。无论如何,CGLib 在幕后使用 ASM,但通过使用 CGLib,您将不必直接制作字节码。

这是一个如何使用 CGLib 执行此操作的示例:

package cglibtest;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class CGLibTest
{
public static void main(String... args)
{
MyAbstract instance = (MyAbstract)Enhancer.create(MyAbstract.class, new MyInterceptor(42));
System.out.println("Value from instance: " + instance.valueMethod());
}

public static class MyInterceptor implements MethodInterceptor
{
private final Object constantValue;

public MyInterceptor(Object constantValue)
{
this.constantValue = constantValue;
}

@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable
{
if ("valueMethod".equals(method.getName()))
return(constantValue);
else
return(null);
}
}

public static abstract class MyAbstract
{
public abstract int valueMethod();
}
}

关于java - 在运行时实现抽象方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7425219/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com