Java并不能在真正意义上被称作一门“纯面向对象”语言,因为它的基本类型与函数并不能看作对象。
在Kotlin类型系统中,并不区分基本数据类型与包装类型。
Kotlin类型结构图(选取String、Int、自定义类型)
Any;非空类型的根类型
与Object作为Java类层级结构的顶层类似,Any类型是 Kotlin 中所有非空类型的超类。
对于Kotlin 来说,如果定义了一个没有指定父类型的类型,则该类型将是Any的直接子类型。如果定义了父类型,那么该父类型是该类的直接父类型,但是新类型的最终根类型为Any。
Kotlin的Type Checker强制检查了父子关系。例如,你可以将子类型值存储到父类型变量中。但是不能将父类型值存储到子类型中。
Kotlin把Java方法参数和返回类型中用到的Object类型看作Any(更准确的说是“平台类型”)。当在Kotlin函数中使用Any时,它会编译成Java字节码中的Object。
什么是平台类型?
平台类型本质上是Kotlin不知道可空性信息的类型,所有Java 引用类型在Kotlin中都表现为平台类型。当在Kotlin中处理平台类型的值的时候,它既可以被当作可空类型来处理,也可以被当作非空类型来操作。
Any?:所有类型的根类型
Any是所有非空类型的根类型,Any?才是所有类型(可空和非空类型)的根类型。
什么才是子类型化?
从Java来看:继承关系决定父子类型关系。
“继承”和“子类型化”是两个完全不同的概念。子类型化的核心是一种类型的替代关系,可表示为:
S<:T
S是T的子类,这意味着在需要T类型值的地方,S类型的值同样适用。如在Kotlin中Int是Number的子类:
fun printNum(num: Number) {
println(num)
}
fun main() {
val n: Int = 1
printNum(n)
}
1
作为比较,继承强调的是一种“实现上的复用”,而子类型化是一种类型语义的关系,与实现没有关系。
像Java,由于在声明父子类型关系的同时也声明来继承的关系。
所以在Kotlin中,虽然Any与Any?没有继承关系,然而在使用Any? 类型值的地方,显然可以传入一个类型为Any的值。反之,就不行。
Any? 与Any??
如果Any?是Any的父类型,那么Any?? 是否又是Any?的父类型,如果成立,是否意味着就没有所谓的所有类型的根类型了?
在Kotlin 中,可空类型可以看作是所谓的Union Type,近似于数学中的并集。如果用类型的并集来表示Any?可写为Any U Null。
相应的Any??就可以表示为Any U Null U Null,等价于Any U Null,即Any??等价于Any?。因此,说Any?是所有类型的根类型是没有问题的。
Nothing与Nothing?
abstract class Animal(val weight: Double)
class Bird(weight: Double, val flightSpeed: Double) : Animal(weight)
class Fish(weight: Double, val swimmingSpeed: Double) : Animal(weight)
在Kotlin类型层级结构的最底层是Nothing类型。加上Nothing类型之后,类型结构图如下:
Nothing是没有实例的类型。Nothing类型的表达式不会产生任何值。需要注意的是:任何返回值为Nothing的表达式之后的语句都是无法执行的。Kotlin中retrun、thorw等(流程控制中与跳转相关的表达式)返回值都为Nothing。
Nothing对应的Nothing?,可以从字面上解释为:可空的空。与Any、Any?类似,所以Nothing?是Nothing的父类型,所以Nothing处于Kotlin类型
层级结构的最低层。它只能包含一个值:null,本质上与null没有区别。所以可以使用null作为可空类型的值。
自动装箱与拆箱
Koltin中没有int、double、float、long等基本数据类型,取而代之的是引用类型包装类Int、Float、Double、Long。
除了代表数值的类型,还有布尔(Boolean)、字符(Char)、字符串(String)及数组(Arry)。
但是只能说Kotlin比Java更接近纯面向对象的设计。
因为:
Kotlin代码
val x1:Int = 10
val x2:Int? =12
转Java代码
public final class TestIntDemoKt {
private static final int x1 = 10;
@Nullable
private static final Integer x2 = 12;
public static final int getX1() {
return x1;
}
@Nullable
public static final Integer getX2() {
return x2;
}
}
字节码
BIPUSH 10
PUTSTATIC com/example/kotlindemo/anyclassdemo/TestIntDemoKt.x1 : I
BIPUSH 12
INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
观察上面代码可以发现,Koltin中的Int在JVM中实际以int存储(对应字节码类型是I)
所以:
Kotlin中Int类型等同于int。
Kotlin中Int?等同于Integer。
“新“的数组类型
Kotlin中数组的创建:
val funList0 = arrayOf<Int>()//长度为0的数组
val funList= arrayOf(1,2,3) //初始化长度为3的数组
Kotlin中Array并不是一个原生的数据结构,而是一种Array类,甚至可以将Kotlin中Array视作集合类的一部分。
由于Smart Casts,编译器能够隐式推断出 funList元素类型。
在Kotlin中,还有一些实用的类,IntArray、CharArray、ShortArray等,分别对应Java中的int[]、char[]、short[]等。
val intArray = intArrayOf(1,2)
IntArray等并不是Array的子类,所以两者创建的相同值的对象,并不是相同对象。
由于Kotlin中对原始类有特殊的优化(主要体现在避免自动装箱带来的开销),所以建议优先使用原始类型数组。
参考Kotlin核心编程