2017-10-04 65 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; 
    } 

}