Skip to end of metadata
Go to start of metadata

Kotlin allows us to provide implementations for a predefined set of operators on our types. These operators have fixed symbolic representation (like '+' or '*') and fixed precedence. To implement an operator, one provides a member function or an extension function with a fixed name, for the corresponding type, i.e. left-hand side type for binary operations and argument type for unary ones.

Conventions

Here we describe the conventions that regulate operator overloading for different operators.

Unary operations
Expression Translated to
+a a.plus()
-a a.minus()
!a a.not()

This table says that when the compiler processes, for example, an expression +a, it performs the following steps:

  1. Determines the type of a, let it be T.
  2. Looks up a function plus() with no parameters for the receiver T, i.e. a member function or an extension function.
  3. If the function is absent or ambiguous, it is a compilation error.
  4. If the function is present and its return type is R, the expression +a has type R.

Note that these operations, as well as all the others, are optimized for Basic types and do not introduce overhead of function calls for them.

Expression Translated to
a++ a.inc() + see below
a-- a.dec() + see below

These operations are supposed to change their receiver and (optionally) return a value.

inc()/dec() shouldn't mutate the receiver object
By "changing the receiver" we mean the receiver-variable, not the receiver object.

The compiler performs the following steps for resolution of an operator in the postfix form, e.g. a++:

  1. Determines the type of a, let it be T.
  2. Looks up a function inc() with no parameters, applicable to the receiver of type T.
  3. If the function returns a type R, then it must be a subtype of T.

The effect of computing the expression is:

  1. Store the initial value of a to a temporary storage a0,
  2. Assign the result of a.inc() to a,
  3. Return a0 as a result of the expression.

For a-- the steps are completely analogous.

For the prefix forms ++a and --a resolution works the same way, and the effect is:

  1. Assign the result of a.inc() to a,
  2. Return the new value of a as a result of the expression.
Binary operations
Expression Translated to
a + b a.plus(b)
a - b a.minus(b)
a * b a.times(b)
a / b a.div(b)
a % b a.mod(b)
a..b a.rangeTo(b)

For the operations in this table, the compiler just resolves the expression in the Translated to column.

Expression Translated to
a in b b.contains(a)
a !in b !b.contains(a)


For in and !in the procedure is the same, but the order of arguments is reversed.

Symbol Translated to
a[i] a.get(i)
a[i, j] a.get(i, j)
a[i_1, ..., i_n] a.get(i_1, ..., i_n)
a[i] = b a.set(i, b)
a[i, j] = b a.set(i, j, b)
a[i_1, ..., i_n] = b a.set(i_1, ..., i_n, b)

Square brackets are translated to calls to get and set with appropriate numbers of arguments.

Symbol Translated to
a(i) a.invoke(i)
a(i, j) a.invoke(i, j)
a(i_1, ..., i_n) a.invoke(i_1, ..., i_n)

Parentheses are translated to calls to invoke with appropriate number of arguments.

Expression Translated to
a += b a.plusAssign(b)
a -= b a.minusAssign(b)
a *= b a.timesAssign(b)
a /= b a.divAssign(b)
a %= b a.modAssign(b)

For the assignment operations, e.g. a += b, the compiler performs the following steps:

  1. If the function from the right column is available
    1. If the corresponding binary function (i.e. plus() for plusAssign()) is available too, report error (ambiguity).
    2. Make sure its return type is Unit, and report an error otherwise.
    3. Generate code for a.plusAssign(b)
  2. Otherwise, try to generate code for a = a + b (this includes a type check: the type of a + b must be a subtype of a).

Note: assignments are NOT expressions in Kotlin.

Expression Translated to
a == b a?.equals(b) ?: b.identityEquals(null)
a != b !(a?.equals(b) ?: b.identityEquals(null))

Note: identityEquals checks if two references point to the same object.

The == operation is special in two ways:

  1. It is translated to a complex expression that screens for null's, and null == null is true.
  2. It looks up a function with a specific signature, not just a specific name. The function must be declared as

    Or an extension function with the same parameter list and return type.

Symbol Translated to
a > b a.compareTo(b) > 0
a < b a.compareTo(b) < 0
a >= b a.compareTo(b) >= 0
a <= b a.compareTo(b) <= 0

All comparisons are translated into calls to compareTo, that is required to return Int.

Infix calls for named functions

One can simulate custom infix operations by using infix function calls.

What's next

Labels:
None
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. Jul 20, 2011

    Anonymous

    It would be nice if I could declare equals() as strongly typed for my class - i.e. "equals(other: MyClass): Boolean" rather than "equals(other: Any?): Boolean" - and let the implementation == insert type and null checks as needed.

    1. Jul 21, 2011

      There're two problems:
      1) It's Java-incompatible
      2) What if you have a dozen functions like equals(A), equals(B), ... defined for your type? What should the compiler do: call each of them? One of them? In what order?

      1. Jul 21, 2011

        Anonymous

        Well, you solve that problem somehow when rewriting "a+b" as "a.plus(b)", so I'd assume the same to be true for "==" / "equals" - overload resolution failure? But I guess "==" is special in that, in your type system, it can be applied to any type at all.

        Then, perhaps, provide some syntactic sugar that would let write the body of "equals" with no need for extra checks (so as to adhere to DRY), but expand it to "equals(Any?)" in compiled version? This could be made even shorter then, since the compiler can infer the type of argument (as same class) and type of result (as Boolean); for example.:

        (though then the obvious question is, "why can't I write fun+() etc?")

        Either way, I think that defining equality should be no more verbose than defining any other binary operator. If I don't need any type/null checks to implement "a+b", I shouldn't need any for "a==b", either.

        1. Jul 21, 2011

          You'll have your custom compiler extension generate equals for you. Like this:

          expand
           fun equals(other : X) = this.foo == other.foo

          And it creates a proper equals for you... expand is just an annotation here.

          1. Jul 21, 2011

            Anonymous

            Cool, but can we have it in the stock library? It just seems such a common thing that repeating it over and over again for every project makes little sense - it would be .NET's Enumerable.Zip all over again :)

            1. Jul 21, 2011

              We'll see

      2. Aug 23, 2011

        Strongly typed equals looks nice. In order to provide compatibility with java,
        the compiler can generate a wrapper that accepts object, checks the class
        and calls the strongly typed equals.

        In case of several equals with different types of the argument - what they are for? may be to restrict them?

        1. Aug 23, 2011

          See above about the "expand" annotation

    2. Nov 29, 2011

      I've also recognized the problematic. I also develop as strongly typed as possible. But it's important to implement the comparison logic in the method

      fun equals(other : Any?) : Boolean

      ... so that derived classes only have to override one method.

      A good solution would be, if the compiler would show a warning (or denies the compilation), if one compares incompatible types.

      val a1 : Apple = pickAnApple() 
      val a2 : Apple = pickAnApple() 
      val del : Apple = GoldenDelicious() 
      val rus1 : Apple = Russet() 
      val rus2 : Apple = Russet() 
      val p : Pear = AnyPear() 
       
      val x1 = a1 
      ==
       p // Makes no sense. The compiler should show a warning. 
       
      val x2 = a1 == a2 // OK 
       
      val x3 = a1 == del // OK 
       
      val x4 = del 
      ==
       rus1 // Makes no sense. The compiler should show a warning. 
       
      val x5 = rus1 == rus2 // OK

      Very important is, that an explicit comparison with NULL will allways do a reference comparison and not call the equals-method.

      val y1 = a == b // --> a?.equals(b) ?: a.identityEquals(b) 
       
      val y2 = a == null // --> a.identityEquals(null) 
       
      val y3 = a != null // --> !a.identityEquals(null)
      1. Nov 28, 2011

        We do both

  2. Jul 24, 2011

    Anonymous

    I personally like operator overloading since it is a very convenient feature. But a problem I often see with other languages is, that operator overloading can be abused. I think the language should restrict in this area and force the programmer to write semantically correct operators.

    A prominent example of this is in my opinion the plus-operator (+), which is a numeric operator. Hower, in almost all languages the +-operator is used for string concatenation. In my opinion, this is semantically incorrect since you do concatenate two strings and not (mathematically) add them together.

    I think Digital Mars with their programming language D is doing a good job in this regard as they don't just use the +-operator out of habit. Instead they defined an additional ~-operator for this purpose (SQL standard, not Microsoft SQL, also uses something different for concatenation):

    I don't know how Kotlin handles string concatenation, because I haven't found any examples (or did miss them). But I think this is something that you might want to consider.

    Another Idea from me was to limit the operator overloading to classes which implement a specific interface, e.g. NumericOperations (+ - * / %), BinaryOperations (& | ^ << >>) , ConcatenateOperations (~) and so forth.

    Something like:


    Well, that are my Ideas. I don't know how realizable they are as they aren't 100% thought-out. I also don't have much knowledge in the field of creating compilers and languages. However, it might be a good topic for discussion and perhaps even make Kotlin a better language. If not, I just wasted my time writing this text :-)

    1. Jul 25, 2011

      You shouldn't consider your time wasted, that's for sure!
      It's rather hard to ensure overloaded operators are used for math and satisfy Field axioms, you know. Also, people are quite used to concatenate string with plus sign and one needs to argue with something more than purity reasons to change this.
      I personally like idea of ~ for concatenation BTW

      1. Jul 29, 2011

        Anonymous

        I'd make an argument against "+" for a more practical reason: As an arithmetic operator, its precedence is too low. Also, it's very commonly used in non-string-concatenation contexts as well. So that leads, in Java, to potential bugs like this:

        int i=10;

        int bar();

        String s = "foo" + i*2+1 + "bar";

        The author presumably meant this to be parsed as:

        String s = "foo" + (i*2+1) + "bar";

        with the value "foo21bar", but it actually gets parsed as:

        String s = (("foo" + i*2)1)"bar";

        with the value "foo201bar".

        I've actually seen bugs like this happen in Java code.

        If a binary operator with lower precedence than "+" was used for string concatenation, the problem would have been avoided.

        I know that some languages use "&" for string concatenation instead of "+"...

        1. Jul 29, 2011

          You will not be able to add an integer to a string in Kotlin anyway. You can only concatenate strings or add numerics with it. Your sample will be written as follows:

          1. Jul 30, 2011

            Anonymous

            In that case, why do you need a string concatenation operator at all? Shouldn't the user just do this?

            val s1 = "foo"

            val s2 = "bar"

            val s3 = "$

            Unknown macro: {foo}

            $

            Unknown macro: {bar}

            "

            1. Jul 30, 2011

              Anonymous

              Oops. Wiki screwed up my formatting. That should have been:

              val s1 = "foo"

              val s2 = "bar"

              val s3 = "${foo}${bar}"

              1. Aug 01, 2011

                You're quite right. It's just a matter of taste anyway, not the error prone construct java's string concatenation with whatever used to be

  3. Jul 25, 2011

    Anonymous

    How is associativity handled? If I write

    class Vector(...) { fun times(s: Double){...}}

    then v*s is straightforward v.times(s) but what about s*v?

    Or is that handled w/ extension methods?

    fun Int.times(v: Vector) = v.times(this)

    1. Jul 26, 2011

      You can do an extension method. Otherwise it does not work.

  4. Jul 25, 2011

    Anonymous

    Why did you decide to do plus and plusassign as member functions?

    And not as in .Net as static functions?

    Are there any (dis)advantages with either way to implement?

    Just curous

    1. Jul 25, 2011

      It is basically the same thing with help of Extension functions

  5. Jul 28, 2011

    Anonymous

    This is a lot of method names to remember to not use for other purposes. A lot of them are common names, too.

    It would be nice if an annotation or some kind of restricted method name namespace was required for operator overloading so that people wouldn't end up using one of these names by mistake. For instance, having all of the names preceded by an underscore or other special character, or using names like "opPlus", "opMinus", etc. which users would be unlikely to use (or want to use) for other purposes.

    1. Jul 28, 2011

      Anonymous

      Python has the same problem (remembering the names of underlying operator functions)

      Personally, I would prefer a straight syntactic definition along the lines of C++ or C# (ie. public operator+(...))

      Whether it gets compiled down to a method named "plus" or "opPlus" or something that cannot be named in Java (eg. "<plus>") is another matter.
      (nameable would be good if you wan't to call from Java-side, un-nameable is good if you wish to avoid name-conflicts)

  6. Aug 22, 2011

    Anonymous

    I think that "==" and "!=" as well all comparison operators should be implemented by different methods.

    It will dramatically simplify various DSL implementations. By the way Groovy is going to do in in it's 2.0 upcoming

    release.

    1. Sep 29, 2011

      Anonymous

      Indeed, there are not too many symbols available for DSL:s here.  Maybe add a few unused ones, like ~, or iconographic combinations like arrow ->  (the range operator already looks like it will be very useful).

      I can see why the semantics of the comparison operators would be fixed though, it allows refactorings like !(a < b)  to  a >= b  to be done automatically, as well as manually without having to look into the implementation of the objects operated on.

      1. Sep 29, 2011

        I am not sure if it is a good idea to have things with "no fixed semantics whatsoever". I understand the concern, but I thin in the current version we will refrain from adding these. Will see what demand there actually is.

  7. Oct 07, 2011

    Anonymous

    Hello. Great you decided to include operator overloading in Kotlin.

    What do you think about the idea of making an explicit annotation for compiler?

    Like:

    Without this annotation compiler would not compile implicit calls to the method and possibly warn programmer about using this name.

    Thinking anout the pros and cons of this idea, I decided it just would be much more clearer and easier to understand for new users of the language. Though it may be excessive.

    Just an idea I decided to share.

    1. Oct 07, 2011

      This would require some magic to make Java classes work as we like. E.g., for Java's List to have proper element access with square brackets.

  8. Apr 24, 2012

    there is some missunderstandig caused by .inc() description.

    fun main(args : Array<String>) {
      
      var x : I = I();
      var y : Int = 0;
      
      for (i in 1..10) {
        println("$x ${x++} $x $y ${y++} $y")
      }
    }
    
    class I {
      var i = 0;
         get() = $i;
         set(v) = $i = v;
      
      fun inc() : I {
        i++;
        return this;
      }
      
      fun toString() : String {
        return i.toString();
      }
    }
    
    

    0 1 1 0 0 1
    1 2 2 1 1 2
    2 3 3 2 2 3
    3 4 4 3 3 4
    4 5 5 4 4 5
    5 6 6 5 5 6
    6 7 7 6 6 7
    7 8 8 7 7 8
    8 9 9 8 8 9
    9 10 10 9 9 10
    that means that y++ behaves like ++y.
    it might be some mistake, but can't get where it is.

    1. Apr 24, 2012

      fun main(args : Array<String>) { 
          var x : I = I(); 
          var y : Int = 0; 
       
         for (i in 1..10) { 
             println("$x ${x++} $x $y ${y++} $y") 
         } 
      } 
       
      class I(val i : Int = 0) { 
       
          fun inc() : I { 
              return I(i + 1); 
          } 
       
          fun toString() : String { 
              return i.toString() 
          } 
      }

      This code yield the following result:

      0 0 1 0 0 1
      1 1 2 1 1 2
      2 2 3 2 2 3
      3 3 4 3 3 4
      4 4 5 4 4 5
      5 5 6 5 5 6
      6 6 7 6 6 7
      7 7 8 7 7 8
      8 8 9 8 8 9
      9 9 10 9 9 10

      Problem is that generated code saves the reference to x not the value, so when you call toString() in your example the value has already changed.

      If inc() returns another instance of I then x++ and ++x semantics work as expected.

    2. Apr 25, 2012

      We should update the documentation to make it clear that inc() is not supposed to mutate the object it is called on.
      Thanks

      1. Apr 25, 2012

        while writing that i knew what i was doing.

        That is not about documentation, this is about behavior.

        when i write x++ inside expression i mean that first i want expression to be counted, and then increment logic to run. ++x respectively first run increment logic and then count the expression.

        i do not want to make another instance of my object running unary operation at all. Here is the problem caused by new instance: 

        fun main(args : Array<String>) {
          var c1 = Counter(0);
          var c2 = Counter(0);
        
          affectCounter(c1++);
          println("${c1.value} ${c1.stateAffected}");
        
          c2++;
          affectCounter(c2);
          println("${c2.value} ${c2.stateAffected}");
        
        }
        
        fun affectCounter(c : Counter) {
          c.affect();
        }
        
        class Counter(initialValue : Int) {
          var value = initialValue
          get() = $value;
          var stateAffected = false;
          get() = $stateAffected;
        
          fun inc() : Counter {
            return Counter(value+1);
          }
        
          fun affect() {
            stateAffected = true;
          }
        }
        
        1. Apr 25, 2012

          As far as I can see, the behavior you are requesting is "copy the object behind the scenes". The platform we are working on does not allow us to do this... Any suggestions?

          1. Apr 27, 2012

            that is the real challenge, actually. My first idea was to move increment itself next to expression. But there are two problems. First problem is that what if user decide to use increment like this: f(x+,x+) . That is absolutely crazy code, but this situation cant be solved while playing with tree (this function should work with non-incremented and incremented x same time). the second idea was to create a fork proxy object that will represent x's state before increment, but this way is even more difficult.

            Finally, i think that increment for an object is the real problem. Because. Ok. I see and i do not mutate an object, but return a new instance. But what if x.inc() just delegates increment-like functionality of x.y property, but mutates y. So i need to clone y too, but it is implemented by another guy, so i can't affect it.

            Оf course, i can always clone an object automatically, but i will have to clone it's members to, so i have a real chance to pull a hudge tail of dozens of objects. So copy is not an option to.

            Also i am sure, that if i will dig deeper tp .plus(x) function, i will find lots of troubles to, for example it is surely easy to brake transitive relation so that will lead to problems and fancy bugs.

            1. May 02, 2012

              Yes. And it is not really specific to Kotlin: Java has the same problem, the only difference is syntax: in Kotlin you can say a + b, in Java it would be a.plus(b), but all the problems remain.

  9. Apr 27, 2012

    and another problem

    fun main(args : Array<String>) {
      var c = C(1);
      var l = Looker(c);
      c++;
      println("${l.c} ${c}");
    }
    
    
    class Looker(c : C) {
      var c = c;
      get() = $c;
    }
    
    class C(i : Int = 0) {
      var i = i;
      fun inc() : C = C(i+1);
      fun toString() = i.toString();
    }
    

    shows, that .inc() is dangerously unsafe. If i use it somewhere, i need to keep in mind that everything pointing to this object will become invalid.

  10. Jun 08, 2012

    For Collection.add operation can I use myList[] = "a" ?  

    Thank you, really loving this language - Matt  

    1. Jun 09, 2012

      We do not allow this. I think

      Is good enough, isn't it?

      1. Jun 09, 2012

        I do like infix operators.  The difference I see is precedence of equals is lowest.  

        Also "[]" is a Collection operator (get/set) and instead of misusing "+=" for adding items.

        1. Jun 13, 2012

          The "[] =" notation looks too cryptic to me

          1. Jun 13, 2012

            a[1];   // get

            a[1] = "value1"; // set

            a[] = "value2"; // append

            is very clear to me (this syntax is also used by PHP).

          2. Jun 03, 2013

            matrix[i][]; //return row

            matrix[][i]; //returns column

  11. Aug 29, 2012

    Why assignments are not expressions?

    So I need to write:

    Instead of simple:

    Is it only because named arguments?

    1. Aug 29, 2012

      This should not be hand-coded every time you need to read from a stream.
      There must be a dedicated library routine for this, something like

      inputStream.readBuffered(buffer) { bytesRead -> 
        to?.write(buffer, 0, bytesRead) 
      }
      1. Aug 30, 2012

        Yeah, nice variant, thank you :)
        Still not accustomed to use function literals so actively

  12. Sep 15, 2012

    One thought that might be interesting, although I immediately admit it might seriously degrade readability, is an implicit cast operator.

    So for instance a method like:

    would make casting from MyObject to String possible so we could write:

    Now with extension methods we could do some really interesting things like:

    But again, this might negatively affect readability.

    1. Sep 15, 2012

      One huge problem with this is that you can not have several methods that differ only in their reTurn types

      1. Sep 16, 2012

        That could be overcome by translating the methods:

        from to
        cast() : Date cast_java_util_Date() : Date

        In a way is just like translating from a + sign to a plus function or those new component[n] convention functions.

        But I admit this doesn't make Kotlin necessarily easier to use or understand (let alone to implement).

        1. Sep 16, 2012

          This is a common misconception: methods return types (e.g. Map<String, List<Foo>>), not classes, and types can not be identified by qualified names.

          1. Sep 16, 2012

            True, hadn't thought of that.
            Don't do the above

  13. Dec 28, 2012

    Apparently, function called 'invoke' can be used just like 'get', but with round brackets instead of square brackets. Please add it to the documentation here.

    fun main(args : Array<String>) { 
      val t = Test() 
      
      println("t(1) = ${ t(1) }") // prints t(1) = 2 
    } 
     
    class Test() { 
      fun invoke(v:Int) = v + 1 
    }
    1. Dec 28, 2012

      Done, thanks for the report

  14. Jan 13, 2013

    Operator overloading + Extentsion functions = Magic:

    fun Unit.not() { 
        println("Fun. Unit. Not!"); 
    } 
     
    !println("We've got a message from outer space:")

    More fun:

    fun Unit.rangeTo(same: Unit) {} 
     
    println(1)..println(2)..println(3)..println(4);

    A bit more alchemy:

    fun Unit.get(message: String) { 
        print(message+" "); 
    } 
     
    print("Magic: ")["Alakazam"]["Abracadabra"]["Voila!"];

    I would like to go further and add invoke... But here Kotlin starts acting up, saying he expects function type, not Unit.

    But anyway we can extend Unit's functionality, creating almost... a real unit, in Pascal meaning of the word.

    Ok, then no more magic, just brute force: the nastiest Hello World ever:

    while (println("Hello, ") == println("world")) {}
    1. Jan 14, 2013

      Inability to define invoke() for Unit seems to be bug. Please report it to the tracker.

  15. Sep 26, 2013

    apparently, overloading for shift left "<<" and right ">>" is not supported yet.  any plan to support them in the future?

    1. Sep 26, 2013

      We are not planning to support "<<" and ">>". Shift operations are available as named functions:

      1 shl 5 // = 32
  16. Jul 29, 2014

    What about bit operators &, |, <<, <<<, >>, >>>

    1. about 18 hours ago

      They are there in the form of funcitons: and, or, shl, shr, ushr