Я искал со-и противоречивость в библиотеках коллекций нескольких языков программирования и наткнулся на интерфейс Set Kotlin.
Он задокументирован как
interface Set<out E> : Collection<E>
это означает, что он ковариантный - только "производит" объекты E, следуя документации Kotlin , не потребляя их.
И Set<String>
становится подтипом Set<Any>
.
Тем не менее, у него есть эти два метода:
abstract fun contains(element: E): Boolean
abstract fun containsAll(elements: Collection<E>): Boolean
Поэтому, когда я создаю класс, реализующий Set<String>
, я должен реализовать (помимо других) contains(String)
.Но позже кто-то может использовать мой класс в качестве Set<Any>
и позвонить set.contains(5)
.
Я действительно попробовал это:
class StringSet : Set<String> {
override val size = 2
override fun contains(element: String): Boolean {
println("--- StringSet.contains($element)")
return element == "Hallo" || element == "World"
}
override fun containsAll(elements: Collection<String>) : Boolean =
elements.all({it -> contains(it)})
override fun isEmpty() = false
override fun iterator() = listOf("Hallo", "World").iterator()
}
fun main() {
val sset : Set<String> = StringSet()
println(sset.contains("Hallo"))
println(sset.contains("xxx"))
//// compiler error:
// println(set.contains(5))
val aset : Set<Any> = sset
println(aset.contains("Hallo"))
println(aset.contains("xxx"))
// this compiles (and returns false), but the method is not actually called
println(aset.contains(5))
}
( Запустить онлайн )
Таким образом, получается, что Set<String>
не является "реальным" подтипом Set<Any>
, поскольку set.contains(5)
работает со вторым, но не с первым.
На самом деле вызов метода содержит дажеработает во время выполнения - просто моя реализация никогда не будет вызвана, и он просто печатает false
.
Глядя на исходный код интерфейса, оказывается, что два метода фактически объявлены как
abstract fun contains(element: @UnsafeVariance E): Boolean
abstract fun containsAll(elements: Collection<@UnsafeVariance E>): Boolean
Что здесь происходит?Есть ли какая-то особая магия компилятора для Set?Почему это нигде не задокументировано?