变量
- Kotlin中定义一个变量,只允许在变量前声明两种关键字:val和var
- val(value),unchangable
- var(variable),changable
- 可自动推类型导类型,如果我们对一个变量延迟赋值,需要显式地声明变量类型
- Kotlin完全抛弃了Java中的基本数据类型,全部使用了对象数据类型
class Int
1
2
|
lateinit val a:Int
val a: Int = 10
|
``
声明与初始化
Kotlin默认所有的参数和变量都不可为空
1
2
3
4
5
6
7
8
9
10
11
12
|
//全局变量延迟初始化替代初始化为null
//private var adapter: MsgAdapter? = null
//避免出现adapter?.notifyItemInserted(msgList.size - 1)
private lateinit var adapter: MsgAdapter
override fun onCreate(savedInstanceState: Bundle?) {
if (!::adapter.isInitialized) {
adapter = MsgAdapter(msgList)
}
}
override fun onClick(v: View?) {
adapter.notifyItemInserted(msgList.size - 1)
}
|
函数
1
2
3
|
fun largerNumber(num1: Int, num2: Int): Int {
return max(num1, num2)
}
|
逻辑控制
条件判断
if
1
2
3
4
5
6
7
8
9
|
fun largerNumber(num1: Int, num2: Int): Int {
var value = 0
if (num1 > num2) {
value = num1
} else {
value = num2
}
return value
}
|
Kotlin中if语句是可以有返回值的
1
2
3
4
5
6
7
8
|
fun largerNumber(num1: Int, num2: Int): Int {
val value = if (num1 > num2) {
num1
} else {
num2
}
return value
}
|
1
|
fun largerNumber(num1: Int, num2: Int) = if (num1 > num2) num1 else num2
|
when
when语句和if语句一样,也是可以有返回值的,因此我们仍然可以使用单行代码函数的语法糖
1
2
3
4
5
6
7
8
|
fun getScore(name: String) = when (name) {
//匹配值 -> { 执行逻辑 }
"Tom" -> 86
"Jim" -> 77
"Jack" -> 95
"Lily" -> 100
else -> 0
}
|
类型匹配,Number类型的参数,是Kotlin内置的一个抽象类,像Int、Long、Float、Double等与数字相关的类都是它的子类
1
2
3
4
5
6
7
|
fun checkNumber(num: Number) {
when (num) {
is Int -> println("number is Int")
is Double -> println("number is Double")
else -> println("number not support")
}
}
|
不带参数的用法
1
2
3
4
5
6
7
|
fun getScore(name: String) = when {
name.startsWith("Tom") -> 86
name == "Jim" -> 77
name == "Jack" -> 95
name == "Lily" -> 100
else -> 0
}
|
循环语句
1
2
3
4
5
6
7
8
|
fun main() {
//val range = 0..10
//val range = 10 downto 1
val range = 0 until 10
for (i in 0 until 10 step 2) {
println(i)
}
}
|
数组与集合
传统意义上的集合主要就是List和Set,再广泛一点的话,像Map这样的键值对数据结构也可以包含进来。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//Returns a new MutableList with the given elements.
val list= mutableListOf<String>("A","B","C","D","E")
list.add("F")
//Returns a new read-only list of given elements.
//val list= listOf("A","B","C","D","E")
for (element in list){
println(element)
}
val map = mapOf("Apple" to 1, "Banana" to 2, "Orange" to 3, "Pear" to 4, "Grape" to 5)
for ((fruit, number) in map) {
println("fruit is " + fruit + ", number is " + number)
}
|
for (element in list){
println(element)
}
val map = mapOf(“Apple” to 1, “Banana” to 2, “Orange” to 3, “Pear” to 4, “Grape” to 5)
for ((fruit, number) in map) {
println(“fruit is " + fruit + “, number is " + number)
}
面对对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
class Person {
var name = ""
var age = 0
fun eat() {
println(name + " is eating. He is " + age + " years old.")
}
}
fun main() {
val p = Person()
p.name = "Jack"
p.age = 19
p.eat()
}
|
继承
在Kotlin中任何一个非抽象类默认都是不可以被继承的,相当于Java中给类声明了final 关键字,需要声明open
1
2
3
4
5
6
7
8
|
open class Person {
...
}
class Student : Person() {
var sno = ""
var grade = 0
}
|
Java继承特性中的一个规定,子类中的构造函数必须调用父类中的构造函数,这个规定在Kotlin中也要遵守,子类的主构造函数调用父类中的哪个构造函数,在继承的时候通过括号来指定.
1
2
|
class BadStudent(val criminalrecord:String,sno:Int,grad:String):Student(sno,grade){
}
|
构造函数 & 析构函数
Kotlin将构造函数分成了两种:主构造函数和次构造函数
主构造函数
每个类默认都会有一个不带参数的主构造函数,当然你也可以显式地给它指明参数。主构造函数的特点是没有函数体,直接定义在类名的后面即可。Kotlin给我们提供了一个init结构体,所有主构造函数中的逻辑都可以写在里面:
1
2
3
4
5
6
7
|
class Student(val sno: String, val grade: Int) : Person() {
init {
println("sno is " + sno)
println("grade is " + grade)
}
}
val student = Student("a123", 5) //可用val
|
次构造函数
任何一个类只能有一个主构造函数,但是可以有多个次构造函数,次构造函数也可
以用于实例化一个类,这一点和主构造函数没有什么不同,只不过它是有函数体的.Kotlin规定,当一个类既有主构造函数又有次构造函数时,所有的次构造函数都必须调用主构造函数(包括间接调用)
1
2
3
4
5
6
7
8
9
10
11
|
class Student(val sno: String, val grade: Int, name: String, age: Int) :
Person(name, age) {
constructor(name: String, age: Int) : this("", 0, name, age) {
}
constructor() : this("", 0) {
}
}
val student1 = Student()
val student2 = Student("Jack", 19)
val student3 = Student("a123", 5, "Jack", 19)
|
首先Student类的后面没有显式地定义主构造函数,同时又因为定义了次构造函数,所以现在Student类是没有主构造函数的。那么既然没有主构造函数,继承Person类的时候也就不需要再加上括号了。另外,由于没有主构造函数,次构造函数只能直接调用父类的构造函数,上述代码也是将this关键字换成了super关键字
1
2
3
4
|
class Student : Person {
constructor(name: String, age: Int) : super(name, age) {
}
}
|
函数的可见性修饰符
接口
在接口中定义一系列的抽象行为,然后由具体的类去实现。如果接口中的一个函数拥有了函数体,这个函数体中的内容就是它的默认实现。现在当一个类去实现Study接口时,只会强制要求实现readBooks()函数,而doHomework()函数则可以由选择实现或者不实现,不实现时就会自动使用默认的实现逻辑。
1
2
3
4
|
interface Study {
fun readBooks()
fun doHomework() {}
}
|
Java中继承使用的关键字是extends,实现接口使用的关键字是 implements,而Kotlin中统一使用冒号,中间用逗号进行分隔。
1
2
3
4
5
6
7
8
|
class Student(name: String, age: Int) : Person(name, age), Study {
override fun readBooks() {
println(name + " is reading.")
}
override fun doHomework() {
println(name + " is doing homework.")
}
}
|
使用接口利于扩展
1
2
3
4
5
6
7
8
9
|
fun main() {
val student = Student("Jack", 19)
doStudy(student)
}
//面向接口编程,也可以称为多态,
fun doStudy(study: Study) {
study.readBooks()
study.doHomework()
}
|
数据类与单例类
数据类通常需要重写equals()、hashCode()、toString()这几个方法。其中,equals()方法用于判断两个数据类是否相等。hashCode()方法作为equals()的配套方法,也需要一起重写,否则会导致HashMap、HashSet等hash相关的系统类无法正常工作。toString()方法用于提供更清晰的输入日志,否则一个数据类默认打印出来的就是一行内存地址。
1
|
data class Cellphone(val brand: String, val price: Double)
|
1
2
3
4
5
6
|
fun main() {
val cellphone1 = Cellphone("Samsung", 1299.99)
val cellphone2 = Cellphone("Samsung", 1299.99)
println(cellphone1)
println("cellphone1 equals cellphone2 " + (cellphone1 == cellphone2))
}
|
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
//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();
|
1
2
3
4
5
6
7
8
|
//kotlin
object Singleton {
fun singletonTest() {
println("singletonTest is called.")
}
}
Singleton.singletonTest()
|
Lambda编程
Lambda就是一小段可以作为参数传递的代码。
1
2
3
4
5
6
7
8
9
10
|
//.maxBy:函数式API
val maxLengthFruit = list.maxBy({ fruit: String -> fruit.length })
//Kotlin规定,当Lambda参数是函数的最后一个参数时,可以将Lambda表达式移到函数括号的外面
val maxLengthFruit = list.maxBy() { fruit: String -> fruit.length }
//如果Lambda参数是函数的唯一一个参数的话,还可以将函数的括号省略
val maxLengthFruit = list.maxBy { fruit: String -> fruit.length }
//由于Kotlin拥有出色的类型推导机制,Lambda表达式中的参数列表其实在大多数情况下不必声明参数类型
val maxLengthFruit = list.maxBy { fruit -> fruit.length }
//当Lambda表达式的参数列表中只有一个参数时,也不必声明参数名,而是可以使用it关键字来代替
val maxLengthFruit = list.maxBy { it.length }
|
1
2
|
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
val newList = list.filter { it.length <= 5 }.map { it.upperCase() }
|
Java函数式API
如果我们在Kotlin代码中调用了一个Java方法,并且该方法接收一个Java单抽象方法接口参数,就可以使用函数式API。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
//单抽象方法接口
public interface OnClickListener {
void onClick(View v,String name);
}
//java
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v,String name) {
}
});
//kotlin
button.setOnClickListener(object: View.OnClickListener() {
over fun onClick(v:View, name:String) {
}
})
//kotlin
button.setOnClickListener {v,name ->
}
|
空指针检查
如果我们向doStudy()方法传入了一个null参数,那么毫无疑问这里就会发生空指针异常。
1
2
3
4
5
|
//java
public void doStudy(Study study) {
study.readBooks();
study.doHomework();
}
|
Kotlin默认所有的参数和变量都不可为空,所以这里传入的Study参数也一定不会为空,我们可以放心地调用它的任何函数。
1
2
3
4
|
fun doStudy(study: Study) {
study.readBooks()
study.doHomework()
}
|
比如,Int表示不可为空的整型,而Int?就表示可为空的整型;String表示不可为空的字符串,而String?就表示可为空的字符串。
1
2
3
4
5
6
7
|
val student01:Student
doStudy(student01)
fun doStudy(study: Study?) {
study?.readBooks()
study?.doHomework()
}
|
?.操作符表示对象为空时什么都不做,对象不为空时就调用let函数,而let函数会将study对象本身作为参数传递到Lambda表达式中
1
2
3
4
5
6
|
fun doStudy(study: Study?) {
study?.let {
it.readBooks()
it.doHomework()
}
}
|