Is it possible to write a “double” extension method?
Is it possible to write a “double” extension method?
In Kotlin, it is possible to write
class A
fun B.foo()
and then e.g. write with (myA) myB.foo()
.
with (myA) myB.foo()
Is it possible to write this as an extension method on A
, instead? My use case is writing
A
with (java.math.RoundingMode.CEILING) 1 / 2
which I would want to return 1
, the point being that I want to add operator fun Int.div(Int)
to RoundingMode
.
1
operator fun Int.div(Int)
RoundingMode
3 Answers
3
No it's not possible. operator div
is required to have Int
as a receiver.
operator div
Int
You can't add also RoundingMode
as receiver, since there can only be single function receiver.
RoundingMode
What you can do, though, is use Pair<RoundingMode, Int>
as a receiver:
Pair<RoundingMode, Int>
operator fun Pair<RoundingMode, Int>.div(i: Int): BigDecimal =
BigDecimal.valueOf(second.toLong()).divide(BigDecimal.valueOf(i.toLong()), first)
with(RoundingMode.CEILING)
println((this to 1) / 2) // => 1
My confusion is that it looks like there is a way, in Kotlin, to have two receivers: the
class A fun B.foo()
example from above, where there are two this
es in the method foo
. But it doesn't look like there's a way for both of those parameters to be extensions to separate types.– Louis Wasserman
Sep 5 '18 at 21:32
class A fun B.foo()
this
foo
@LouisWasserman Scala has solution to this - implicits. But Kotlin doesn't.
– Oleksandr.Bezhan
Sep 6 '18 at 13:14
@LouisWasserman Doesn't my answer below (specially the "context class" part) apply to your problem? It's the way I saw JetBrains do some of the Kotlin magic you see with Coroutines lib and Ktor framework
– lucasls
Sep 10 '18 at 20:48
That's not possible, Int
already has a div
function, thus, if you decide to write an extension function div
, you won't be able to apply it, because member functions win over extension functions.
Int
div
div
You can write this though:
fun RoundingMode.div(x: Int, y: Int): Int
return if (this == RoundingMode.CEILING)
Math.ceil(x.toDouble() / y.toDouble()).toInt()
else
Math.floor(x.toDouble() / y.toDouble()).toInt()
fun main(args: Array<String>)
with(java.math.RoundingMode.CEILING)
println(div(1,2))
It's not possible for a couple of reasons:
div
Int
However you can workaround these issues with
block: ContextClass.() -> Unit
15 div 4
15 / 4
See the example below:
class RoundingContext(private val roundingMode: RoundingMode)
infix fun Int.div(b: Int): Int
val x = this.toBigDecimal()
val y = b.toBigDecimal()
val res = x.divide(y, roundingMode)
return res.toInt()
fun <T> using(roundingMode: RoundingMode, block: RoundingContext.() -> T): T
return with(RoundingContext(roundingMode))
block()
// Test
fun main(args: Array<String>)
using(RoundingMode.FLOOR)
println(5 div 2) // 2
val x = using(RoundingMode.CEILING)
10 div 3
println(x) // 4
Hope it helps!
Thanks for contributing an answer to Stack Overflow!
But avoid …
To learn more, see our tips on writing great answers.
Required, but never shown
Required, but never shown
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
Banged my head on that for half a hour, but this does seem to be the optimal solution.
– Alexey Soshin
Sep 5 '18 at 20:55