在java中利用動態編譯實現eval
我們知道,在很多指令碼語言中都有eval涵數,它可以把字串轉換為表態式並執行.如在javaScript中
var str = aid.value + ".style.top = 10;"
把一個id為"aid"的控制的值取出來加合併成一個字串,如果aid的值是"axman",則
str = "axman.style.top = 10"
現在我們要讓控制axman移動到頂部為10的位置:
eval(str);
這樣這個字串就變成了表示式或語句開始執行.這樣的功能對於動態構造變數是有非常重要的
意義.
那麼在java中,如果實現這個功能呢?其實我們可以用動態編譯來實現:
假設有一組方法實現不同的功能,現在要根據傳進來的方法名呼叫相應的方法,假如沒有eval功能,我們
只能這麼做:
MyClass mc = new MyClass();
if(str.equals("m1"))
mc.m1();
else if(str.equals("m1"))
mc.m2();
else if(str.equals("m3"))
mc.m3();
else if(.........)
.........();
如果有一百種情況呢?
如果我們用eval方法就可以直接這樣:
String str = ...........;
eval("mc"+str+"();");
是不是非常方便?關鍵是如何實現eval()?
我們把要轉換的字串構造一個完整的類:如果方法是有返回值的.則:
public Object eval(String str){
//生成java檔案
String s = "class Temp{";
s += "Object rt(){"
s += "MyClass mc = new MyClass();"
s += " return mc."+str+"();";
s += "}"
s +="}";
File f = new File("Temp.java");
PrintWriter pw = new PrintWriter(new FileWriter(f));
pw.println(s);
pw.close();
//動態編譯
com.sun.tools.javac.Main javac = new com.sun.tools.javac.Main();
String[] cpargs = new String[] {"-d", "所在目錄","Temp.java"};
int status = javac.compile(cpargs);
if(status!=0){
System.out.println("沒有成功編譯原始檔!");
return null;
}
//呼叫Temp的rt方法返回結果:
MyClassLoader mc = new MyClassLoader();
Class clasz = mc.loadClass("Test.class",true);
Method rt = clasz.getMethod("rt", new Class[]{ String[].class });
return rt.invoke(null, new Object[] { new String[0] });
//如果方法沒有返回就直接呼叫
}
我們可以先寫好多個過載的eval,有返回值和沒有返回值的.以及可以傳遞引數的.
這樣我們就可以用字串轉換為java的語句來執行.
本文只是一個例子,說明了一個動態編譯的思想,更好的實現請各位朋友自己來完成.
String[] cpargs = new String[] {"-d", "所在目錄","Temp.java"};
-d指明的目錄應該是當前目錄,因為生成的java檔案是以當前目錄為"/"然後在此目錄下建立相應的包的."當前目錄"應該用new File(".").getAbsoultPath()來確定.
java檔案如果有package,在生成的時候應該建立相應的子目錄.而
這個引數應該是 java原始檔的file物件的getAbsoultPath(),
如當前應用程式是在d:/debug目錄執行,動態生成的java檔案有個package 為temp;
則 String[] cpargs = new String[] {"-d", "d://debud","d://debug//temp//Temp.java"};這樣生成的class檔案應該和java原始檔在同一目錄d:/debug//temp/下.
過載loadClass方法時應該注意能正確讀取到class檔案