चलो कहते हैं कि मैं एक अमूर्त वर्ग करते हैं:रनटाइम पर सार तरीकों को लागू करना?
abstract class Foo extends Bar {
public abstract int foo();
}
कि मैं कार्यावधि में विस्तार करना चाहते एक कक्षा वस्तु बनाने के लिए। आशा होगा मैं एक गतिशील रूप से उत्पन्न वर्ग हो सकता है:
class FooImpl extends Foo {
@Override
public int foo() {
return 5;
}
}
है कि एक वर्ग वस्तु का प्रतिनिधित्व करती जाएगा और मैं तो के नए उदाहरण बना करने के लिए प्रतिबिंब इस्तेमाल कर सकते हैं। कुंजी यह है कि मैं रनटाइम पर विधि foo() के वापसी मूल्य का निर्णय लेना चाहता हूं। मेरा विचार कक्षा के लिए बाइटकोड बनाने के लिए एएसएम का उपयोग करना है और फिर क्लास को परिभाषित करने के लिए क्लासलोडर ऑब्जेक्ट पर प्रतिबिंब का उपयोग करना है।
एएसएम का उपयोग कर रहा है और फिर जेनरेट बाइट्स पर क्लासलोडर # defineClass विधि का प्रतिबिंब गैर-हार्डकोडेड मानों के साथ रनटाइम पर सार विधियों को लागू करने का सबसे अच्छा तरीका है?
यदि हां, तो मैं ऐसा करने के लिए कैसे जाउंगा। मेरा आंत ASMifierClassVisitor का उपयोग करना है, लेकिन मुझे ऐसा करने की सटीक विधि पर बिल्कुल यकीन नहीं है। मुझे पता है कि अगर सब कुछ विफल हो जाता है तो मैं एक विशिष्ट वर्ग को परिभाषित करने के लिए आवश्यक JVM निर्देशों को मैन्युअल रूप से देख सकता हूं लेकिन मुझे लगता है कि एक आसान तरीका होना चाहिए।
यदि नहीं, तो सबसे अच्छा तरीका क्या है और मैं सबसे अच्छा तरीका उपयोग करने के बारे में कैसे जाउंगा?
संपादित करें: मैंने सभी उत्तरों की जांच की और मैंने फैसला किया कि उनमें से कोई भी बिल्कुल ठीक नहीं था जिसे मैं ढूंढ रहा था। मैं एएसएम के बारे में जो बात कर रहा था उसके बारे में एक छोटा सा कार्यान्वयन करना समाप्त कर दिया। मुझे लगा कि मुझे इसे यहां पोस्ट करना चाहिए:
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;
}
}
}
मुझे लगता है कि आपको सिंथेटिक तरीकों को देखना पड़ सकता है। – Shahzeb
रनटाइम पर 'foo' के वापसी मूल्य का निर्णय लें? एक वर्ग कॉल कैसे करेगा कि अगर यह मनमाना प्रकार देता है? –
रनटाइम पर इस अर्थ में कि वर्ग उत्पन्न होने पर foo का मान निर्धारित होता है, और इसे बाद में बदलने की आवश्यकता नहीं होती है। – mburke13