为"数字类"重载+和+ =运算符

Jho*_*007 9 operator-overloading kotlin extension-function

我想为封装简单Numbers的类创建扩展函数.例如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)
    }
}
Run Code Online (Sandbox Code Playgroud)

它可以使用这些扩展函数实现:

operator fun ObservableDoubleValue.plus(number: Number): DoubleProperty 
    = SimpleDoubleProperty(get() + number.toDouble())

operator fun WritableDoubleValue.plusAssign(number: Number) 
    = set(get() + number.toDouble())
Run Code Online (Sandbox Code Playgroud)

问题是,如果+是过度的,那么+=也不能过载:

Assignment operators ambiguity. All these functions match.
- public operator fun ObservableDoubleValue.plus(number: Number): DoubleProperty
- public operator fun WritableDoubleValue.plusAssign(number: Number): Unit
Run Code Online (Sandbox Code Playgroud)

如果我只重载+操作符,DoubleProperty则在+=操作上返回一个新对象而不是初始对象.

有办法解决这个限制吗?

hol*_*ava 7

+=Kotlin 的奇怪操作员

你可以在kotlin中重载plus运算符和plusAssign运算符,但是你必须遵循kotlin的规则来解决奇怪的+=冲突.

  1. 为运算符引入类的不可变结构,plus这意味着类外的任何类都无法编辑其内部数据.

  2. 为操作符引入类的可变结构,plusAssign这意味着它的内部数据可以在任何地方编辑.

kotlin已经在stdlibfor Collection&Mapclasses中完成了这些事情,Collection#plusMutableCollection#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
Run Code Online (Sandbox Code Playgroud)

但是等一下,当我们使用+=运营商时如何解决冲突呢?

如果列表是不可变的,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]
Run Code Online (Sandbox Code Playgroud)

如果列表是可变的,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]
Run Code Online (Sandbox Code Playgroud)

另一方面,您可以使用diff签名重载操作符,每个签名用于不同的上下文,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);
Run Code Online (Sandbox Code Playgroud)