当参数是谓词时,EL 3.0 (JSR-341) 不工作?
EL 3.0 (JSR-341) not working when argument is a Predicate?
这个测试说明了我的问题:
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
截至 2021 年 9 月 6 日的更新
通过https://github.com/eclipse-ee4j/el-ri/commit/bcd0eeb349af607eb15428c04853a0be0948f80c
实施
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.
中结束
对于任何对解决方案感兴趣的人:
/**
* {@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;
}
}
这个测试说明了我的问题:
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
截至 2021 年 9 月 6 日的更新
通过https://github.com/eclipse-ee4j/el-ri/commit/bcd0eeb349af607eb15428c04853a0be0948f80c
实施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.
中结束对于任何对解决方案感兴趣的人:
/**
* {@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;
}
}