5

I'm a little confused why this doesn't work. I'm having a simple Iterable of String that I want to sort via toSortedSet() my own way. I wanted to pass a lambda to it like this:

myStringIterable.toSortedSet({a,b -> a.compareTo(b)}) 

That, however, doesn't seem to work. The error says

Type mismatch. Required kotlin.Comparator < String>

Found: (String,String) -> Int

A Comparator is a Functional Interface, so I should be able to pass it as a Lambda, shouldn't I?

Community
  • 1
  • 1
Jan B.
  • 6,030
  • 5
  • 32
  • 53

4 Answers4

6

You can use compareBy to wrap code into Comparators:

toSortedSet(compareBy { it.length })

I think in your case, no argument is necessary for toSortedSet though.

s1m0nw1
  • 76,759
  • 17
  • 167
  • 196
  • 1
    While this answer is correct, it might not completely answer what the OP is really asking, i.e. *why* a simple lambda doesn't work. For simple field selection, `compareBy` works, but what if the OP wanted a complex calculation for the comparison? – DodgyCodeException Aug 23 '18 at 08:11
  • Thanks for your solution. As DodgyCodeException pointed out, though, my actual case is a little more complex, so I just reduced it to a example with `String`s since the issue why it wouldn't accept lambdas is the same. – Jan B. Aug 23 '18 at 08:25
4

As of Kotlin 1.2, SAM conversion is only supported for Java interfaces. kotlin.Comparator is an interface defined in Kotlin, and for such interfaces there is no support for converting lambdas to implementations of those interfaces.

yole
  • 92,896
  • 20
  • 260
  • 197
  • Isn't it a good counterexample to "since Kotlin has proper function types, automatic conversion of functions into implementations of Kotlin interfaces is unnecessary"? Should all standard library methods taking `Comparator` get overloads (or extension function "overloads") taking functions? – Alexey Romanov Aug 23 '18 at 19:53
  • @AlexeyRomanov it's very likely that we will add support for SAM conversion of Kotlin interfaces in a future language version, so I don't think it makes sense to add these overloads now. and there are enough comparator-making facilities in the standard library to cover the current needs – yole Aug 24 '18 at 07:22
  • That's very nice to hear! – Alexey Romanov Aug 24 '18 at 09:07
2

yole already provided the why, and here is the how:

setOf("a", "b", "c").toSortedSet(object: Comparator<String>{
    override fun compare(s1: String, s2: String): Int {
        return s1.compareTo(s2) // replace with own logic
    }
}) 

Or like this:

val comp = object: Comparator<String> {
    override fun compare(s1: String, s2: String): Int {
        return s2.compareTo(s1) // replace with own logic
    }
}

sortedSetOf(comp, "a", "b", "c")
Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
0

Not sure if this is new, but you can do:

val set = listOf("a", "c", "b").toSortedSet(Comparator { s1, s2 ->
    s1.compareTo(s2) // replace with own logic
})
Xi Wei
  • 1,669
  • 1
  • 21
  • 33