快速入门kotlin编程(精简但全面版)
注:本文章为个人学习记录,如有错误,欢迎留言指正。
目录
1. 变量
1.1 变量声明
1.2 数据类型
2. 函数
3. 判断语句
3.1 if
3.2 when语句
4. 循环语句
4.1 while
4.2 for-in
5. 类和对象
5.1 类的创建和对象的初始化
5.2 继承
5.3 构造函数
5.3.1 主构造函数
init结构体
5.3.2 次构造函数
5.3.3 无主构造
6. 接口
6.1 接口定义
6.2 接口的实现
7. 权限修饰符
8. 数据类和单例类
8.1 数据类
8.2 单例类
9. Lambda
9.1 集合的创建和使用
9.2 Lambda的使用
10. Java函数式接口的使用
11. 空指针检查机制
11.1 判空辅助工具
11.2 let函数
11.3 全局变量判空
12. 内嵌表达式
13. 函数的参数的默认值
1. 变量
kotlin没有;结尾
1.1 变量声明
//不声明类型(kotlin存在类型推导机制)
val a = 1 //final修饰,先使用val,有需要可变,再改成var
var b = 2 //无final修饰
// 声明类型用“:”
var c: Int = 1
优先使用val
1.2 数据类型
kotlin没有基本数据类型,使用对象类型 Int,Long,Short, Float,Double,Boolean,Char,Byte
2. 函数
fun main() {
}
fun sum(a: Int, b: Int): Int {
return a + b
}
// 函数体只有一行代码:
fun sum(a: Int, b: Int): Int = a + b
// Kotlin存在类型推导,返回值类型也可省略
fun sum(a: Int, b: Int) = a + b
3. 判断语句
有if和when
3.1 if
if语句必须要有else,不然会报错
//返回最大值
fun max(a: Int, b: Int): Int {
if (a > b)
return a
else
return b
}
//if可以包含返回值,if语句的最后一行会作为返回值返回
fun max(a: Int, b: Int): Int {
return if (a > b)
a
else
b
}
//写成一行
fun max(a: Int, b: Int) = if (a > b) a else b
3.2 when语句
eg:
//使用if实现
// Kotlin中==等价于Java的equals, 比较的是对象里的内容,
// === 等价于Java的==,比较的为对象的引用。
fun getScore(n: String) = if (n == "A") "优秀"
else if (n == "B") "良好"
else "不好"
//使用when实现(when和if一样也可以有返回值)
fun getScore1(n: String) =
when (n) {
"A" -> "优秀"
"B" -> "良好"
else -> "不好"
}
when支持参数检查
fun checkType(num: Number) {
when (num) {
is Int -> println("Int")
is Double -> println("Double")
else -> println("others")
}
}
when里面也可以不填形参
fun getScore2(n: String) =
when {
n == "A" -> "优秀"
n == "B" -> "良好"
else -> "不好"
}
->后面也可以有很多行
fun getScore3(n: String) =
when {
n == "A" -> {
println("打印一下:$n")
"优秀"
}
n == "B" -> "良好"
else -> "不好"
}
4. 循环语句
有while和for-in。
while与java中的while没有区别,
for-in是对Java for-each的加强,
Kotlin舍弃了for-i的写法
4.1 while
和java一样
4.2 for-in
Java中的for-i在Kotlin中被舍弃
Java中的for-each被Kotlin加强变成了for-in
-
什么是区间
// 表示[0,10]区间 val range = 0..10 // 表示[0,10)区间 val range1 = 0 until 10
-
for-in的使用
for (i in 0..10) { println(i) //0 ~ 10 } for (i in 0 until 10) { println(i) //0 ~ 9 } for (i in 0 until 10 step 2) { println(i) //0,2,4,6,8 } for (i in 10 downTo 1) { println(i) //10 ~ 1 }
5. 类和对象
5.1 类的创建和对象的初始化
-
创建Person类
class Person {
var name = ""
var age = 0
fun pringInfo() {
println(name + "的年龄是" + age)
}
}
-
在main方法中声明一个Person对象并调用printInfo方法
fun main() {
//去掉了new关键字
val person = Person()
person.name = "lyx"
person.age = 24
person.pringInfo()
}
5.2 继承
Kotlin中任何一个非抽象类默认都是不可以被继承的,相当于Java中给类声明了final 关键字。
声明Student类继承Person,Kotlin中继承使用:,后接父类的构造
Person类为final不可被继承,因此需借助open关键字
class Student : Person() {
var id = ""
var grade = 0
fun study() {
println(name + "是一个学生")
}
}
5.3 构造函数
Kotlin将构造函数分 成了两种:主构造函数和次构造函数。
5.3.1 主构造函数
特点:没有函数体,直接定义在类名的后面
每个类默认都会有一个不带参数的主构造函数
class Student(val id: String, val grade: Int) : Person() {
}
也可以显式地给它指明参数
实例化的时候,必须传入构造函数中要求的所有参数
open class Person(val name :String,val age :Int) {
}
此时Student类就会报错,因为Person没有无参构造了
修改:
class Student( name :String, age :Int,val id: String, val grade: Int) : Person(name,age) {
}
init结构体
构造时需要进行特殊处理怎么办,Kotlin提供了init结构体,主构造的逻辑可在init
中处理
open class Person(val name :String,val age :Int) {
init {
println("name is" + name)
println("age is" + age)
}
}
5.3.2 次构造函数
任何一个类只能有一个主构造函数,但是可以有多个次构造函数
次构造写在{}里面
有函数体
当一个类既有主构造函数又有次构造函数时,所有的次构造函数都必须调用主构造函数(包括间接调用)
class Student(name: String, age: Int, val id: String, val grade: Int) : Person(name, age) {
// 三个参数
// this关键字调用了主构造函数,this里面写默认值
constructor(name: String, age: Int, id: String) : this("", 0, "", 0) {
}
// 无参构造
//this关键字调用刚才定义的第一个次构造函数
constructor() : this("", 0, "", 0) {
}
}
5.3.3 无主构造
次构造可以调用父类构造super进行初始化,
但是次构造的参数在其他地方无法引用,
class Student : Person {
// 次构造可以调用父类构造super进行初始化
constructor(name: String, age: Int, id: String) : super(name, age) {
}
fun study() {
//
// name,age可使用
println(name + "," + age + ",")
// 使用number则会报错,若number是主构造的参数则可引用
// println(number) 报红
}
}
6. 接口
6.1 接口定义
和java类似
interface Study {
fun study()
fun eat()
}
6.2 接口的实现
在后面加逗号
class Student : Person ,Study{
//Kotlin中使用override关键字来重写父类或者实现接口中的函数
override fun study() {
TODO("Not yet implemented")
}
override fun eat() {
TODO("Not yet implemented")
}
}
Kotlin支持接口方法的默认实现,
方法有方法体默认实现,则继承类无需必须实现此方法
interface Study {
fun study()
fun eat(){
println("eat")
}
}
7. 权限修饰符
Java | kotlin | |
---|---|---|
public | 所有类 | 所有类(默认) |
protected | 当前类,子类,同一包下的类 | 当前类,子类 |
default | 同一包的类(默认) | x |
private | 当前类 | 当前类 |
internal | x | 同模块下的类 |
8. 数据类和单例类
8.1 数据类
数据类则只处理数据相关,与Java Bean类似,通常需要实现其get
,set
,hashCode
,equals
,toString
等方法
一行代码即可
若无data
关键字,上述方法(hashCode
,equals
,toString
)无法正常运行
当一个类中没有任何代码时,还可以将尾部的大括号省略。
data class UserBean(var id: Int, var name: String)
8.2 单例类
创建单例类,创建类型选择“Object”
object Singleton {
fun test(){
println("===")
}
}
对应Java:
public final class Singleton {
@NotNull
public static final Sinfleton INSTANCE;
public final void test() {
String var1 = "===";
System.out.println(var1);
}
private Sinfleton() {
}
static {
Sinfleton var0 = new Sinfleton();
INSTANCE = var0;
}
}
使用:
fun main() {
Singleton.test()
}
9. Lambda
9.1 集合的创建和使用
fun main() {
//(1) List集合的创建
// 常规创建
val list = ArrayList<Int>()
list.add(1)
list.add(2)
// lisrOf:不可变,只能查
val list1 = listOf<Int>(1, 2, 3)
// list1.add(4) //报错
// mutableListOf:可变
val list2 = mutableListOf<Int>(1, 2, 3)
list2.add(4)
// 循环
for (value in list2) {
println(value)
}
//(2) Set集合与List类似, 只是使用的是setOf()和mutableSetOf()
val set = setOf<Int, String>(1 to "1", 2 to "2")
//(3) Map
val map1 = HashMap<Int, String>()
map1.put(1, "1")
map1.put(2, "2")
// 赋值
map1[2] = "222"
// 访问
println(map1[2])
println(map1.get(2))
// 不可变
val map2 = mapOf<Int, String>(1 to "1", 2 to "2")
// map2[2] = "222" //报错
// 可变
val map3 = mutableMapOf<Int, String>(1 to "1", 2 to "2")
map3[2] = "222"
// 循环
for ((key, value) in map3) {
println("$key:$value")
}
}
9.2 Lambda的使用
Lambda格式: { 参数名1: 参数类型, 参数名2:参数类型 -> 函数体 }
-
maxByOrNull():返回当前集合中...最大元素
-
map 映射:返回新集合,将集合中的元素映射成另一个值
-
filter过滤:返回新集合,将集合中的元素进行筛选
-
any:返回Boolean,集合中是否存在元素满足Lambda的条件,有则返回true,无则false
-
all:返回Boolean,集合中元素是否全部满足Lambda的条件,有则返回true,无则false
fun main() {
val list = listOf<String>("aa", "dededed", "ajd", "aa")
// 返回当前list中xx最大的元素
// (1)不使用maxByOrNull函数
var maxStr = ""
for (str in list) {
if (str.length > maxStr.length) {
maxStr = str
}
}
println("list中最大的str:" + maxStr)
// (2)使用maxByOrNull函数
// maxByOrNull或者maxBy是一个普通的方法,参数是Lambda参数
var lambda = { str: String -> str.length }
var maxString = list.maxByOrNull(lambda)
println("list中最大的str:" + maxString)
// 直接当成参数传递
var maxString1 = list.maxByOrNull({ str: String -> str.length })
// 如果lambda是方法的最后一个参数,参数可以提出来
var maxString2 = list.maxByOrNull() { str: String -> str.length }
// 如果只有一个参数,且是lambda参数,可以去掉方法的()
var maxString3 = list.maxByOrNull { str: String -> str.length }
// 有类型推导机制,也可以去掉参数类型
var maxStrinf4 = list.maxByOrNull { str -> str.length }
// 如果lambda只有一个参数,可以用it代替
var maxString5 = list.maxByOrNull { it.length }
// map映射(将集合中的每个元素都映射成一个另外的值)
var newList1 = list.map { it.toUpperCase() }
// filter过滤
var newList2 = list.filter { it.length > 5 }
// any 判断是否存在满足条件的元素
var isAny = list.any { it.length > 5 }
// all 判断是否全部满足条件
var isAll = list.all { it.length > 0 }
}
10. Java函数式接口的使用
kotlin调用Java的方法,如果方法只接收一个Java单继承方法接口参数,可以使用函数式接口。
单继承方法接口:就是接口中只有一个方法,比如Runnable接口:
public interface Runnable {
void run();
}
在Java中启动一个线程:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("test");
}
}).start();
在kotlin中:
// Kotlin摒弃了new,若想声明匿名内部类,必须使用object
Thread(object :Runnable{
override fun run() {
print("aaa")
}
}).start()
// Runnable类中只有一个待实现方法,即使这里没有显式地重写run()方法,Kotlin也能自动明白Runnable后面的Lambda 表达式就是要在run()方法中实现的内容
Thread(Runnable {
print("aaa")
}).start()
// 如果一个Java方法的参数列表中有且仅有一个Java单抽象方法接口参数,可以将接口名进行省略
Thread({
print("aaa")
}).start()
// Thread只需一个参数,可省略()
Thread {
print("aaa")
}.start()
// 与上类似的,click也使用上述方法
// button.setOnClickListener { println("test") }
11. 空指针检查机制
Kotlin把空指针异常的检查提前到了编译期,若空指针则编译期就会崩溃,避免在运行期出现问题
fun main() {
// study(null)// 编译期就会报错
}
fun study(study: Study) {
study.eat()
}
有特殊的需求,可能需要传递null参数,用?表示可以为空
fun main() {
study(null)
}
// ? 表示参数可以为空,
fun study(study: Study?) {
// 此时这里会报错
study.eat()
}
如果参数可为空的话,则此对象调用的方法必须要保证对象不为空,上面代码没有保证,则报错,修改如下
fun main() {
study(null)
}
// ? 表示参数可以为空,
fun study(study: Study?) {
if (study != null) {
study.eat()
}
}
11.1 判空辅助工具
-
?.
表示?前面的不为空,才会执行.后面的方法,
如果为空,就什么都不做
fun study(study: Study?) { study?.eat() }
-
?:
表示?前的不为空,才返回?前的值,否则返回?后的值
val c = a ?: b fun getTextLength(text: String?) = text?.length ?: 0
-
!!
想要强行通过编译
fun study(study: Study?) { study!!.eat() }
11.2 let函数
fun study(study: Study?) {
// ?.保证了study不为空,才会执行let函数
study?.let {
it.eat() // it是study
it.drink()
}
}
11.3 全局变量判空
全局变量即使做了判空,但是很可能被其他地方修改,还是不能保证没有空指针
var str: String? = null
fun demo() {
//有问题
if (str != null) {
str.toUpperCase()
str.reversed()
}
}
fun demo1() {
//使用let规避风险
str?.let {
it.toUpperCase()
it.reversed()
}
}
12. 内嵌表达式
在Java中使用的是+连接,kotlin中用$
fun main() {
var name = "lyx"
var age = 24
println("姓名:$name,年龄:$age")
// 复杂的: ${}
var a = 2
var b = 3
println("a和b比较,${if (a > b) a else b} 大")
}
13. 函数的参数的默认值
给函数设定参数默认值,在很大程度上能够替代次构造函数
fun main() {
// 正常传参
myFun(1, "lisa")
// 只传第一个,第二个参数有默认
myFun(2)
// 如果第一个参数有默认值,此时会报错,因为参数按照顺序放的
// myFun1("lyx") //认为传入的是Int
// kotlin使用键值对传参,不必按照参数定义的顺序来传参
myFun1(name = "lyx")
}
// 第二个参数有默认值
fun myFun(id: Int, name: String = "nana") {
println("$id:$name")
}
// 第一个参数有默认值
fun myFun1(id: Int = 4, name: String) {
println("$id:$name")
}
之前的次构造函数(三个参数的),可以和主构造合并,只需要把不需要的参数加上默认值即可:
// 不使用默认参数
class Student1(name: String, age: Int, val number: String, val grade: Int) : Person(name, age) {
constructor(name: String, age: Int, number: String) : this(name, age, number, 0) {
}
}
// 使用默认参数
class Student2(name: String, age: Int, val number: String, val grade: Int = 0) : Person(name, age) {
}