是否可以编写“双重”扩展方法?

时间:2018-09-05 19:51:33

标签: kotlin

在Kotlin中可以写

class A {
  fun B.foo()
}

,然后例如写with (myA) { myB.foo() }

是否可以将其作为扩展方法写在A上?我的用例是写作

with (java.math.RoundingMode.CEILING) { 1 / 2 }

我想返回1,关键是我想将operator fun Int.div(Int)添加到RoundingMode

3 个答案:

答案 0 :(得分:2)

不,这不可能。 operator div必须具有Int作为接收者。

您也不能添加RoundingMode作为接收者,因为只能有一个功能接收者。

不过,您可以使用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
}

答案 1 :(得分:1)

这是不可能的,Int已经具有div函数,因此,如果您决定编写扩展函数div,则将无法应用它,因为member功能胜过扩展功能。

您可以这样写:

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

答案 2 :(得分:0)

由于以下几个原因,这是不可能的:

  1. Kotlin中没有“双重扩展功能”的概念
  2. 您无法使用扩展功能覆盖方法,并且div中已经定义了运算符Int

但是,您可以通过以下方法解决这些问题

  1. 上下文类和扩展lambda(例如block: ContextClass.() -> Unit
  2. 中缀函数(例如,使用15 div 4代替15 / 4

请参见以下示例:

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
}

希望有帮助!