instanceof check in EL expression language

2019-01-11 06:21发布

Is there a way to perform an instanceof check in EL?

E.g.

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

7条回答
Deceive 欺骗
2楼-- · 2019-01-11 06:32

There is a way, see

JSF EL: instanceof reserved but not yet implemented?

However, the instanceof operator is still not implemented, at least in Mojarra 2.1. Please vote for the bug here:

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

The best workaround currently is probably to store the class name in a backing bean getter instead of creating a boolean test method for each class:

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

So it would be a mix of BalusC's and flash's solutions. It would however be much more readable in JSF than BalusC's plus it pretty much resembles the future instanceof operator use:

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

This will not produce one method per class test on the backing bean as flash suggested. This could be slower than flash's though.

查看更多
趁早两清
3楼-- · 2019-01-11 06:36

it works:

rendered="#{node.getClass().getSimpleName() == 'Logt_anno'}"
查看更多
一纸荒年 Trace。
4楼-- · 2019-01-11 06:40

You could compare Class#getName() or, maybe better, Class#getSimpleName() to a String.

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

Note the importance of specifying Object#getClass() with brace notation ['class'] because class is a reserved Java literal which would otherwise throw an EL exception in EL 2.2+.

The type safe alternative is to add some public enum Type { A, B } along with public abstract Type getType() to the common base class of the model.

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

Any invalid values would here throw an EL exception during runtime in EL 2.2+.

In case you're using OmniFaces, since version 3.0 you could use #{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>
查看更多
劳资没心,怎么记你
5楼-- · 2019-01-11 06:40

Define a static function like:

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

Define a custom EL function for it, and use that. We could also pass a string name and do a forName() inside the method.

查看更多
三岁会撩人
6楼-- · 2019-01-11 06:42

That doesn't work in EL. Use the backing bean for this:

public class MyBean {

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


}

And then do the check by calling the backing bean:

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

查看更多
小情绪 Triste *
7楼-- · 2019-01-11 06:42

You could use a helper bean for that:

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

Usage:

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

This has the advantage that inheritance is taken into account and you can test for classes which you can't modify (both disadvantages of the solutions of BalusC).

If you like to use the simple class name (and don't fear name clashes), you could use a lookup map which you fill by hand or with a class path scanner like 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);
  }
}

You could even move the helper function to an 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 ...
}

Usage:

<h:link rendered="#{model.isInstanceOf('package.ClassA')}">
  #{errorMessage1}
</h:link>
查看更多
登录 后发表回答