我最近一直在学习Kotlin,同时对协变类型有一些疑问。
示例代码在这里。
我有Option
和Option2
都具有类型参数T
和扩展名run
。
我可以理解run
中的前两个validation()
,因为它们表现得像Java。
但是为什么第三行会编译? Option<T>
在T
中是不变。我们无法将Option<C>
实例传递到预期Option<B>
的地方。
在为out
添加一个T
关键字之后,现在它们都可以编译了。为什么?
open class A
open class B : A()
open class C : B()
class Option<T>(val item: T)
fun <T> Option<T>.run(func: (Int) -> Option<T>): Option<T> = func(1)
class Option1<out T>(val item: T) //out keyword
fun <T> Option1<T>.run(func: (Int) -> Option1<T>): Option1<T> = func(1)
fun validation() {
val opt: Option<B> = Option(B())
opt.run { Option(A()) } //won't compile as expected
opt.run { Option(B()) } //return type is Option<B>
opt.run { Option(C()) } //return type is Option<B>; why could this compile?
val opt1: Option1<B> = Option1(B())
opt1.run { Option1(A()) } //return type is Option<A>; why could this compile?
opt1.run { Option1(B()) } //return type is Option<B>
opt1.run { Option1(C()) } //return type is Option<B>
}
答案 0 :(得分:4)
opt.run { Option(C()) } //return type is Option<B>; why could this compile?
在这里,您可以通过将调用分解为分别进行类型检查的两行来近似估算如下行为:
val func: (Int) -> Option<B> = { Option(C()) }
opt.run(func)
第一行是正确的,因为:
Option<B>
(精确地为B
,因为Option
是不变的),Option(item: T): Option<T>
构造函数调用需要接受B
,C()
,C : B
,C()
通过了B
支票,Option(C())
也可以键入为Option<B>
并通过检查,(Int) -> Option<B>
的支票。
健全性检查:如果您按如下所示替换第一行怎么办?
val func: (Int) -> Option<B> = { Option(C()) as Option<C> }
然后它将不被编译,因为lambda内的表达式现在被键入为Option<C>
,它不是Option<B>
的子类型。
opt1.run { Option1(A()) } //return type is Option<A>; why could this compile?
在此示例中,编译器为T
选择的类型不是B
,而是A
。由于类型参数T
的协方差,编译器可以这样做。
opt1
是Option1<B>
Option1<out T>
是T
的协变变量,它可以用T
的任何超类型替换B
,
之所以允许这样做,是因为对Z
,B : Z
这样的opt1
,Option1<out Z>
也可以归为out
,这要归功于Option1<Z>
修饰符,然后编译器可以根据接收者类型T
对调用进行类型检查。
B
的替代将是X
以及任何Option1<X>
的最不常见的超类型,以使lambda返回Option1<A>
,
B
,A
和B : A
的最不常见超类型,A
,最不常见的超类型是T := A
opt1.run { Option1(0) }
。
健全性检查:如果您按如下所示更改表达式怎么办?
Option1<Any>
它仍将成功编译,但推断的返回类型将为B
。根据上述情况,这是完全合理的,因为Int
和Any
的最不常见超类型是# unk to json & xml
else:
with open(filePath) as u:
fLine = u.readline() #This is only reading the first line.
uStr = u.read()
if '<' in fLine:
time = strftime('%Y%b%d %H%M', gmtime())
fName = fileName + ' ' + time + ".xml"
with open(fName, 'w') as x:
x.write(uStr)
elif '{' in fLine:
time = strftime('%Y%b%d %H%M', gmtime())
fName = fileName + ' ' + time + ".json"
with open(fName, 'w') as j:
j.write(uStr)
。
免责声明:这不是编译器内部工作的方式,但是使用这种推理方式,您可能经常会得到与编译器结果一致的结果。