Java 如何使用比较器比较空值?

Java 如何使用比较器比较空值?,java,refactoring,comparator,Java,Refactoring,Comparator,我有一些比较器-,一个用于日期,一个用于小数,一个用于百分比,等等 起初,我的十进制比较器如下所示: class NumericComparator implements Comparator<String> { @Override public int compare(String s1, String s2) { final Double i1 = Double.parseDouble(s1); final Double i2 = Double.pars

我有一些比较器-,一个用于日期,一个用于小数,一个用于百分比,等等

起初,我的十进制比较器如下所示:

class NumericComparator implements Comparator<String> {

  @Override
  public int compare(String s1, String s2) {
    final Double i1 = Double.parseDouble(s1);
    final Double i2 = Double.parseDouble(s2);
    return i1.compareTo(i2);
  }

}
生活更美好。测试感觉更加可靠。然而,我的代码审阅者指出,“那么
null
s呢?”

很好,现在我必须用
NullPointerException
重复上述内容,或者在方法体前面加上:

if (s1 == null) {
  if (s2 == null) {
    return 0;
  } else {
    return -1;
  }
} else if (s2 == null) {
  return 1;
}
这种方法是巨大的。最糟糕的是,我需要用另外三个类重复这个模式,它们比较不同类型的字符串,并且在解析时可能引发另外三个异常

我不是Java专家。有比复制和粘贴更干净、更整洁的解决方案吗?只要有文档记录,我是否应该以正确性换取复杂性的缺乏



更新:一些人认为处理
null
值不是
比较器的工作。由于排序结果显示给用户,我确实希望对空值进行一致的排序。

您可以创建一个实用方法,在空值或解析异常的情况下处理解析并返回特定值。

这个问题有很多主观答案。这是我自己的0.02美元

首先,您所描述的问题是缺乏一流函数的语言的典型症状,这将使您能够简洁地描述这些模式


其次,在我看来,如果两个字符串中的一个不能被视为双精度的表示,那么将两个字符串作为双精度进行比较应该是错误的。(对于null等也是如此)因此,应该允许异常传播!我认为这将是一个有争议的观点。

如果您能够更改签名,我建议您编写该方法,以便它可以接受任何受支持的对象

  public int compare(Object o1, Object o2) throws ClassNotFoundException {
      String[] supportedClasses = {"String", "Double", "Integer"};
      String j = "java.lang.";
      for(String s : supportedClasses){
          if(Class.forName(j+s).isInstance(o1) && Class.forName(j+s).isInstance(o1)){
              // compare apples to apples
              return ((Comparable)o1).compareTo((Comparable)o2);
          }
      }
      throw new ClassNotFoundException("Not a supported Class");
  }

您甚至可以递归地定义它,将字符串转换为Double,然后返回使用这些对象调用自身的结果。

您应该首先创建一个从字符串返回Double的方法,嵌入null和解析失败案例(但您必须定义在这种情况下的操作:抛出异常?返回默认值??)

然后,比较器只需比较获得的双实例

换句话说,重构


但是我仍然想知道为什么你需要比较字符串,尽管你希望它们代表双精度。我的意思是,是什么阻止你在实际使用这个比较器的代码中操作双精度呢?

你正在实现一个
比较器
字符串
的方法,包括
compareTo
抛出一个
空点EXCEOption
如果将null交给它们,那么您也应该这样做。类似地,
Comparator
如果参数的类型阻止它们进行比较,则抛出一个
ClassCastException
。我建议您实现这些继承的行为

class NumericComparator implements Comparator<String> {

  public int compare(String s1, String s2) {
    final Double i1;
    final Double i2;
    if(s1 == null)
    {
      throw new NullPointerException("s1 is null"); // String behavior
    }
    try {
      i1 = Double.parseDouble(s1)
    } catch (NumberFormatException e) {
      throw new ClassCastException("s1 incorrect format"); // Comparator  behavior
    }

    if(s2 == null)
    {
      throw new NullPointerException("s2 is null"); // String behavior
    }
    try {
      i2 = Double.parseDouble(s1)
    } catch (NumberFormatException e) {
      throw new ClassCastException("s2 incorrect format"); // Comparator  behavior
    }
    return i1.compareTo(i2);
  }
}
类NumericComparator实现Comparator{
公共整数比较(字符串s1、字符串s2){
最终双i1;
最终双i2;
如果(s1==null)
{
抛出新的NullPointerException(“s1为null”);//字符串行为
}
试一试{
i1=Double.parseDouble(s1)
}捕获(数字格式){
抛出新的ClassCastException(“s1格式不正确”);//比较器行为
}
if(s2==null)
{
抛出新的NullPointerException(“s2为null”);//字符串行为
}
试一试{
i2=Double.parseDouble(s1)
}捕获(数字格式){
抛出新的ClassCastException(“s2格式不正确”);//比较器行为
}
返回i1。比较(i2);
}
}
通过进行类型检查和转换,您几乎可以恢复原始的优雅

class NumericComparator implements Comparator<String> {

  public int compare(String s1, String s2) {
    final Double i1;
    final Double i2;

    i1 = parseStringAsDouble(s1, "s1");
    i2 = parseStringAsDouble(s2, "s2");
    return i1.compareTo(i2);
  }

  private double parseStringAsDouble(String s, String name) {

    Double i;
    if(s == null) {
      throw new NullPointerException(name + " is null"); // String behavior
    }
    try {
      i = Double.parseDouble(s1)
    } catch (NumberFormatException e) {
      throw new ClassCastException(name + " incorrect format"); // Comparator  behavior
    }
    return i;
  }
}
类NumericComparator实现Comparator{
公共整数比较(字符串s1、字符串s2){
最终双i1;
最终双i2;
i1=parseStringAsDouble(s1,“s1”);
i2=parseStringAsDouble(s2,“s2”);
返回i1。比较(i2);
}
私有双parseStringAsDouble(字符串s,字符串名称){
双i;
如果(s==null){
抛出新的NullPointerException(name+“is null”);//字符串行为
}
试一试{
i=Double.parseDouble(s1)
}捕获(数字格式){
抛出新的ClassCastException(名称+“格式不正确”);//比较器行为
}
返回i;
}
}
如果您对异常消息不太在意,您可能会丢失“name”参数。我确信,通过应用一些小技巧,您可能会在这里或那里丢失额外的一行或多个单词


你说你需要用另外三个类来重复这个模式,它们比较不同类型的字符串,并且可能引发另外三个异常。在没有看到情况的情况下,很难提供详细信息,但是你可以在my
parseStringAsDouble
的一个版本上使用到
Nume的一个共同祖先中ricComparator
本身实现java的
比较器

请后退一步。那些
字符串
来自何处?这个
比较器
将用于什么?您是否有一个
字符串的
集合
,希望对其进行排序

所以我改进了compare()

当然有

首先,Comparator接口没有指定null的情况。如果null检查if语句适用于您的用例,这很好,但是一般的解决方案是抛出一个npe

至于清洁剂…为什么是最终的?为什么所有的捕获/抛出?为什么使用compareTo作为原始包装

class NumericComparator implements Comparator<String> {
 public int compare(String s1, String s2) throws NullPointerException, NumberFormatException {

  double test = Double.parseDouble(s1) - Double.parseDouble(s2);

  int retVal = 0;
  if (test < 0) retVal = -1;
  else if (test > 0) retVal = 1;

  return retVal;  
 }
}
类NumericComparator实现Comparator{
公共int比较(字符串s1、字符串s2)引发NullPointerException、NumberFormatException{
双重测试=double.parseDouble(s1)-double.parseDouble(s2);
int-retVal=0;
如果(测试<0)retVal=-1;
如果(测试>0)retVal=1,则为else;
返回返回;
}
}
看来你可能会被解雇
class NumericComparator implements Comparator<String> {
 public int compare(String s1, String s2) throws NullPointerException, NumberFormatException {

  double test = Double.parseDouble(s1) - Double.parseDouble(s2);

  int retVal = 0;
  if (test < 0) retVal = -1;
  else if (test > 0) retVal = 1;

  return retVal;  
 }
}
import com.google.common.base.Function;
import com.google.common.collect.Ordering;

Ordering.nullsFirst().onResultOf(
    new Function<String, Double>() {
      public Double apply(String s) {
      try {
        return Double.parseDouble(s);
      } catch (NumberFormatException e) {
        return null;
      }
    })
private Double getDouble(String number) {
 try {
  return Double.parseDouble(number);
 } catch(NumberFormatException e) {
  return null;
 }
}
if i1==null && i2!=null return -1
if i1==null && i2==null return 0
if i1!=null && i2==null return 1
if i1!=null && i2!=null return comparison
class NumericComparator implements Comparator<String> {

     public int compare(String s1, String s2) {
      final Double i1 = getDouble(s1);
      final Double i2 = getDouble(s2);

      return (i1 == null) ? (i2 == null) ? 0 : -1 : (i2 == null) ? 1 : i1.compareTo(i2);
     }
     private Double getDouble(String number) {
          try {
               return Double.parseDouble(number);
          } catch(NumberFormatException e) {
               return null;
          }
     }
}
public class ParsingComparator implements Comparator<String> {
  private Parser parser;

  public int compare(String s1, String s2) {
    Object c1 = parser.parse(s1);
    Object c2 = parser.parse(s2);
    new CompareToBuilder().append(c1, c2).toComparison();
  }
}
class NumericComparator implements Comparator<String> {
    private SafeAdaptor<Double> doubleAdaptor = new SafeAdaptor<Double>(){
        public Double parse(String s) {
            return Double.parseDouble(s);
        }
    };
    public int compare(String s1, String s2) {
        final Double i1 =doubleAdaptor.getValue(s1, "s1");
        final Double i2 = doubleAdaptor.getValue(s2, "s2");
        return i1.compareTo(i2);
    }
}

abstract class SafeAdaptor<T>{
    public abstract T parse(String s);
    public T getValue(String str, String name) {
        T i;
        if (str == null) {
            throw new NullPointerException(name + " is null"); // String
        }
        try {
            i = parse(str);
        } catch (NumberFormatException e) {
            throw new ClassCastException(name + " incorrect format"); // Comparator
        }
        return i;
    }

}