2015-05-12 8 views
17

у меня есть экземпляр этого класса Java, доступной в моей программе JavascriptRhino: Java числа не ведут себя как числа Javascript

public class ContentProvider { 
    public Object c(int n) { 
    switch (n) { 
     case 1: return 1.1; 
     case 2: return 2.2; 
     case 3: return 3.3; 
     case 4: return "4"; 
     case 5: return new java.util.Date(); 
    } 
    return null; 
    } 
} 

Это код внутри основной():

ScriptEngineManager mgr = new ScriptEngineManager(); 
ScriptEngine engine = mgr.getEngineByName("JavaScript"); 
engine.put("ctx", new ContentProvider()); 

res = engine.eval("ctx.c(1)"); 

System.out.printf("rhino:> %s (%s)%n" 
     , res 
     , res != null ? res.getClass().getName() : null 
); 

простое выражение ctx.c(1) печатает:

rhino:> 1.1 (java.lang.Double) 

Теперь вот что происходит с ctx.c(1) + ctx.c(2):

rhino:> 1.12.2 (java.lang.String) 

И наконец (ctx.c(1) + ctx.c(2)) * ctx.c(3):

rhino:> nan (java.lang.Double) 

Rhino выполняет конкатенацию вместо чисел арифметика! Следующая программа работает, как и ожидалось, вместо этого:

engine.put("a", 1.1); 
engine.put("b", 2.2); 
engine.put("c", 3.3); 
res = engine.eval("(a + b) * c"); 

Выходы:

rhino:> 10,89 (java.lang.Double) 
+19

О мой бог, кто-то на самом деле использовали «Java» и тег «JavaScript» вместе * правильно *! – immibis

+0

Но почему бы просто не попробовать 'engine.eval (" typeof ctx.c (1) ")' и посмотреть, что JavaScript считает типом? – immibis

+0

typeof ctx.c (1) = объект – lunicon

ответ

4

Это странная особенность Rhino: в Java Number комплекта с engine.put("one", new Double(1)) работает, как ожидалось, в то время как результат методы Java зависит от типа возвращаемого значения объявленного самого метода, который читается с отражением API:

  • если это примитивный, как double, он преобразуется в число Javascript
  • иначе он обрабатывается, как и другие объекты хозяевах и + означает конкатенацию, либо Object как в вашем образце, а также Double

Вы можете настроить это поведение с помощью wrapFactory.setJavaPrimitiveWrap(false) на WrapFactory в текущем Context. Таким образом, код Rhino может храниться в начальной загрузке линии вашей программы и не загромождать ContentProvider (который я думаю, это какое-то конфигурация прокси-сервер)

С live Javadoc of WrapFactory.isJavaPrimitiveWrap()

По умолчанию метод возвращают правда, чтобы указать, что экземпляры String, Number, Boolean и Character должны быть обернуты как и любой другой объект Java и сценарии могут получить доступ к любой метод Java, доступные в этих объектов

Итак, вы можете установить этот флаг в false, чтобы указать, что Java Number должен быть преобразован в номера Javascript.Это займет всего две строки кода

Context ctx = Context.enter(); 
ctx.getWrapFactory().setJavaPrimitiveWrap(false); 

Вот the Gist с полным кодом я использовал для тестирования

+0

Спасибо. Он должен быть выделен жирным шрифтом в документации. – lunicon

+0

Согласен. Вместо этого мне пришлось отлаживать и загружать источники. Это также кажется странным по умолчанию – Raffaele

+0

Я столкнулся с этой проблемой и искал всю документацию, но ничего не нашел. Спасибо, что спасли мое время. – VGaur

1

Я создал значение обертку:

public static class JSValue extends sun.org.mozilla.javascript.internal.ScriptableObject 
{ 
    Object value; 

    public JSValue(Object value) { 
     this.value = value; 
    } 

    public String getClassName() { 
     return value != null? value.getClass().getName(): null; 
    } 

    @Override 
    public Object getDefaultValue(Class typeHint) { 
     if (typeHint == null || Number.class.isAssignableFrom(typeHint)) { 
      if (value instanceof Number) 
       return ((Number) value).doubleValue(); 
     } 

     return toString(); 
    } 

    @Override 
    public String toString() { 
     return value != null? value.toString(): null; 
    } 
} 

и функции редактирования:

public static class ContentProvider { 
    public Object c(int n) { 
    ... return new JSValue(1.1); 

сейчас выражение работает так, как ожидалось. Спасибо всем.