【问题标题】:instanceof check in EL expression languageEL 表达式语言中的 instanceof 检查
【发布时间】:2012-05-06 01:14:59
【问题描述】:

有没有办法在 EL 中执行 instanceof 检查?

例如

<h:link rendered="#{model instanceof ClassA}">      
    #{errorMessage1}
</h:link>
<h:link rendered="#{model instanceof ClassB}">      
    #{errorMessage2}
</h:link>

【问题讨论】:

    标签: jsf el instanceof


    【解决方案1】:

    您可以将Class#getName()Class#getSimpleName()String 进行比较。

    <h:link rendered="#{model['class'].simpleName eq 'ClassA'}">      
        #{errorMessage1}
    </h:link>
    <h:link rendered="#{model['class'].simpleName eq 'ClassB'}">      
        #{errorMessage2}
    </h:link>
    

    请注意使用大括号符号 ['class'] 指定 Object#getClass() 的重要性,因为 class 是保留的 Java 文字,否则会在 EL 2.2+ 中引发 EL 异常。

    类型安全的替代方法是在模型的公共基类中添加一些 public enum Type { A, B }public abstract Type getType()

    <h:link rendered="#{model.type eq 'A'}">      
        #{errorMessage1}
    </h:link>
    <h:link rendered="#{model.type eq 'B'}">      
        #{errorMessage2}
    </h:link>
    

    任何无效值都会在 EL 2.2+ 的运行时引发 EL 异常。

    如果你使用OmniFaces,从3.0版开始你可以使用#{of:isInstance()}

    <h:link rendered="#{of:isInstance('com.example.ClassA', model)}">      
        #{errorMessage1}
    </h:link>
    <h:link rendered="#{of:isInstance('com.example.ClassB', model)}">      
        #{errorMessage2}
    </h:link>
    

    【讨论】:

    • @Lucas:如果实现是接口是不可能的 :) 如果你对它有问题,那是由其他原因引起的。
    • 类字符串测试建议在某些情况下很有用,但它不适用于接口或子类。对 instanceof 有一个真实而迫切的需求。请在这里投票:java.net/jira/browse/JSP_SPEC_PUBLIC-113。另请参阅相关问题,如何使用“渲染”以外的某种测试调用绑定到类型的复合组件仅用于匹配类型,这在构建阶段似乎不起作用(stackoverflow.com/questions/16665705/… 底部的备注)跨度>
    • 层次继承呢。我有一个 Item 类,它有两个子类,即 Product 和 Service。反过来,产品类有两个子类,名为 Medicine 和 Consumable。所以我想检查天气一个医学实体是一个产品吗?仅当我将其检查为 Medicine 时才会返回 true,但在检查为 Product 时则不会。
    • @jonnieZG:不,使用答案第二部分中所示的 type safe 替代方法(换句话说,适用于所有类型/子类)。
    • @BalusC 我不认为您的第二个答案是“类型安全”。以上面评论中的例子为例-即使我有时需要检查“产品”,您的提案也会返回“药物”。尽管我并不总是能够修改一个类。而model['class'].simpleName 可能会在使用代理的环境(如 JPA)中导致虚假错误。因此,我想出了自己的解决方案。我想知道为什么 OmniFaces 不包含类似的东西,例如自定义 EL 函数。
    【解决方案2】:

    这在EL 中不起作用。为此使用支持 bean:

    public class MyBean {
    
        public boolean getIsClassA() {
            if(model instanceof ClassA) {
                return true;
            }
            return false;
        }
    
    
    }
    

    然后通过调用 backing bean 进行检查:

    <h:link outcome="#{PageNameA}?faces-redirect=true&amp;" rendered="#{myBean.isClassA}">      
        #{errorMessage}
    </h:link>
    

    【讨论】:

    • 这确实是一个很好的答案。但是我采用了另一种解决方案:rendered="#{listRow.rowData.getClass().getName() == 'ClassB'}" (ClassB 必须完全限定)。这种“不那么优雅”的解决方案有什么强烈的反对意见吗?
    • 我认为这个解决方案更好,因为它更容易理解,但这在某种程度上取决于意图。如果您的意图是只显示一个链接或另一个,那么只在视图中放置一个链接,并为 业务逻辑 使用一个支持 bean,该 bean 决定结果应该是什么。这也更具可测试性。如果您想显示两个链接,那么其他解决方案可以正常工作,但我仍然更喜欢支持 bean 的可测试性和清晰性。你有一个非常好的语言(java),所以没有必要在 EL 中做疯狂的事情和硬编码类名。
    • 这是一个比 BalusC 建议的(有时仍然有用的)基于 EL 的类字符串测试更通用的解决方案,因为它可以用于接口和子类,但是必须全部都这样做很痛苦时间,并且它经常污染支持 bean 和/或实体(或放置测试的任何地方)。我们需要 JSF2.x a.s.a.p 中的 instanceof !
    【解决方案3】:

    有效:

    rendered="#{node.getClass().getSimpleName() == 'Logt_anno'}"
    

    【讨论】:

      【解决方案4】:

      定义一个静态函数,如:

      public boolean isInstanceOf( Object obj, Class targetClass) {
              return targetClass.isInstance(obj);
          }
      

      为它定义一个自定义的 EL 函数,并使用它。 我们还可以传递一个字符串名称,并在方法中使用forName()

      【讨论】:

        【解决方案5】:

        有办法,看

        JSF EL: instanceof reserved but not yet implemented?

        但是,instanceof 运算符仍然没有实现,至少在 Mojarra 2.1 中是这样。请在此处为错误投票:

        http://java.net/jira/browse/JSP_SPEC_PUBLIC-113

        目前最好的解决方法可能是将类名存储在支持 bean getter 中,而不是为每个类创建一个布尔测试方法:

        public String getSelectedNodeClassName()
        {
            return selectedNode.getClass().getSimpleName();
        }
        

        所以它将是 BalusC 和 flash 解决方案的混合体。然而,它在 JSF 中的可读性要比 BalusC 高得多,而且它非常类似于未来 instanceof 运算符的使用:

        rendered="#{nodeManager.selectedNodeClassName eq 'ChapterNode'}"
        

        这不会像 flash 建议的那样在支持 bean 上为每个类测试生成一个方法。不过这可能比 flash 慢。

        【讨论】:

          【解决方案6】:

          不是很优雅,因为它混合了 JSP EL 和早期的 表达式语法,但不需要任何额外的 Java 代码:

          <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
          
          <c:set var="interfaceClass" value="<%=com.example.ClassA.class%>"/>
          <c:set var="implementationClass" value="${model['class']}"/>
          <c:if test="${interfaceClass.isAssignableFrom(implementationClass)}">
              <%-- Your logic here. --%>
          </c:if>
          

          【讨论】:

            【解决方案7】:

            您可以为此使用辅助 bean:

            @ManagedBean
            public class Helper {
              public boolean isInstance(Object bean, String fullyQualifiedClassName) {
                return Class.forName(fullyQualifiedClassName).isInstance(bean);
              }
            }
            

            用法:

            <h:link rendered="#{helper.isInstance(model, 'package.ClassA')}">
              #{errorMessage1}
            </h:link>
            

            这样做的好处是考虑了继承,并且您可以测试您无法修改的类(BalusC 解决方案的两个缺点)。

            如果您喜欢使用简单的类名(并且不要担心名称冲突),您可以使用手动填写的查找图或使用类路径扫描器(如 org.reflections):

            @ManagedBean
            @ApplicationScoped
            public class Helper {
              private Map<String, Class<? extends MyBaseClass>> classes = 
                  new Reflections("myrootpackage").getSubTypesOf(MyBaseClass.class).stream()
                  .collect(Collectors.toMap(Class::getSimpleName, Function.identity()));
            
              public boolean isInstance(Object bean, String simpleClassName) {
                final Class<? extends MyBaseClass> c = this.classes.get(simpleClassName);
                return c != null && c.isInstance(bean);
              }
            }
            

            您甚至可以将辅助函数移动到 ELResolver:

            public class InstanceOfELResolver extends ELResolver {
            
              public Object invoke(final ELContext context, final Object base, 
                  final Object method, final Class<?>[] paramTypes, final Object[] params) {
                if ("isInstanceOf".equals(method) && params.length == 1) {
                  context.setPropertyResolved(true);
                  try {
                    return params[0] != null && Class.forName(params[0].toString()).isInstance(base);
                  } catch (final ClassNotFoundException e) {
                    return false;
                  }
                }
                return null;
              }
            
              // ... All other methods with default implementation ...
            }
            

            用法:

            <h:link rendered="#{model.isInstanceOf('package.ClassA')}">
              #{errorMessage1}
            </h:link>
            

            【讨论】:

              猜你喜欢
              • 2011-06-24
              • 2011-01-13
              • 1970-01-01
              • 1970-01-01
              • 2011-07-20
              • 2010-12-19
              • 2013-12-28
              相关资源
              最近更新 更多