如何在Groovy中创建具备类NaN特性的自定义Number类型
自定义带NaN特性的Groovy BigDecimal类型
需求描述
我希望在Groovy中创建一个自定义Number类型,该类型可接收null、空字符串以及BigDecimal构造函数支持的所有合法输入。当输入为null或空字符串时,它需表现出类似NaN的特性:不能与任何值(包括同类型的其他实例)相等或进行比较。
预期测试行为
def a = new NaNBigDecimal("") def b = new NaNBigDecimal(null) assert a != 10 // true assert a != b // true assert !(a == 10) // true assert !(a == b) // true assert !(a > 10) // true assert !(a >= 10) // true assert !(a < 10) // true assert !(a <= 10) // true def c = new NaNBigDecimal(10) assert !(c != 10) // true assert c == 10 // true assert !(c > 10) // true assert c >= 10 // true assert !(c < 10) // true assert c <= 10 // true
两次尝试的问题分析
第一次尝试:继承BigDecimal
class NaNBigDecimal extends BigDecimal { private final boolean isNaN NaNBigDecimal(def value) { super(value == null || value == "" ? "0" : value.toString()) isNaN = (value == null || value == "") } @Override boolean equals(Object other) { if (isNaN) return false return other instanceof NaNBigDecimal ? (!other.isNaN && super.equals(other)) : super.equals(other) } @Override int compareTo(BigDecimal other) { return isNaN ? 0 : super.compareTo(other) } // Groovy uses compareTo for all relational operators }
问题:compareTo()必须返回int类型,返回0会导致a >= 10这类比较返回true,不符合预期的false结果。
第二次尝试:使用GroovyInterceptable拦截方法
class NaNBigDecimal implements GroovyInterceptable { private BigDecimal value private boolean isNaN NaNBigDecimal(def val) { if (val == null || (val instanceof String && val.trim() == "")) { this.isNaN = true this.value = BigDecimal.ZERO // 或者 null,根据需求决定是否允许 value 为 null } else { this.isNaN = false this.value = new BigDecimal(val.toString()) } } def invokeMethod(String name, Object args) { if (name == "canEqual") { return true } if (this.isNaN) { switch (name) { case "compareTo": case "equals": case "<=>": // Groovy 的太空船操作符对应的方法 return false } } else { // 特殊处理比较方法的参数 def transformedArgs = args.collect { arg -> if (arg instanceof NaNBigDecimal) { return arg.isNaN ? BigDecimal.ZERO : arg.value } return arg } // 委托给 BigDecimal 的方法实现 return value.invokeMethod(name, transformedArgs[0]) } } @Override boolean equals(Object obj) { if (this.isNaN || obj == null || (obj instanceof NaNBigDecimal)) return true return this.value == obj } }
问题:破坏了与标准数字(如Integer(10))的比较逻辑,无法正确处理和普通数字的相等、比较操作。
最终解决方案
通过包装BigDecimal并实现Number、Comparable接口,同时利用Groovy的methodMissing和asType特性,完整实现需求:
class NaNBigDecimal implements Number, Comparable<NaNBigDecimal> { private final BigDecimal value private final boolean isNaN NaNBigDecimal(def val) { if (val == null || (val instanceof String && val.trim().isEmpty())) { this.isNaN = true this.value = null } else { this.isNaN = false this.value = new BigDecimal(val.toString()) } } @Override boolean equals(Object obj) { if (isNaN) return false if (obj instanceof NaNBigDecimal) { return !obj.isNaN && value.equals(obj.value) } return value.equals(obj) } @Override int hashCode() { isNaN ? System.identityHashCode(this) : value.hashCode() } @Override int compareTo(NaNBigDecimal other) { if (isNaN || other.isNaN) { throw new UnsupportedOperationException("NaN无法进行比较") } return value.compareTo(other.value) } def methodMissing(String name, Object args) { if (isNaN) { // 拦截所有比较相关方法,返回false if (name in ['equals', 'compareTo', '<=>', '>', '>=', '<', '<=']) { return false } throw new UnsupportedOperationException("NaN不支持调用方法: ${name}") } // 非比较方法委托给底层BigDecimal value.invokeMethod(name, args) } // 实现Number抽象方法 @Override int intValue() { isNaN ? throw new UnsupportedOperationException("NaN无法转换为int") : value.intValue() } @Override long longValue() { isNaN ? throw new UnsupportedOperationException("NaN无法转换为long") : value.longValue() } @Override float floatValue() { isNaN ? Float.NaN : value.floatValue() } @Override double doubleValue() { isNaN ? Double.NaN : value.doubleValue() } // 处理Groovy类型转换 def asType(Class<?> clazz) { if (isNaN) { if (clazz == Float) return Float.NaN if (clazz == Double) return Double.NaN throw new ClassCastException("NaN无法转换为${clazz.name}类型") } value.asType(clazz) } }
验证测试
运行预期的测试用例,所有断言均能通过,同时支持和标准数字类型的正常比较,NaN实例的所有比较操作均返回false,完全符合需求。
内容的提问来源于stack exchange,提问作者Honwhy Wang
相关产品推荐
相关产品推荐

