仅为个人笔记
目录
1、变量
Kotlin中定义一个变量,只允许在变量前声明两种关键字:val和var。
val:用来声明一个不可变的变量,初始赋值之后不能重新赋值,对应Java中的final变量。
var:用来声明一个可变的变量,初始赋值之后可以被重新赋值,对应Java中的非final变量。
val a: Int = 10
Kotlin每一行代码的结尾是不用加分号的。
fun main() {
var a: Int = 10
a = a * 10
println("a= " + a)
}
结果:
a = 100
2、函数(方法)
fun largerNumber(num1: Int, num2: Int): Int {
return max(num1, num2)
}
首先fun是定义函数的关键字,无论定义什么函数,都一定要使用fun来声明。
紧跟在fun后面的是函数名。函数名后面紧跟着一对括号,里面可以声明该函数接收什么参数,参数的声明格式是“参数名: 参数类”,如果不想接收任何参数,就写一对空括号。
参数括号后面的那部分是可选的,用于声明该函数会返回什么类型的数据,上述示例就表示该函数会返回一个Int类型的数据。如果函数不需要返回任何数据,这部分可以直接不写。
最后两个大括号之间的内容就是函数体了,可以在这里编写一个函数的具体逻辑。上述代码中使用了一个max()函数,这是Kotlin提供的一个内置函数,它的作用就是返回两个参数中更大的那个数。
fun main() {
val a = 37
val b = 40
val value = largerNumber(a, b)
println("larger number is "+ value)
}
fun largerNumber(num1: Int, num2: Int): Int {
return max(num1, num2)
}
结果:
larger number is 40
当一个函数中只有一行代码时,Kotlin允许我们不必编写函数体,可以直接将唯一的一行代码写在函数定义的尾部,中间用等号连接即可。比如我们刚才编写的LargerNumber()函数就只有一行代码,于是可以将代码简化成如下形式:
fun largerNumber(num1: Int, num2: Int): Int = max(num1, num2)
由于max()函数返回的是一个Int值,而我们在LargerNumber()函数的尾部又使用等号连接了max()函数,因此Kotlin可以推导出LargerNumber()函数返回的必然也是一个Int值,这样就不用再显式地声明返回值。
fun largerNumber(num1: Int, num2: Int) = max(num1, num2)
3、if条件语句
fun largerNumber(num1: Int, num2: Int): Int {
var value = 0
if (num1 > num2) {
value = num1
} else {
value = num2
}
return value
}
Kotlin中的if语句相比于Java有一个额外的功能,它是可以有返回值的,返回值就是if语句每
个条件中最后一行代码的返回值。
仔细观察上述代码,你会发现value其实也是一个多余的变量,我们可以直接将if语句返回,这样代码将会变得更加精简。
因此,上述代码就可以简化成如下形式:
fun largerNumber(num1: Int, num2: Int): Int {
return if (num1 > num2) {
num1
} else {
num2
}
}
当一个函数只有一行代码时,可以省略函数体部分,直接将这一行代码使用等号串连在函数定义的尾部。
fun largerNumber(num1: Int, num2: Int) = if (num1 >num2) num1 else num2
4、when条件语句
Kotlin中的when语句有点类似于Java中的switch语句,但它又远比switch语句强大得多。
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
}
当判断条件非常多的时候,就是应该考虑使用when语句的时候。
fun getScore(name: String) = when (name) {
"Tom" -> 86
"Jim" -> 77
"Jack" -> 95
"Lily" -> 100
else -> 0
}
when语句允许传入一个任意类型的参数,然后可以在wheen的结构体中定义一系列的条件,当你的执行逻辑只有一行代码时,{}可以省略。格式:
匹配值 -> { 执行逻辑 }
除了精确匹配之外,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中的instanceof关键字。由于checkNumber()函数接收一个Number类型的参数,这是Kotlin内置的一个抽象类,像Int、Long、Float、Double等与数字相关的类都是它的子类,所以这里就可以使用类型匹配来判断传入的参数到底属于什么类型。
when语句的基本用法就是这些,但其实when语句还有一种不带参数的用法,虽然这种用法可能不太常用,但有的时候却能发挥很强的扩展性。
举个例子,假设所有名字以Tom开头的人,他的分数都是86分,这种场景如果用带参数的when语句来写就无法实现,而使用不带参数的when语句就可以这样写:
fun getScore(name: String) = when {
name.startsWith("Tom") -> 86
name == "Jim" -> 77
name == "Jack" -> 95
name == "Lily" -> 100
else -> 0
}
注意,Kotlin中判断字符串或对象是否相等可以直接使用==关键字,而不用像Java那详调equals()方法。
5、区间
使用如下Kotlin代码来表示一个区间:
..关键字
//创建一个0到10的闭区间,它的数学表达方式是[0,10]
val range = 0..10
上述代码表示创建了一个0到10的闭区间,这意味着0到10这两个端点都是包含在区间中的。其中,..是创建两端闭区间的关键字,在..的两边指定区间的左右端点就可以创建一个区间了。
until关键字
//创建一个0到10的左闭右开区间,它的数学表达方式是[0,10)
val range = 0 until 10
6、for循环
Java中另一种for-each循环被Kotlin进行了大幅度的加强,变成了for-in循环。
fun main() {
for (i in 0..3) {
println(i)
}
结果:
0
1
2
3
使用step跳过区间内的元素
默认情况下,for-in循环每次执行循环时会在区间范围内递增1,相当于Java for-i循环中i++的效果,而如果你想跳过其中的一些元素,可以使用step关键字:
//在遍历[0,5)这个区间的时候,每次执行循环都会在区间范围内递增2。
fun main() {
for (i in 0 until 5 step 2) {
println(i)
}
}
结果:
0
2
4
使用downTo遍历降序区间
//创建一个降序的区间,可以使用downTo关键字。
fun main() {
for (i in 3 downTo 1) {
println(i)
}
}
结果:
3
2
1
0
7、类与对象
//Kotlin中也是使用class关键字来声明一个类的。
class Person {
var name = ""
var age = 0
fun eat(){
println(name + " is eating. He is " + age + " years old.")
}
}
fun main() {
//Kotlin中实例化一个类的方式和Java是基本类似的,只是去掉了new关键字而已。
val p = Person()
p.name = "Jack"
p.age = 19
p.eat()
}
结果:
Jack is eating. He is 19 years old.
构造函数
Kotlin将构造函数分成了两种:主构造函数和次构造函数。
主构造函数将会是你最常用的构造函数,每个类默认都会有一个不带参数的主构造函数,当然
你也可以显式地给它指明参数。主构造函数的特点是没有函数体,直接定义在类名的后面即可。比如下面这种写法:
//主构造函数
class Person(val name: String, val age: Int) {
...
}
这表明在对Person类进行实例化的时候,必须传入构造函数中要求的所有参数。
val person = Person("Tom", 5)
任何一个类只能有一个主构造函数,但是可以有多个次构造函数。次构造函数也可以用于实例化一个类,这一点和主构造函数没有什么不同,只不过它是有函数体的。Kotlin规定,当一个类既有主构造函数又有次构造函数时,所有的次构造函数都必须调用主构造函数(包括间接调用)。
class Person(val name: String, val age: Int) {
//次构造函数是通过constructor关键字来定义的
constructor(name: String) : this(name, 5) {
}
constructor() : this("Tom") {
}
}
这里我们定义了两个次构造函数:第一个次构造函数接收name参数,然后它又通过this关键字调用了主构造函数,并将age参数赋值成初始值;第二个次构造函数不接收任何参数,它通过调用第一个次构造函数,间接的调用了主构造函数。
person1 = Person()
person2 = Person("Jack")
person3 = Person("Jack", 19)
继承
第一步:使父类可继承。
在Kotlin中任何一个非抽象类默认都是不可以被继承的,相当于Java中给类声明了final关键字。加上open关键字之后,我们就是在主动告诉Kotlin编译器,Person这个类是专门为继承而设计的,这样Person类就允许被继承了。
open class Person(val name: String, val age: Int) {
...
}
第二步:要让Student类继承Person类。在Java中继承的关键字是extends,而在Kotlin中变成了一个冒号,写法如下:
class Student(val sno: String, val grade: Int, name: String, age: Int) : Person(name, age){
...
}
Person类后面的一对空括号表示Student类的主构造函数在初始化的时候会调用Person类的无参数构造函数,即使在无参数的情况下,这对括号也不能省略。
注意,我们在Student类的主构造函数中增加name和age这两个字段时,不能再将它们声明成val,因为在主构造函数中声明成val或者var的参数将自动成为该类的字段,这就会导致和父类中同名的name和age字段造成冲突。因此,这里的name和age参数前面我们不用加任何关键字,让它的作用域仅限定在主构造函数当中即可。
//创建一个Student类的实例
val student = Student("a123", 5, "Jack", 19)
那么接下来我们就再来看一种非常特殊的情况:类中只有次构造函数,没有主构造函数。
class Student : Person {
constructor(name: String, age: Int) : super(name, age) {
}
Student类既然没有主构造函数,继承Person类的时候也就不需要再加上括号了。由于没有主构造函数,次构造函数只能直接调用父类的构造函数,上述代码也是将this关键字换成了super关键字。
接口
interface Study {
fun readBooks()
//Kotlin还增加了一个额外的功能:允许对接口中定义的函数进行默认实现。
fun doHomework() {
println("do homework default implementation.")
}
fun ageInformation() {
println("I don't know how old he is.")
}
}
//Kotlin用逗号代替java的implements用来实现接口
class Student(name: String, age: Int) : Person(name, age), Study {
override fun readBooks() {
println(name + " is reading.")
}
override fun ageInformation() {
println(name + " is " + age + " years old.")
}
}
如果接口中的一个函数拥有了函数体,这个函数体中的内容就是它的默认实现。现在当一个类去实现Study接口时,只会强制要求实现readBooks()函数,而doHomework()函数则可以自由选择实现或者不实现,不实现时就会自动使用默认的实现逻辑。
fun main(){
val student = Student("Jack", 19)
doStudy(student)
}
fun doStudy(study: Study) {
study.readBooks()
study.doHomework()
study.ageInformation()
}
结果:
Jack is reading.
do homework default implementation.
Jack is 19 years old.
Java和Kotlin函数可见性修饰符对照表
数据类(data关键字)
构建一个手机数据类,字段就简单一点,只有品牌和价格这两个字段。
//Kotlin
data class Cellphone(val brand: String, val price: Double)
//当一个类中没有任何代码时,还可以将尾部的大括号省略路。
相当于
//java
public class Cellphone {
String brand;
double price;
public Cellphone(String brand, double price) {
this.brand = brand;
this.price = price;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof Cellphone) {
Cellphone other = (Cellphone) obj;
return other.brand.equals(brand) && other.price == price;
}
return false;
}
@Override
public int hashCode() {
return brand.hashCode() + (int) price;
}
@Override
public String toString() {
return "Cellphone(brand=" + brand + ", price=" + price + ")";
}
}
当在一个类前面声明了data关键字时,就表明你希望这个类是一个数据类,Kotlin会根据主构造函数中的参数帮你将equals()、hashCode()、toString()等固定且无实际逻辑意义的方法自动生成,从而大大减少了开发的工作量。
fun main( ){
val cellphone1 = Cellphone("Samsung", 1299.99)
val cellphone2 = Cellphone("Samsung", 1299.99)
println(cellphone1)
printin("cellphone1 equals cellphone2 " + (cellphone1 == cellphone2))
}
结果:
Cellphone(brand=Samsung, price=1299.99)
cellphone1 equals cellphone2 true
单例类(object关键字)
创建一个Singleton单例类
//Kotlin
object Singleton {
fun singletonTest(){
println("singletonTest is called.")
}
}
//调用单例中的函数
Singleton.singletonTest()
//这种写法虽然看上去像是静态方法的调用,但其实Kotlin在背后自动帮我们创建Singleton类的实例,并且保证全局只会存在一个Singleton实例。
相当于
//Java
public class Singleton {
private static Singleton instance;
private Singleton() {}
public synchronized static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
public void singletonTest() {
System.out.println("singletonTest is called.");
}
}
//调用单例中的函数
Singleton singleton = Singleton.getInstance();
singleton.singletonTest();