Kotlin 1.0-beta-4
I have an AutoCloseable.use
extension method mimicking stdlib’s Closeable.use
extension method. To my big surprise, I just discovered that my AutoClosable.use
method is used even for objects with static type Closeable
. Why isn’t the more specific method from stdlib used in that case?
Next surprise: When I declare both overloads in my own code, overload resolution fails with an ambiguity:
inline fun <T : Closeable, R> T.myUse(action: (T) -> R): R = TODO()
inline fun <T : AutoCloseable, R> T.myUse(action: (T) -> R): R = TODO()
fun testMyUse() {
val c: Closeable = RandomAccessFile("foo", "r")
// "Cannot choose among the following candidates without completing type inference: ..."
val x: Int = c.myUse { 42 }
// "Overload resolution ambiguity. All these functions match: ..."
val y: Int = c.myUse<Closeable, Int> { 42 }
Something seems to go wrong here, and it seems to be related to the presence of a second type argument (R
). Other than that, overload resolution behaves as I would expect (I’ve pasted my full set of experiments below).
import java.io.Closeable
import java.io.RandomAccessFile
fun testStdlibUse() {
val c: Closeable = RandomAccessFile("foo", "r")
c.use {} // chooses AutoCloseable.use (unexpected)
val ac: AutoCloseable = RandomAccessFile("foo", "r")
ac.use {} // chooses AutoCloseable.use (expected)
}
fun testMyUse() {
val c: Closeable = RandomAccessFile("foo", "r")
// "Cannot choose among the following candidates without completing type inference: ..." (unexpected)
c.myUse {}
// "Cannot choose among the following candidates without completing type inference: ..." (unexpected)
val x: Int = c.myUse { 42 }
// "Overload resolution ambiguity. All these functions match: ..." (unexpected)
val y: Int = c.myUse<Closeable, Int> { 42 }
val ac: AutoCloseable = RandomAccessFile("foo", "r")
ac.myUse {} // chooses AutoCloseable overload (expected)
}
fun testGenericExtension() {
val c: Closeable = RandomAccessFile("foo", "r")
c.foo() // chooses Closeable overload (expected)
val ac: AutoCloseable = RandomAccessFile("foo", "r")
ac.foo() // chooses AutoCloseable overload (expected)
}
fun testNonGenericExtension() {
val c: Closeable = RandomAccessFile("foo", "r")
c.bar() // chooses Closeable overload (expected)
val ac: AutoCloseable = RandomAccessFile("foo", "r")
ac.bar() // chooses AutoCloseable overload (expected)
}
fun testGenericNonExtension() {
val c: Closeable = RandomAccessFile("foo", "r")
baz(c) // chooses Closeable overload (expected)
val ac: AutoCloseable = RandomAccessFile("foo", "r")
baz(ac) // chooses AutoCloseable overload (expected)
}
fun testNonGenericNonExtension() {
val c: Closeable = RandomAccessFile("foo", "r")
bazz(c) // chooses Closeable overload (expected)
val ac: AutoCloseable = RandomAccessFile("foo", "r")
bazz(ac) // chooses AutoCloseable overload (expected)
}
// defined in stdlib:
// public inline fun <T : Closeable, R> T.use(block: (T) -> R): R
inline fun <T : AutoCloseable, R> T.use(action: (T) -> R): R = TODO()
inline fun <T : Closeable, R> T.myUse(action: (T) -> R): R = TODO()
inline fun <T : AutoCloseable, R> T.myUse(action: (T) -> R): R = TODO()
fun <T : Closeable> T.foo() {}
fun <T : AutoCloseable> T.foo() {}
fun Closeable.bar() {}
fun AutoCloseable.bar() {}
fun <T: Closeable> baz(c: T) {}
fun <T: AutoCloseable> baz(c: T) {}
fun bazz(c: Closeable) {}
fun bazz(ac: AutoCloseable) {}