Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

overloading + and += operators for "Number Classes"

I want to create extension functions for classes that encapsulate simple Numbers. For example DoubleProperty. I encountered the problem, that I can't overload the + and the += operator at the same time.

I wan't to create a behaviour, that passes following tests:

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)
    }
}

It could be implemented using these extension functions:

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

operator fun WritableDoubleValue.plusAssign(number: Number) 
    = set(get() + number.toDouble())

The problem is, that if + is overlodaded the += can't be overloaded aswell:

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

If I only overload the + operator, a new DoubleProperty object is returned on += operations instead of the initial one.

Is there a way to work around this limitation?

like image 570
Jhonny007 Avatar asked Jun 15 '17 04:06

Jhonny007


2 Answers

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);
like image 127
holi-java Avatar answered Sep 27 '22 17:09

holi-java


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!

like image 33
LiTTle Avatar answered Sep 27 '22 18:09

LiTTle