代码之家  ›  专栏  ›  技术社区  ›  rmuller

当参数是谓词时,EL 3.0(JSR-341)不起作用?

  •  4
  • rmuller  · 技术社区  · 8 年前

    这个测试显示了我的问题:

    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 javax.el.LambdaExpression@511baa65 of type class javax.el.LambdaExpression to interface java.util.function.Predicate

    这是EL 3的缺陷或限制还是我错过了什么?

    测试版本: org.glassfish:javax.el:jar:3.0.0 org.glassfish:javax.el:jar:3.0.1-b08

    也已发布 as github issue

    https://github.com/eclipse-ee4j/el-ri/commit/bcd0eeb349af607eb15428c04853a0be0948f80c

    2 回复  |  直到 5 年前
        1
  •  3
  •   BalusC    8 年前

    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中结束。下一个

        2
  •  3
  •   rmuller    8 年前

    对于任何对解决方案感兴趣的人:

    /**
     * {@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:rmuller@xiam.nl">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;
        }
    
    }