overloading + and += operators for “Number Classes”

前端 未结 4 830
野性不改
野性不改 2021-02-08 14:45

I want to create extension functions for classes that encapsulate simple Numbers. For example DoubleProperty. I encountered the problem, that I can\'t

相关标签:
4条回答
  • 2021-02-08 15:07

    If DoubleProperty is your class, you can make plus and plusAssign its methods, that should resolve any ambiguity.

    0 讨论(0)
  • 2021-02-08 15:18

    The strange += operator in Kotlin

    you can both overloading the plus operator and plusAssign operator in kotlin, but you must following the rules of kotlin to solving the strange += conflicts.

    1. introduce an immutable structure of the class for the plus operator which means any class outside the class can't edit its internal data.

    2. introduce a mutable structure of the class for the plusAssign operator which means its internal data can be edited anywhere.

    the kotlin has already done such things in the stdlib for the Collection & the Map classes, the Collection#plus and MutableCollection#plusAssign as below:

    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
    

    But wait, how to solving the conflict when we using the += operator?

    IF the list is an immutable Collection then you must define a mutable var variable, then the plus operator is used since its internal state can't be edited. for example:

    //         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]
    

    IF the list is a mutable MutableCollection then you must define a immutable val variable, then the plusAssign operator is used since its internal state can be edited anywhere. for example:

    //    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]
    

    On the other hand, you can overloads an operator with diff signatures, each signature for the different context, and kotlin also do it, e.g: Collection#plus. for example:

    var list = listOf<Int>();
    
    list += 1; //list = [1];
    //   ^--- list = list.plus(Integer);
    
    list += [2,3]; //list = [1, 2, 3]
    //   ^--- list = list.plus(Iterable);
    
    0 讨论(0)
  • 2021-02-08 15:19

    Your operator override implementation has two problems:

    1. inconsistent type after plus

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

    Any ObservableDoubleValue instance plus a Number, got a DoubleProperty instance(or say a SimpleDoubleProperty instance). Let's say I have a type ComplexDoubleProperty implements ObservableDoubleValue, you will see:

    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
    

    You can see this behavior makes no sense.

    2. a=a+b and a+=b should be identical

    If your implementation compiles, you will have

    var a: DoubleProperty = SimpleDoubleProperty(0.1)  //add DoubleProperty to make it compile
    var b = a
    a += 0.1
    println(b == a)
    

    prints true because += sets the value to the original instance. If you replace a+=0.1 with a=a+0.1 you will get false because a new instance is returned. Generally speaking, a=a+b and a+=b are not identical in this implementation.

    To fix the two problems above, my suggestion is

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

    so you don't need to override plusAssign. The solution is not as general as yours, but it's correct if you only have SimpleDoubleProperty calculations, and I believe you do, because in your implementation, plus always returns a SimpleDoubleProperty instance.

    0 讨论(0)
  • 2021-02-08 15:20

    You cannot overload both + and +=. Overload one of the them.

    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.

    I copied/pasted from Kotlin in Action book!

    0 讨论(0)
提交回复
热议问题