一、类和对象初始
在Kotlin中类的关键字也class,和Java一样,但是构造函数却是完全不同。Kotlin中的构造函数有两种,一种是主构造函数(primary constructor),另一种是次级构造函数(secondary constructor)。而且构造函数的写法也有多种,我们依依来介绍一下:
第一种写法
class User constructor(name:String,age:Int,sex:Char){
val name:String
val age :Int
val sex :Char
init {
this.name = name
this.age = age
this.sex = sex
println("name:$name,age:$age,sex:$sex")
}
}
其实代码很好懂,我简单说一下几个需要说明的地方:
(1)第一行的constructor是构造函数的关键字,括号中是传入的参数。这个构造函数在第一行,所以它也是主构造函数
(2)init{}这个代码块主要就是做一些初始化操作。因为主构造初始化的地方,所以参数的初始化操作都是在这个代码块中。相当于Java构造函数中大括号的作用
第二种写法
class User(name:String,age:Int,sex:Char){
val name:String
val age :Int
val sex :Char
init {
this.name = name
this.age = age
this.sex = sex
println("name:$name,age:$age,sex:$sex")
}
}
第二种写法和第一种相比,少了constructor关键字。在constructor关键字没有注解和修饰符修饰的话,这个关键字就可以省略了
如果init{}中只有变量的赋值没有其他的代码的话,还可以这样写
class User(name:String,age:Int,sex:Char){
//为了区分参数,我把变量改了个名字
val mName:String = name
val mAge :Int = age
val mSex :Char = sex
}
当然,如果init{}中还有其他的方法,比如上面的打印函数,这样的写法就不行了
第三种写法
class User(val name:String,val age:Int, val sex:Char){}
这样的写法是不是让你眼前一亮呢,我们在构造器的参数括号中,直接声明了参数名称,这样将形参直接省略了。如果类中没有了其他的方法,是一个空{},我们连{}也可以省略
无参构造器
Kotlin中也存在无参构造器,和Java是一样。当你什么也没有写的时候,就会自动生成一个无参的构造器,这个没什么好说的。
次级构造函数
class User{
var mName:String="gzc"
var mAge:Int=26
var mSex:Char='男'
constructor(name:String){
mName = name
}
constructor(name:String,age:Int):this(name){
mAge = age;
}
constructor(name:String,age:Int,sex:Char):this(name,age){
mSex = sex;
}
}
次级构造函数也很好理解,上述代码中存在三个次级构造函数,并且逐级调用。当然逐级调用并不是必须的。上面这个User类有一个无参的主构造函数。如果是一个有参的主构造函数又会怎么样呢?如下代码
class User(name:String,age:Int){
var mName:String = "gzc"
var mAge:Int = 26
var mSex:Char = '男'
init {
mName = name
mAge = age
}
constructor(name:String,age:Int,sex:Char):this(name,age){
mSex = sex
}
}
上述代码中的次级构造函数调用了主构造函数,这个是必要的的,答案是:必要的,你可以试一试,如果次级函数不调用主构造函数,回报一个错误 Primary constructor call expected
二、空类型和智能类型转换
空类型
在Java中空指针异常是一个非常常见而且还让人头疼的问题,而Kotlin中在这个问题的处理上非常让人舒服,看下面的代码
fun main(args: Array<String>) {
println(getName())
}
fun getName():String{
return null
}
在这里我们可能看不出来,但是放在IDE中,问题就会非常明显了,如下图:
编译器会报错,报错的原因:String类型是一个非空的类型 所以不能返回null。但是在某些情况,我们需要返回值可以使null怎么办,我们做一下简单的修改:
fun main(args: Array<String>) {
println(getName())
}
fun getName():String?{
return null
}
你会发现,在getName方法后面String类型变成了String?,多了一个?,表示返回的字符串类型可以为空。你把上段代码放在编译器中就不会报错了。虽然这样不会报错了,但是你想用的时候,还是有问题,我直接贴出图片:
上述代码,我想输出getName方法返回值的长度,编译器会报错,提示:返回值可能是一个空字符串,直接输出的话会报错。那怎么办呢?如果在Java中,会写一个if-else来判断一下,但是在Kotlin中却不用,如下:
fun main(args: Array<String>) {
println(getName()?.length)
}
fun getName():String?{
return "gzc"
}
我们使用的时候在返回值后加一个?,编译器就不会报错了。如果不是空,正常返回字符串;如果是空,则返回null这个字符串
在有些情况下,当某个值为空时,我们需要进行return,Java中我们依然需要写一个if语句,但是Kotlin中确有简便的写法:
fun main(args: Array<String>) {
val name = getName()?:return
println("name:$name")
}
fun getName():String?{
return null
}
我们声明一个变量name,用来承接getName的返回值。在getName后面有一个?:,这个就是用来判断前面的值是否为空的,而后面就是要执行的操作,当然后面的操作不一定是return,主要看需求。
还有一种情况,如下图,我们已经明确给name赋值了,肯定不是空。但是在调用的时候,编译器依然给我们错误的提示,怎么办?看图下面的代码
fun main(args: Array<String>) {
val name:String? = "gzc"
println(name!!.length)
}
我们在name后加上了两个!!,这样编译器就不会报错了
类型转换
Kotlin中的类型转换和Java也有些不同,更加智能些。
我们创建两个Java文件,一个Parent类,一个Son类,Son继承于Parent,Son有一个自己的方法sonFun,里面输出一句话。代码比较简单,我就不贴出来了。这两个Java代码我们会在Kotlin中使用,不用担心兼容问题。Kotlin代码如下
fun main(args: Array<String>) {
val parent:Parent = Son()
if(parent is Son){
parent.sonFun()
}
}
我们创建了一个Parent对象,具体的构造方法我们使用的是Son的。之后判断了一下对象是否属于Son,然后调用了子类的sonFun方法。
发现和Java的不同点了吗?是的,parent这个对象没有强转就直接调用了子类的方法。这就是Kotlin智能的地方。这个只能的地方并不是在于is这个关键字,而是在于如果之前判断过了,那么if的代码块中就会以条件成立的情况执行。比如上面说的S字符串判空问题,如果我们加上了判空的条件,那么if中的代码就会认为字符串不会为空,编译器也不会包什么错误提示了。
在Kotlin如果想进行强制类型转换应该怎么做呢?如下代码
//第一种
fun main(args: Array<String>) {
val parent:Parent = Parent()
val son:Son = parent as Son
}
//第二种
fun main(args: Array<String>) {
val parent:Parent = Parent()
val son:Son? = parent as? Son
}
为了方便,我写了两种
第一种:as关键字就是强转的意思,但是这里肯定会报错。因为parent对象根本不是Son类型。
第二种:as?当然也是强转的意思,不同的是它的接收类型后面也需要有一个?,而且这里强转并不会报错。输出son时,它为null
三、包(package)
Kotlin中的包和Java中的包概念相似,但是也不完全相同。比如在Kotlin中有一个类,类中有一个包名,但是这个包名不一定就是这个类的路径。这是一个区别。还有一个区别就是,我们可以对导入的类取别名,这个地方和Python是一样的。如下
import main.kotlin.com.gzc.firstkotlin.cls.ClsA as MyCls
fun main(args: Array<String>) {
val cls: MyCls = MyCls()
}
可以看见我们在import这一行后面有一个as关键字,后面呢就是别名的名称。这样的情况可以适用于类名相同的时候
四、区间(Range)
关于区间,没有什么特别说明的,Java中没有,是Kotlin中特有的。但是也不是特别复杂的东西。举个例子:
fun main(args: Array<String>) {
val r1:IntRange = 0..100//表示0到100的整数,包含0和100
val r2:IntRange = 0 until 100//表示0到99的整数,包含0但不包含100
}
IntRange是区间的类型,还有一些相应的API,都是比较简单的,我这里就不一一列举了。
关于Kotlin的数据类型我这里就介绍完毕了,欢迎各位伙伴提问和纠错!