这篇文章作为我读《第一行代码第三版》第二章的笔记,同时算一篇(混乱的)极简教程,献给和我一样从C++/Java/C#进入安卓开发的新手。
Kotlin的文件后缀为 .kt ,就是Kotlin的简写
在Kotlin中,创建一个main函数(就是入口啦),可以和C一样单独声明一个main函数:
fun main()
{
//Coding here...
}
也可以如OOP形式,在类中声明一个静态main方法,在这种情况下,main函数的参数不可少
class MyMain{
companion object {
@JvmStatic
fun main(args: Array<String>) {
//Coding here
}
}
}
在IDEA中,main函数的边上会出现一个绿色的三角,点击即可编译运行。
两种方法都可,但作为学习,第一种更加简洁
暂且先用着,后面刨根问底。
在Kotlin中,用val声明常量(value),用var声明变量(variable),kotlin有着优秀的类型推导,所以在声明变量时可以不用显示指明类型 例:
val a = 1
a =a * 4//报错! Val cannot be reassigned
var b = “Hello”
b = "a"//Success!
当类型推导不能正常工作或者延迟赋值的时候,可以手动声明变量的类型
val a : Int = 3
var b : String = "Hello"
var c : Long
c= 100000
但注意,不论是手动声明类型还是自动推导,一旦变量初始化了,就不能再更改类型。
Kotlin的内置类型与Java对照如下表
| 类型 | Kotlin | Java |
|:------:|:------:|:------:|
|字节|Byte|byte/Byte|
|整型|Int & Long|int/Integer & long/Long|
|浮点型|Float & Double|float/Float & double/Double|
|字符|Char|char/Character|
|字符串|String|String|
用fun关键字可声明一个函数
fun myFun(){
return
}
函数的返回值与声明变量类型的方法相同,在函数声明的后面加上冒号和类型
fun myFun():Int{
return 3
}
带参数的函数也大差不差
fun Add(a : Int ,b : Int):Int{
return a + b
}
由于Kotlin还提供了一种语法糖,来简化只有一行的函数
fun Add(a : Int ,b : Int):Int = a + b
再结合一下他的类型推导系统,可以得到一种更简洁的语法
fun Add(a : Int ,b : Int) = a + b
Kotlin的if与其它语言的if并无不同
fun myMax(a : Int,b : Int):Int{
if(a > b){
return a
}else{
return b
}
//当然还可以后面跟着else if,随意!
}
但Kotlin的if是可以有返回值的,就比如上面的函数可有化简为
fun myMax(a : Int,b : Int):Int{
return if(a > b){
a
}else{
b
}
}
再次结合之前的一大堆内容,我们可以得到终极化简
fun myMax(a : Int,b : Int) = if(a > b) a else b
可以说Kotlin的if是加强版的三元运算符
Kotlin的when是大号版的switch,不同于其他语言的switch需要break,when语句相当直接
fun getScore(name: String) = when (name)
{
"Tom" -> 86
"Jim" -> 77
"Jack" -> 95
"Lily" -> 100
else -> 0
}
当然,你也可以让他执行一些逻辑,比如你想帮某个学生偷偷加点分,只需要一个大括号
fun getScore(name: String) = when (name)
{
"Tom" -> {
var score = 87
score +=10
}
"Jim" -> {
77
}
"Jack" -> {
95
}
"Lily" -> {
100
}
else -> {
0
}
}
还有一种when的语法可以看作加强版的if-else if-else
比如上面的语句可以改写
fun getScore(name: String) = when
{
name == "Tom" -> {
var score = 87
score +=10
}
name == "Jim" -> {
77
}
name == "Jack" -> {
95
}
name == "Lily" -> {
100
}
else -> {
0
}
}
这种方式不在when中传入参数,而是将判断逻辑写在了when的结构体里
ps.Kotlin的String可以用==直接判断相等
学过其他的语言都会while,所以Kotlin的while不用多说了...
这里最难理解就是for循环
Kotlin的for的语法,是和别处不同的:都是括号里一个循环变量,但是Kotlin有区间这个东西!
先说一下区间,在Kotlin你可以做如下的赋值:
val range = 0..10
这相当与创建了一个从0到10的区间,并且两端都是闭区间,用数学的方法来写就是$[0,10]$,假如你想要一个左闭右开,那么这么来:
val c = 0 until 10
同样的,在数学上就是$[0,10)$
现在我们可以进入Kotlin的for循环了,准确的说是for-in循环:
for(i in 1..10){
println(i)
}//std out:1 2 3... 10
for(i in 1 until 10){
println(i)
}//std out:1 2 3... 9
或者可以用step关键字声明步长
for(i in 1..10 step 2){
println(i)
}//std out:1 3 5 7 9
那如果是反着来呢?比如从10降到1?是把步长的值调为负数么?试一下
for(i in 10..1 step -1){
println(i)
}//报错!: Step must be positive, was: -1.
这样行不通!因为步长必须是正数!
Kotlin有针对这样情况的语法,只要用关键词downTo
for(i in range 10 downTo 1){
println(i)//std out:10 9....1
}
同样的,这代表了一个两边都是闭区间的范围
如果还有更多需求...找while去吧
ArrayList可以类比C++的std::vector<T>
Kotlin的写法如下
val fruit = arrayListOf<String>("Apple","Banana","Peach")
fruit.add("Pear")
fruit[1] = "cherry"
这个val的意思不是ArrayList里的东西不可改,而是表示指向ArrayList的引用不可改!如同C++的int * const
如上,ArrayList是可变的,不定长的,需要指明类型
不过你也可以不指明类型,然后把各种类型放进去
val largeArrayList = arrayListOf(1,"Banana",3.33f)
这是其实是省略了arrayListOf<Any>
的<Any>
类型,一个数据结构如果指明了是Any的话,那就可以放入任何东西了,除了Null
Any类型事实上就是Java的Object类,在Compile时被映射,Runtime时Any就被视为了Object
所以,一切的类都可以视作继承自Any
利用这个特性,可以用is
关键词来判断元素类型,先用一个简单的for-in来遍历这个ArrayList,然后根据不同的类型打印不同的语句
for(i in largeArrayList)
{
when
{
(i is Int) ->
{
println("I is Int")
}
(i is String) ->
{
println("I is String")
}
(i is Double) ->
{
println("I is Double")
}
else -> println("Nope")
}
}
利用listOf()声明一个不可变的List,就是不能改变里面的任何元素
//手动声明类型
val fruit = listOf<String>("Apple","Banana","Peach")
//利用类型推导
val intList = listOf(1,2,3,4)
利用mutableListOf()声明一个可变的mutableList,就是能改变里面元素的List
//手动声明类型
val fruit = mutableListOf<String>("Apple","Banana","Peach")
fruit[0] = "Pear"
//利用类型推导
val intList = mutableListOf(1,2,3,4)
intList[0] = 5
还有一种不可存放重复元素的数据结构Set集合,当存放了多个元素,里面只会存在一份,使用方法
//不可变
val set = setOf("Apple", "Banana", "Orange", "Pear", "Grape")
//可变
val mutableSet = mutableSetOf("Apple", "Banana", "Orange", "Pear", "Grape")
还有存放键值对的数据结构Map
val map = HashMap<String, Int>()
//添加、读取方法方法一
//不建议使用
map.put("Apple", 1)
map.put("Banana", 2)
var number = map.get("Apple")
//添加、读取方法二
map["Grape"] = 5
number = map["Apple"]
//添加方法三
//不可变map
val anotherMap = mapOf("Apple" to 1, "Banana" to 2, "Orange" to 3, "Pear" to 4, "Grape" to 5)
//这里的to并不是关键字, 而是infix函数
//同样的,用mutableMapOf创建可变的map
Lambda就是所谓的匿名函数,主流的语言中都有,Kotlin也不例外
有一个需求,找到水果集合中字符最长的水果名字,传统写法
val set = setOf("Apple", "Banana", "Orange", "Pear", "Grape")
var maxLen = 0
var fruitName : String
for(i in set){
if(i.length > max_){
maxLen = i.length
fruitName = i
}
}
但利用lambda可以更简单,使用函数式API:
val set = setOf("Apple", "Banana", "Orange", "Pear", "Grape")
val lambda = {str : String -> str.length}
val maxLength = set.maxBy(lambda)
maxBy函数可以根据传入的lambda的返回值来筛选最大的值 当然也可以不用专门定义lambda变量,直接传入函数
val set = setOf("Apple", "Banana", "Orange", "Pear", "Grape")
val maxLength = set.maxBy({str : String -> str.length})
这段还可以更加简化,如下图
图片来源: https://blog.csdn.net/PrisonJoker/article/details/114273946
对于集合还有些常用的接受Lambda的一堆API
比如.filter,当lambda返回true的时候就会把目标的元素加入一个新的集合,最后返回一个满足过滤条件的集合
map可以把集合里的元素按照Lambda所指定的方法映射,集合里的元素是lambda的返回值
val fruit = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
//返回一个集合里以A开头的元素组成的集合
var Afruit = fruit.filter { it.startsWith("A") }
//转换到大写,并且让元素的后面更上一句话
Afruit = Afruit.map{ it.uppercase() }.map{ it+" is my fruit" }
在IDEA/Android Studio可以用右键源文件夹,新建一个类;当然也可以手动在目录下新建文件 XXX.kt
用关键词class
创建一个类,并且在main函数创建对象
class Person{
var age = 0
fun myAge(){
println("I am "+age+" years old!")
}
}
...
//in function main
val me = Person()
me.age = 20
me.myAge()
Kotlin创建对象的时候不用new关键字,这也是其语法的特点。
因为还没有构造函数,所以我们只能在类中初始化字段age
Kotlin的构造函数分为主构造函数和次构造函数
每个类默认都有一个无参数的主构造函数
两种构造函数的定义方法:
class Person(var age : Int){
init {
age -= 2
}
//次构造函数
constructor():this(0){
println("Call to sub constructor")
}
//允许重写
fun myAge(){
println("I am "+age+" years old!")
}
}
在这里,可以看到字段age的声明放到了Person类声明的括号里去了,这样在初始化类Person的时候就能够同时初始化age字段了
同时用init{}
作为构造函数的一部分,可以把要执行的逻辑放进去。
次构造函数则必须调用主构造函数,用关键词constructor
声明
在kotlin中类中所有的函数默认访问权限都是public,另附java,kotlin访问权限对照表
Kotlin的类默认是不能继承的,在前面加上关键字open
用来允许其他类继承
在函数前加上open允许重写
class(mAge : Int) : Person(mAge){
//注意,要在Person类的函数前加上open
override fun myAge(){
println("It's me! I am "+age+" years old!")
}
}
用符号:
表示继承的关系,同时必须调用父类的构造函数。
interface Study {
fun readBooks()
fun doHomework()
}
让Me类实现接口,每个接口中的函数都必须实现!!!
class(mAge : Int) : Person(mAge){
override fun myAge(){
println("It's me! I am "+age+" years old!")
}
override fun readBooks() {
println(name + " is reading.")
}
override fun doHomework() {
println(name + " is doing homework.")
}
}
也可以在接口中做一个默认实现,这样在类中就可以不需要强制实现这两个函数了
interface Study {
fun readBooks(){
println("TO DO: readBooks")
}
fun doHomework(){
println("TO DO: doHomeWorks")
}
}
Kotlin对单例的实现做了巨大的简化,只需要用关键字object替换class就可以了
object Singleton{
fun mPrint(){
println("Nope.")
}
}
调用方式
//in main function
Singleton.mPrint()
Kotlin会保证只有一份这个类存在.
数据类的声明为data class
,在数据类中会自动覆写equals()、hashCode()、toString()
方法..
Kotlin中函数默认不能传入null,编译器会报错。
如果想突破这个限制,在类型后面加个?
,这就是可空类型
但这时候,传入参数会为空,正常情况在调用参数时要做非空判断,Kotlin引入了简便的判空系统
fun study(man : Person?){
man?.myAge()
/*
if(man != null){
man.myAge()
}
*/
}
但如果你非常确定它不会为空,而且希望编译器不报错,那么使用强制断言
fun study(man : Person?){
man!!.myAge()
}
如果存在多个判空,在逻辑上视为多个if,可以用let函数来简化
fun study(man : Person?){
man?.let {
it.myAge()
it.toString()
}
}
同时let可以处理全局变量判空问题,保证线程安全