重载 "Number Classes" 的 + 和 += 运算符
overloading + and += operators for "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())
问题是,如果 +
过载,+=
也不能过载:
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
是您的 class,您可以将 plus
和 plusAssign
作为它的方法,这应该可以解决任何歧义。
您不能同时重载 +
和 +=
。超载其中之一。
When you write += in your code, theoretically both plus the plusAssign functions
can be called (see figure 7.2). If this is the case, and both functions are defined and
applicable, the compiler reports an error.
我 copied/pasted 来自 Kotlin in Action 一书!
Kotlin 中奇怪的 +=
运算符
你可以在kotlin中同时重载plus
运算符和plusAssign
运算符,但是你必须遵循kotlin的规则来解决奇怪的+=
冲突。
introduce an immutable structure of the class for the plus
operator which means any class outside the class can't edit its internal data.
introduce a mutable structure of the class for the plusAssign
operator which means its internal data can be edited anywhere.
kotlin 已经在 stdlib
中为 Collection
和 Map
类、Collection#plus and 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]
另一方面,你可以用不同的签名重载一个operator,每个签名对应不同的context,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+=0.1
替换为 a=a+0.1
,您将得到 false
,因为返回了一个新实例。一般来说,a=a+b
和 a+=b
在此实现中并不相同。
解决以上两个问题,我的建议是
operator fun SimpleDoubleProperty.plus(number: Number): SimpleDoubleProperty
= SimpleDoubleProperty(get() + number.toDouble())
因此您无需覆盖 plusAssign
。该解决方案不像你的那样通用,但如果你只有 SimpleDoubleProperty
计算,它是正确的,我相信你这样做,因为在你的实现中,plus
总是 returns a SimpleDoubleProperty
实例。
我想为 类 创建封装简单 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())
问题是,如果 +
过载,+=
也不能过载:
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
是您的 class,您可以将 plus
和 plusAssign
作为它的方法,这应该可以解决任何歧义。
您不能同时重载 +
和 +=
。超载其中之一。
When you write += in your code, theoretically both plus the plusAssign functions can be called (see figure 7.2). If this is the case, and both functions are defined and applicable, the compiler reports an error.
我 copied/pasted 来自 Kotlin in Action 一书!
Kotlin 中奇怪的 +=
运算符
你可以在kotlin中同时重载plus
运算符和plusAssign
运算符,但是你必须遵循kotlin的规则来解决奇怪的+=
冲突。
introduce an immutable structure of the class for the
plus
operator which means any class outside the class can't edit its internal data.introduce a mutable structure of the class for the
plusAssign
operator which means its internal data can be edited anywhere.
kotlin 已经在 stdlib
中为 Collection
和 Map
类、Collection#plus and 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]
另一方面,你可以用不同的签名重载一个operator,每个签名对应不同的context,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+=0.1
替换为 a=a+0.1
,您将得到 false
,因为返回了一个新实例。一般来说,a=a+b
和 a+=b
在此实现中并不相同。
解决以上两个问题,我的建议是
operator fun SimpleDoubleProperty.plus(number: Number): SimpleDoubleProperty
= SimpleDoubleProperty(get() + number.toDouble())
因此您无需覆盖 plusAssign
。该解决方案不像你的那样通用,但如果你只有 SimpleDoubleProperty
计算,它是正确的,我相信你这样做,因为在你的实现中,plus
总是 returns a SimpleDoubleProperty
实例。