Kotlin基础学习
Posted wushenjiang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin基础学习相关的知识,希望对你有一定的参考价值。
写在前面
在刚开学的时候,买了一本《第一行代码android》,但一直在上课没有机会看,这几天刚好写完了上一个项目,这段时间就对这本书进行了学习。在这本书中,由于谷歌大力推广kotlin语言,将其作为android开发的首推语言,本书也遵循了谷歌的推广,因此我就跟着书本学习了基本的Kotlin语法,当作记录笔记了。
变量和函数
变量
在Koltin中的变量定义方式与java有很大不同,在kotlin中要定义一个变量,只允许在变量前声明两种关键字:val和var,val是value的简写,表示一个不可变的变量,对应java中的final关键字定义的变量。var是variable的简写,表示一个可变的变量,对应java中的非final关键字定义的变量。这时可能学过java的都会冒出一个疑惑,只靠这两个怎么能知道具体的类型呢?Kotlin里有类型推导机制,如:
val a = 37
println("a = " + a)
这里可以看出,Kotlin不需要行尾分号了,这里我们将10赋值给了a,那么a就会被推导成整型变量。如果你把一个字符串赋值给a变量,那么a就会被自动推导为字符串变量。但需要注意的是,类型推导机制并不总是好用的,因此我们可以对一个变量进行显式声明,如:
val a:Int = 10
Kotlin里摒弃了java中的int,double,float等,全部都改用了对象数据类型,即首字母大写的类型,如Int,Double,Float。此时我们尝试对a变量进行一个扩大的操作:
a = a * 10
可以看到编译器报错了,因为val类型的变量无法被重新赋值,我们把a的类型变成var就可以了:
var a : Int = 10
函数
首先要注意的是,方法和函数其实说的都是一个东西,只是叫法不同而已。接下来我们看看如何在kotlin中定义一个函数:
fun largerNumber(num1:Int,num2:Int):Int{
return max(num1,num2)
}
fun表示function,即声明函数,后面就是函数名了。函数名后的括号里为变量参数列表,参数的声明格式为:"参数名:参数类型"。参数后面的冒号内容可以省略,表示返回值类型,这里我们声明为返回一个Int型的数据。这里使用了一个max函数,表示取二者的最大值。
这里我们提一句语法糖,当返回值只有一句代码时候,我们可以将大括号省略,用等号连接,且return也可以省略。如下:
fun largerNumber(num1:Int,num2:Int):Int = max(num1,num2)
而我们刚才提到了kotlin的类型推导机制,返回值肯定是一个Int值,因此我们不需要显式声明返回值类型了。就可以写成:
fun largerNumber(num1:Int,num2:Int) = max(num1,num2)
逻辑控制
if条件语句
Kotlin的if和java几乎没有任何区别,但需要注意的是,kotlin中的if条件语句是有返回值的,返回值为if语句每个条件中的最后一行代码的返回值。意味着我们可以这么写:
fun largerNumber(num1: Int, num2: Int) = if (num1 > num2) num1 else num2
这里又使用了上面提到的语法糖,因为这里放到大括号里也就是return 一句话,所以符合语法糖的规则。
when条件语句
kotlin中的when有点像java中的switch,但比起switch要强大的多。switch里面只能传入整型和短于整型的变量作为条件,JDK1.7后还引入了字符串变量的支持。但如果我们传入的类型不是这些,那么switch语句就无法使用了。同时,switch语句的最后需要加一个break,否则会顺序执行每个case。而kotlin不仅解决了以上的痛点,还添加了很多好用的功能。
比如我们先看一个简单的通过姓名输出学生分数的功能:
fun getScore(name:String) = if(name =="Tom"){
86
}else if(name == "Jim"){
77
}else if(name == "Jack"){
95
}else if(name == "Lily"){
100
}else{
0
}
这里又再次使用了单行代码函数的语法糖,但写了这么多if和else显得代码十分冗余,这里我们改用when的写法:
fun getScore(name: String) = when (name) {
"Tom" -> 86
"Jim" -> 77
"Jack" -> 95
"Lily" -> 100
else -> 0
}
when语句可以传入一个任意类型的变量,然后在when的结构体定义一系列的条件,格式为: 匹配值 -> {执行逻辑},当执行逻辑只有一行的时候,大括号就可以省略了。
除了精确匹配外,when还支持类型匹配,比如我们定义一个checkNumber()函数:
// when类型匹配
fun checkNumber(num: Number) {
when (num) {
is Int -> println("number is Int")
is Double -> println("number is Double")
else -> println("number not support")
}
}
在上面的代码中,is相当于java中的instance of,而Number是kotlin中的一个抽象类,像Int,Float,Double等与数字有关的类都是他的子类,我们可以通过类型匹配来判断是什么类型。比如:
val num = 10.0
checkNumber(num)
就会输出Double类型了。
需要注意的是,kotlin中判断字符串或者对象是否相等用 == 就可以了,不再需要调用equals方法。
循环语句
kotlin中有两种循环,while和for循环,而while循环和java基本一模一样,因此不再提了,而java中常用的for-i循环被kotlin舍弃,java中的另一种for-each循环则被大幅加强,变成了for-in循环。在学习循环前,要先学习区间的概念,这也是java种没有的。如下代码:
val range = 0..10
这段代码的意思其实就是[0,10]的意思,有了区间,我们就可以用for-in来遍历区间了:
for (i in range){
println(i)
}
但很多时候,我们一般不会用左右闭区间,而会使用左闭右开区间,kotlin也提供了until关键字来实现这个功能:
for (i in 0 until 10){
println(i)
}
这时生成的就是[0,10)区间了。
默认情况下,会在区间范围内递增1,我们可以用step来递增更多,如:
for(i in 0 until 10 step 2){
println(i)
}
这时就是递增2了,相当于i+=2的意思。
但需要注意的是,..和until都只能左边小右边大,也就是只能创建升序区间,而创建降序区间可以用downTo,如:
for (i in 10 downTo 1){
println(i)
}
downTo也可以使用step,不再多提了。
类与对象
基本用法
在面向对象语言中,类和对象是很重要的概念。kotlin自然也有这样的概念。
我们先来通过IDE来定义一个类:
class Person() {
}
可以看到,kotlin也是使用class关键字来声明类的,这点与java一致。我们定义几个变量和函数:
class Person() {
var name = ""
var age = 0
fun eat(){
println(name + " is eating.He is " + age + " years old")
}
}
这里用var是因为我们要在创建对象后对其进行重新赋值,eat函数就不再解释了,很简单。
接下来我们把这个类实例化一下:
val p = Person()
p.name = "Jack"
p.age = 19
p.eat()
可以看到,与java不同的是,我们不再需要new关键字了,因为你调用一个类的构造方法只可能是为了对类进行实例化。接下来我们执行这段代码,发现结果和我们想的一致。
继承与构造函数
面向对象的一个重要特性就是继承,我们先定义一个Student类:
class Student {
}
要让Student类继承Person类,我们要干两件事:
-
使Person类可以被继承。这话听起来似乎很怪,java中所有类默认都是可以被继承的,但Kotlin却不是,在Kotlin中任何一个非抽象类默认都是不可以被继承的,而抽象类如果不继承就无任何意义,所以必须被继承。想要让一个类可以被继承,其实很简单,加上一个open关键字就可以了:
open class Person() { var name = "" var age = 0 fun eat(){ println(name + " is eating.He is " + age + " years old") } }
-
第二件事,自然就是让Student类继承Person类了,如下写法:
class Student:Person(){ var sno = "" var grade = 0 }
继承的写法首先,不再需要extends关键字,加上冒号即可。之后如果仔细观察,会发现多了一对括号。而要理解括号,就要解释另一个概念——构造函数了。
在Kotlin中,有两类构造函数,主构造函数与次构造函数,主构造函数只能有一个,且没有函数体。直接定义在类名的后面。而次构造函数则是可以有任意多个,且有函数体。如下例子:
class Student(var sno:String,var grade:Int)
这种就是典型的主构造函数定义了。如果我们要在主构造函数里写一些逻辑,就需要用到init结构体了:
init{
println("sno is " + name)
println("grade is " + age)
}
而根据继承的特性,子类的构造函数必须调用父类的构造函数。这时我们要怎么调用父类的主构造函数呢?自然是在继承上写了:
因此,如果我们再看上面的最开始的定义就会更加明了了。其实就是调用了父类的无参构造方法,就算是无参的也不能省略括号。如果我们对Person函数改造一下,将Person函数改成有参的构造方法:
open class Person(val name:String,val age:Int) {
}
此时的Student类要继承Person类,就必须实现他的主构造函数,即必须传入name和age两个值。我们的Student类就可以写成这样:
class Student(val sno:String,val grade :Int, name: String , age :Int):Person(name,age){
}
这里定义name和age时不能再声明成val,因为在主构造函数中声明的参数都会默认变成该类的字段。这会导致与同名的name和age发生冲突。因此不用再加任何关键字。
至于次构造函数,当一个类有主构造函数和次构造函数时,所有的次构造函数必须调用主构造函数。具体的例子就不再写了。
接下来我们看一个特殊情况,当一个类没有显式定义主构造函数而且定义了次构造函数时,它就是没有主构造函数的。此时继承Person类就不再需要加上括号了,因为没有主构造函数。
接口
kotlin是继承自java的语言,自然很多方面都很像了。在接口方面,与java有很多相似。我们先定义一个接口:
interface Study {
fun readBooks()
fun doHomework(){
println("do homework default")
}
}
这里kotlin和java一样,支持为接口定义默认函数体,使得实现类不需要实现这个方法。我们让Student类实现这个接口:
class Student( name:String, age:Int) :Person(name,age),Study {
override fun readBooks() {
println(name + "is reading")
}
}
这里可以看到,kotlin中接口的关键字也是冒号,且只需要复写方法即可。我们在main函数调用该方法即可。
这里还需要提一下,函数的可见性修饰符。见下表:
修饰符 | Java | Kotlin |
---|---|---|
public | 所有类可见 | 所有类可见(默认) |
private | 当前类可见 | 当前类可见 |
protected | 当前类、子类、同一包路径下的类可见 | 当前类、子类可见 |
default | 同意包的路径下的类可见(默认) | 无 |
internal | 无 | 同一模块中的类可见 |
这里的模块概念暂时不深究。
数据类与单例类
在写web时候,model是很常用的一种类。我们在kotlin中如何定义一个数据类呢?如下:
data class CellPhone(val brand:String,val price:Double)
只需要加上data关键字,所有需要的方法就全部都实现了,如toString(),equals等等方法。
关于单例模式,也是我们很常见的一种设计模式。要使用单例类,如下:
object Singleton {
fun singletonTest(){
println("singletonTest is called")
}
}
只需要定义为object类即可。要调用里面的方法,如下:
Singleton.singletonTest()
即可。这样看上去是静态方法的调用,但其实kotlin在背后为我们自动创建了一个Singleton类的实例,并保证全局只会存在一个Singleton实例。
以上是关于Kotlin基础学习的主要内容,如果未能解决你的问题,请参考以下文章