2017-10-04 118 views
3

該測試顯示我的問題:當參數是謂詞時,EL 3.0(JSR-341)不工作?

public static String testMe(Predicate<String> filter) { 
    return "yeah!"; 
} 

@Test 
public void testPredicateAsArgument() throws ClassNotFoundException, NoSuchMethodException { 
    final ELProcessor elp = new ELProcessor(); 
    elp.defineFunction("", "", "test.EL3Test", "testMe"); 
    try { 
     Object result = elp.eval("testMe(o->true)"); // IllegalArgumentException 
     // ... 
    } catch (javax.el.ELException ex) { 
     fail(Exceptions.getCauseMessage(ex)); 
    } 
} 

它拋出:IllegalArgumentException: Cannot convert [email protected] of type class javax.el.LambdaExpression to interface java.util.function.Predicate

這是EL 3的錯誤或限制還是我錯過了什麼?

測試版本:org.glassfish:javax.el:jar:3.0.0org.glassfish:javax.el:jar:3.0.1-b08

還貼出as github issue

回答

2

EL 3.0的Java 1.7(Java EE的7的目標),而不是Java 1.8中創建。換句話說,它早於Java本地lambda,因此不可能知道它們的任何事情。

你可以做的最好的事情是發佈下一個EL版本的增強問題。我們目前已經在Java EE 8上(針對Java 1.8),但目前還沒有EL 3.1。您必須等待Java EE 9的任何增強請求才有可能在EL.next中結束。

+0

感謝。自EL3.0以來(使用Java 8語法)支持lambdas這一事實使我感到困惑,並且同樣適用於支持Streams(包括使用lambda語法的篩選器操作)。但是,只檢查了實現,所有這些功能都是使用Java 7進行模擬的。 – rmuller

1

任何有興趣的解決方案:

/** 
* {@code FunctionalInterfaceResolver} intercepts method calls with EL lambda expressions as 
* arguments which need to coerce to Java 8 functional interfaces. 
* 
* A custom `ELResolver` is always consulted before the default resolvers. 
* 
* Example usage: 
* ```` java 
* final ELProcessor elp = new ELProcessor(); 
* elp.getELManager().addELResolver(new FunctionalInterfaceResolver()); 
* final Object result = elp.eval("p.findResources(o->o.getContentType() eq 'image/png')"); 
* ```` 
* 
* @author <a href="mailto:[email protected]">Ronald K. Muller</a> 
*/ 
public final class FunctionalInterfaceResolver extends ELResolver { 

    @Override 
    public Object invoke(final ELContext context, final Object base, final Object method, 
     final Class<?>[] paramTypes, final Object[] params) { 

     if (context == null || base == null || !(method instanceof String) || params == null) { 
      return null; 
     } 
     // takes about 5ms. Try out caching if it becomes a bottleneck 
     for (int i = 0; i < params.length; ++i) { 
      if (params[i] instanceof javax.el.LambdaExpression) { 
       for (Method m : base.getClass().getMethods()) { 
        if (m.getName().equals(method) && m.getParameterCount() == params.length) { 
         final Class[] types = m.getParameterTypes(); 
         if (types[i].isAnnotationPresent(FunctionalInterface.class)) { 
          params[i] = coerceToFunctionalInterface(context, 
           (LambdaExpression)params[i], types[i]); 
         } 
        } 
       } 
      } 
     } 
     return null; 
    } 

    @Override 
    public Class<?> getType(ELContext context, Object base, Object property) { 
     return null; 
    } 

    @Override 
    public void setValue(ELContext context, Object base, Object property, Object value) { 
    } 

    @Override 
    public boolean isReadOnly(ELContext context, Object base, Object property) { 
     return false; 
    } 

    @Override 
    public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) { 
     return null; 
    } 

    @Override 
    public Class<?> getCommonPropertyType(ELContext context, Object base) { 
     return String.class; 
    } 

    @Override 
    public Object convertToType(ELContext context, Object obj, Class<?> targetType) { 
     if (obj instanceof LambdaExpression && 
      targetType.isAnnotationPresent(FunctionalInterface.class)) { 
      context.setPropertyResolved(obj, targetType); 
      return coerceToFunctionalInterface(context, (LambdaExpression)obj, targetType); 
     } 
     return null; 
    } 

    private Object coerceToFunctionalInterface(
     final ELContext context, final LambdaExpression elLambda, final Class<?> targetType) { 

     assert targetType.isAnnotationPresent(FunctionalInterface.class); 
     return Proxy.newProxyInstance(targetType.getClassLoader(), 
      new Class[]{targetType}, (Object obj, Method method, Object[] args) -> { 

      // a FunctionalInterface has exactly one abstract method 
      if (Modifier.isAbstract(method.getModifiers())) { 
       // the "functional method" 
       return elLambda.invoke(context, args); 
      } else if ("toString".equals(method.getName())) { 
       return "Proxy[" + targetType.getName() + ", wrapping " + 
        elLambda.getClass().getName() + ']'; 
      } else { 
       throw new AssertionError("Method not expected: " + method.getName()); 
      } 
     }); 
    } 

    @Override 
    public Object getValue(ELContext context, Object base, Object property) { 
     return null; 
    } 

}