2010-03-29 3 views
2

Я разбор текстового файла, который привязывается к какому-то коду Java, как, например:Как построить функцию «на лету» в java?

public void eval(Node arg) 
    { 
     if(arg.data.equals("rand")) 
     { 
      moveRandomly(); 
     } 
     else if(arg.data.equals("home")) 
     { 
      goHome(); 
     } 
      else if(arg.data.equals("iffood")) 
    { 
     ifFoodHere(arg.left, arg.right); 
    }//snip.. 

Это будет необходимо провести переоценку около тысячи раз, и я предпочел бы не пройти все это каждый раз. Есть ли способ сделать этот обход один раз, а затем он будет функцией, которая вызывается каждый раз?

ответ

2

Создайте анонимный внутренний класс.

Что-то вроде:

public Callable<Void> eval(Node arg) 
{ 
    if(arg.data.equals("rand")) 
    { 
    return new Callable<Void>{ public Void call() { moveRandomly(); return null; } }; 
    } 
    ... 
} 

Callable<Void> f = eval(a); 
f.call(); 
+0

Функция Eval рекурсивно вызывается через детей. Означает ли это, как это может работать? – stereos

+0

Вы можете вызвать вызываемый, который был возвращен рекурсивным eval в вызываемом вами создании. пс. Звучит как язык. :) – lexicore

+0

'java.util.concurrent.Callable'? Ergh. Нажмите лодку: напишите три строки кода для полноценного интерфейса. –

3

Вы можете сделать Карту Runnables:

Map<String, Runnable> methods = new HashMap<String, Runnable>(); 
methods.put("rand", new Runnable() 
{ 
    public void run() 
    { 
     moveRandomly(); 
    } 
}); 
... 

затем в методе

public void eval(Node arg) 
{ 
    Runnable command = methods.get(arg.data); 
    command.run(); 
} 
+0

Я думаю, вы имеете в виду Callable? Если он выполнит Runnable, каждая команда будет запускаться в отдельном потоке, который будет сильно отличаться от того, что делает исходный код. –

+0

Вызывается работает в отдельной теме тоже. Он возвращает значение типа T; Runnable - нет. – duffymo

+3

Runnable работает только в отдельном потоке, если я запускаю его в новом потоке, верно? Вы можете использовать любой класс, который хотите. Он мог даже создать свой собственный класс с помощью метода, который принимает Node в качестве параметра, чтобы он мог получить доступ к другим данным на узле, поскольку, как он выражал, может быть чем-то, что он хочет. – Joel

1

Если вы знаете все аргументы/команды, которые вы можете ожидаем, я мог бы сделать это вот так:

enum Args { 
    home, rand, iffood; 

    private Method method; 

    private Args() { 
    try { 
     this.method = Commands.class.getMethod(this.name(), Node.class); 
    } catch (final Exception e) {} 
    } 
    public void invoke (final Node args) 
    throws IllegalArgumentException, IllegalAccessException, 
     InvocationTargetException { 
    this.method.invoke(null, args); 
    } 
    public static Args valueOf (final Node arg) { 
    return valueOf(arg.data); 
    } 
    public static void eval (final Node arg) 
    throws IllegalArgumentException, IllegalAccessException, 
     InvocationTargetException { 
    valueOf(arg).invoke(arg); 
    } 
} 

реализации команд являются:

class Commands {  
    public static void home (final Node arg) { 
    goHome(); // Call the implementation 
    // or simply make these bodies the implementations. 
    } 
    public static void iffood (final Node arg) { 
    ifFoodHere(arg.left, arg.right); 
    }  
    public static void rand (final Node arg) { 
    moveRandom(); 
    } 
    //... 
} 

ваш Eval(), то становится просто:

try { 
    Args.eval(arg); 
} catch (IllegalArgumentException e) { 
    // Handle unknown arg.data 
} 
+0

+1 для подхода 'enum' ... но вы можете упростить дальнейшее, каждый перечисление может иметь метод apply (final Node arg), который определяется отдельно для каждого перечисления. Тогда вам не нужен отдельный класс Commands или использование отражения. –

Смежные вопросы