简体   繁体   English

将Java中的两个自定义对象与可为空的字段进行比较

[英]Compare two custom objects in java with nullable fields

I have a Collection<A> 我有一个Collection<A>

class A {
    String name;
    Long id;
}

Now I have UI screen where I can provide name & id value to match this collection and display the result. 现在,我有了UI屏幕,可以在其中提供名称和ID值以匹配此集合并显示结果。

Now I have 3 criteria: 现在,我有3个条件:

  1. Only Id provided: Display all the records matchs with Id although name is null 仅提供ID:尽管名称为null,但显示所有与ID匹配的记录
  2. Only name 只有名字
  3. Both Name and Id. 名称和ID。

I can do it by three if check-based on null check of UI fields but I don't want to make if-else block because if I have multiple UI fileds it would be hectic to do nested if-else check. if基于UI字段的空检查, if检查基于三,我可以做到这一点,但是我不想创建if-else块,因为如果我有多个UI字段,那么嵌套if-else检查会很忙。

Please suggest a better way in java 7. 请在Java 7中提出一种更好的方法。

You could provide some predicates that are used to filter the collection. 您可以提供一些用于过滤集合的谓词。 Since java.util.function.Predicate has only been added with Java 8 you'd need to either roll your own or use a library such as Google Guava . 由于java.util.function.Predicate仅在Java 8中添加,因此您需要自己滚动或使用诸如Google Guava之类的库。

With that you could pass the predicates to be used in a filter: 这样,您可以传递要在过滤器中使用的谓词:

Collection<Predicate<A>> predicates = ...;//you get them from your UI
Collection<A> filtered = Collections2.filter( originalCollection, Preciates.and( predicates ) );

The predicates could then look like this: 谓词可能如下所示:

class ANamePredicate implements Predicate<A> {
  private String name; 

  public ANamePredicate( String n) { 
    name = n;
  }     

  boolean apply(@Nullable A input) { 
    //if one or both could be null, add handling for that
    return input.name.equals(n);
  }

  //implement equals(...) as well
}

Of course you could use anonymous classes as well but you'd then probably want to provide for an abstract base class that remove the requirement to always implement equals() : 当然,您也可以使用匿名类,但是您可能想要提供一个抽象基类,以消除始终实现equals()的要求:

abstract class AbstractPredicate<T> implements Predicate<T> {
  public boolean equals( Object o) { 
    //simplified, you might have to provide a better implementation, e.g. you want to provide a set of predicates
    return false;
  }
}

Then you create a collection of predicates when needed: 然后在需要时创建谓词集合:

Collection<Predicate<A>> predicates = new LinkedList<>();
predicates.add( new AbstractPredicate<A>() {
   boolean apply(A input) { 
    return "Skabdus".equals(input.name);
   }
} );
predicates.add( new AbstractPredicate<A>() {
   boolean apply(A input) { 
    return input.id == 1;
   }
} );

You could use the decorator pattern with expressions. 您可以将装饰器模式与表达式一起使用。 The principle is to have an interface that each "criteria" will implement (with one method : eval) and to have a distinction bewteen terminal operators and not terminal operators . 原理是具有每个“标准”都将实现的接口(使用一种方法:eval),并且在终端操作员之间 而非终端操作员之间具有区别。

You would have a NameIsSet class, an IdIsSet class as terminal opeators (or other classes if you have other implementations in the future), and logical expressions as non-terminal operators : And , Or , Not , ... 您将拥有一个NameIsSet类,一个IdIsSet类作为终端操作符(或其他类,如果将来有其他实现)以及逻辑表达式作为非终端操作符: ,...

Then, you should use the pattern this way : 然后,您应该通过以下方式使用模式:

Predicate predicate = new And(new IdIsSet("id"), new Not(new NameIsSet("name")));
if(predicate.eval()){
// Do something
}

If you want to set a particular behaviour for each case, you could create a List of these predicates and set a method to call if the predicate is true : 如果要为每种情况设置特定的行为,则可以创建这些谓词的列表,并设置一个在谓词为true时要调用的方法:

for(Predicate predicate: predicateList){
    if(predicate.eval()) {
       predicate.execute();
       //break if only one case is accepted
    }
}

Here is a sample of And class and NameIsSet class: 这是And类和NameIsSet类的示例:

public class And implements Predicate{
    private Predicate pred1;
    private Predicate pred2;

    public And(Predicate p1, Predicate p2){
        this.pred1 = p1;
        this.pred2 = p2;
    }

    @Override
    public boolean eval(){
        return this.pred1.eval() && this.pred2.eval();
    }

    @Override
    public void execute(){
        // Not sure of you want to do here
    }

}

public class NameIsSet implements Predicate{
    private String name;

    public NameIsSet (String name){
        this.name = name;
    }

    @Override
    public boolean eval(){
        return this.name == null || this.name.length = 0;
    }

    @Override
    public void execute(){
        // Not sure of you want to do here
    }

}

In this implementation, you must create all your predicates beforehand, add them to a list and set the execute() method for each. 在此实现中,您必须事先创建所有谓词,将它们添加到列表中,然后为每个谓词设置execute()方法。

Hope this will be useful to you. 希望这对您有用。

Your if statements don't need to be nested. 您的if语句不需要嵌套。 You can, for example, do this: 例如,您可以执行以下操作:

static boolean matches(A candidate, A pattern) {
    if (pattern.name != null && !candidate.name.equals(pattern.name))
        return false;
    if (pattern.id != null && !candidate.id.equals(pattern.id))
        return false;
    return true;
}

This scales at a 1:1 rate with any number of fields. 在任何数量的字段中,它都以1:1的比例缩放。 (You could use candidate.id != pattern.id there, but I used equals for consistency.) (您可以在此处使用candidate.id != pattern.id ,但是为了保持一致性,我使用了equals 。)

If you have dozens of fields and you don't want to name them individually, you can use reflection and process all the fields with a single for loop: 如果您有数十个字段并且不想单独命名,则可以使用反射并通过一个for循环处理所有字段:

static boolean matches(A candidate, A pattern) throws IllegalAccessException {
    for (Field fld : A.class.getDeclaredFields()) {
        Object c = fld.get(candidate);
        Object p = fld.get(pattern);
        if (p != null && !c.equals(p))
            return false;
    }
    return true;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM