为“Number Classes”重载+和+ =运算符
我想为封装简单Number
的类创建扩展函数。 例如DoubleProperty
。 我遇到了这个问题,我不能同时重载+
和+=
运算符。
我不想创建一个行为,通过以下测试:
class DoublePropertyTest { lateinit var doubleProperty: DoubleProperty @Before fun initialize() { doubleProperty = SimpleDoubleProperty(0.1) } @Test fun plus() { val someProperty = doubleProperty + 1.5 assertEquals(someProperty.value, 1.6, 0.001) } @Test fun plusAssign() { val someProperty = doubleProperty doubleProperty += 1.5 //error if + and += are overloaded assert(someProperty === doubleProperty) //fails with only + overloaded assertEquals(doubleProperty.value, 1.6, 0.001) } }
它可以使用这些扩展功能来实现:
operator fun ObservableDoubleValue.plus(number: Number): DoubleProperty = SimpleDoubleProperty(get() + number.toDouble()) operator fun WritableDoubleValue.plusAssign(number: Number) = set(get() + number.toDouble())
问题是,如果+
被overlodaded +=
不能超载,以及:
Assignment operators ambiguity. All these functions match. - public operator fun ObservableDoubleValue.plus(number: Number): DoubleProperty - public operator fun WritableDoubleValue.plusAssign(number: Number): Unit
如果我只重载+
运算符, DoubleProperty
在+=
操作上返回一个新的DoubleProperty
对象,而不是最初的那个。
有没有办法解决这个限制?
Kotlin中奇怪的+=
运算符
你可以重载plusAssign
的plus
操作符和plusAssign
操作符,但是你必须遵循kotlin的规则来解决奇怪的+=
冲突。
为
plus
运算符引入类的不可变结构 ,这意味着类外的任何类都不能编辑其内部数据。为
plusAssign
操作符引入一个可变结构的类,这意味着它的内部数据可以在任何地方编辑。
kotlin已经在Stdlib中为Collection
&the Map
类, Collection#plus和MutableCollection#plusAssign做了如下的事情 :
operator fun <T> Collection<T>.plus(elements: Iterable<T>): List<T> // ^--- immutable structure operator fun <T> MutableCollection<in T>.plusAssign(elements: Iterable<T>) // ^--- mutable structure
但是,等等,当我们使用+=
运算符时如何解决冲突呢?
如果列表是一个不可变的Collection
那么你必须定义一个可变的var
变量,然后使用plus
运算符,因为它的内部状态不能被编辑。 例如:
// v--- define `list` with the immutable structure explicitly var list: List<Int> = arrayListOf(1); //TODO: try change `var` to `val` val addend = arrayListOf(2); val snapshot = list; list += addend; // ^--- list = list.plus(addend); // list = [1, 2], snapshot=[1], addend = [2]
如果列表是可变的MutableCollection
那么你必须定义一个不可变的val
变量,然后使用plusAssign
运算符,因为它的内部状态可以在任何地方编辑。 例如:
// v--- `list` uses the mutable structure implicitly val list = arrayListOf(1); //TODO: try change `val` to `var` val addend = arrayListOf(2); val snapshot = list; list += addend; // ^--- list.plusAssign(addend); // list = [1, 2], snapshot=[1, 2], addend = [2]
另一方面,你可以使用差异签名来重载一个运算符 ,每个签名用于不同的上下文 ,而kotlin也可以这样做,例如: Collection#plus 。 例如:
var list = listOf<Int>(); list += 1; //list = [1]; // ^--- list = list.plus(Integer); list += [2,3]; //list = [1, 2, 3] // ^--- list = list.plus(Iterable);
您的操作员重写实现有两个问题:
1. plus
后不一致的类型
operator fun ObservableDoubleValue.plus(number: Number): DoubleProperty = SimpleDoubleProperty(get() + number.toDouble())
任何ObservableDoubleValue
实例加上一个Number
,得到一个DoubleProperty
实例(或者说一个SimpleDoubleProperty
实例)。 比方说,我有一个ComplexDoubleProperty
实现了ObservableDoubleValue
类型,你会看到:
var a = getComplexDoubleProperty() a = a + 0.1 //compile error, WTF? //or even var b = SimpleDoubleProperty(0.1) b = b + 0.1 //compile error, because b+0.1 is DoubleProperty
你可以看到这种行为没有任何意义。
2. a = a + b和a + = b应该是相同的
如果你的实现编译,你会有
var a: DoubleProperty = SimpleDoubleProperty(0.1) //add DoubleProperty to make it compile var b = a a += 0.1 println(b == a)
打印true
因为+=
将值设置为原始实例。 如果用a=a+0.1
代替a+=0.1
,则会返回false
因为返回了新的实例。 一般来说, a=a+b
和a+=b
在这个实现中是不相同的。
为了解决上述两个问题,我的建议是
operator fun SimpleDoubleProperty.plus(number: Number): SimpleDoubleProperty = SimpleDoubleProperty(get() + number.toDouble())
所以你不需要重写plusAssign
。 该解决方案不如您的一般,但是如果您只有SimpleDoubleProperty
计算是正确的,并且我相信您这样做,因为在您的实现中,总是返回一个SimpleDoubleProperty
实例。
你不能重载+
和+=
。 重载其中之一。
当你在你的代码中写入+ =时,理论上可以调用两个plusAssign函数(见图7.2)。 如果是这种情况,并且这两个函数都已定义并适用,则编译器将报告一个错误。
我从行动书中复制/粘贴Kotlin !
如果DoubleProperty
是您的类,则可以使用plus
和plusAssign
其方法,这可以解决任何不明确的问题。