使用一个对象来实现一个空List的错误
我试图重写列表接口作为学习功能编程Kotlin的练习,但我无法弄清楚为什么我得到一个错误,当我试图让一个对象作为空的列表,在Kotlin标准库中不会发生。 在我的代码中,见下面,我想使用NIL作为单例空列表,使用list()作为函数返回它。 但是,这会在类型为“必需列表”的函数上生成类型不匹配错误。找到NIL“
interface List<A> { val empty: Boolean val head: A val tail: List<A> fun cons(a: A): List<A> = Cons(a, this) } object NIL : List<Nothing> { override val empty: Boolean = true override val head: Nothing get() = throw IllegalStateException("head called on empty list") override val tail: List<Nothing> get() = throw IllegalStateException("tail called on empty list") } private class Cons<A>(override val head: A, override val tail: List<A>) : List<A> { override val empty: Boolean = false } fun <A> list(): List<A> = NIL // Type mismatch. Required: List<A>. Found: NIL fun <A> list(vararg a: A): List<A> { var n = list<A>() for (e in a.reversed()) { n = Cons(e, n) } return n }
在标准库中,这个错误不会发生,正如您可以在Collections.kt的EmptyList中看到的一样 。 我做错了什么,或者我错过了一些让后者成为可能的概念,而前者不是?
评估提出的解决方案
在所提出的三种解决方案中,唯一允许通过下面的测试代码的是使用匿名对象的 ,而其他的则抛出异常java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Void
。
assertEquals("a", list<String>().cons("a").head)
但是,如果将NIL
更改为NIL : List<Any?>
,则解决方案NIL as List<A>
工作没有任何问题。
您可以通过as
关键字将List<Nothing>
List<A>
转换为List<A>
,例如:
fun <A> list(): List<A> = NIL as List<A>;
如果你想压制编译器警告,你可以使用@Suppress
注解来注释这个函数,例如:
@Suppress("UNCHECKED_CAST") fun <A> list(): List<A> = NIL as List<A>;
或者如果你不喜欢这种方式,你可以用一个匿名的对象来代替,例如:
fun <A> list(): List<A> = object : List<A> { override val empty: Boolean = true override val head: A get() = throw IllegalStateException("head called on empty list") override val tail: List<A> get() = throw IllegalStateException("tail called on empty list") };
通用接口应该是<out A>
而不是<A>
。
另外,IntelliJ Idea在这种情况下使用泛型时会显示一个警告。