我們知道,在很多腳本語言中都有eval函數,它可以把字符串轉換為表達式并執行.如在javaScript中:

var str = aid.value + ".style.top = 10;" 

 
把一個id為"aid"的控制的值取出來加合并成一個字符串,如果aid的值是"axman",則

     str = "axman.style.top = 10"  
現在我們要讓控制axman移動到頂部為10的位置:

eval(str);  
這樣這個字符串就變成了表達式或語句開始執行.這樣的功能對于動態構造變量是有非常重要的
意義.

  那么在java中,如果實現這個功能呢?其實我們可以用動態編譯來實現。

動態編譯實現eval
  假設有一組方法實現不同的功能,現在要根據傳進來的方法名調用相應的方法,假如沒有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{";
+= "Object rt(){"
+= "MyClass mc = new MyClass();"
        s 
+= " return mc."+str+"();";
        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(nullnew 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文件

 完整的列子:

import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.lang.reflect.Method;

import com.sun.tools.javac.Main;


public class testJavac{
        
public String getName(){
           
return "劉凱毅";
        }

        
public int getAvg(){
           
return 24;
        }

        
        
public Object eval(String str)throws Exception{
           
//生成java文件
        String s = "class Temp{";
            s 
+= "private testJavac tj = new testJavac();";
            s 
+= "public String rt(){";
            s 
+= " return  \"\"+tj."+str+"();"  ;
            s 
+= "}";
            s 
+="}";
            
           File f 
= new File(System.getProperty("user.dir")+"\\Temp.java");
           PrintWriter pw 
= new PrintWriter(new FileWriter(f));
           pw.println(s);
           pw.close();
           
//動態編譯
           Main javac = new Main();
           String[] cpargs 
= new String[] {"-d", System.getProperty("user.dir") ,"Temp.java"};
           
int status = javac.compile(cpargs);
           
if(status!=0){
              System.out.println(
"沒有成功編譯源文件!");
              
return null;
           }

           
//調用Temp的rt方法返回結果:
           ClassLoader mc = this.getClass().getClassLoader();
           
           Class clasz 
= mc.loadClass("Temp");

           Method rt 
= clasz.getMethod("rt"new Class[]{});
           
return rt.invoke(clasz.newInstance(), new Object[] { });
           
//如果方法沒有返回就直接調用
        }

        
        
    
public static void main(String[]args)throws Exception{
        testJavac jj 
= new testJavac();
        System.out.println( jj.eval(args[
0]) );
    }


}