Hello word
前言:上次学习安卓,学了Java开发,简单的搭了几个安卓界面。这次要学习Kotlin语言,然后开发安卓,趁着还年轻,学点新东西,坚持~
未来的你会感谢现在努力的你~
主要学习资料:
- 《第一行代码Android 第三版》
- 菜鸟教程
- android官网-kotlin
- Kotlin 官方文档 中文版
- 2018年黑马安卓开发视频教程
- 幕课网课件
那么,开始吧~
开始-Kotlin语言-安卓开发学习
首先是搭建环境,在一通操作过后,在android studio上成功安装行了kotlin
经典的:hello word
eclipse环境搭建结果:
参数
又是一个绕不开的话题
kotlin中
使用val修饰的变量,是不可变的(value)
使用var修饰的变量,是可变的(variable 可变的)
- tips: 优先使用val来声明一个变量,而当val没有办法满足你的需求时再使用var
小数类型的,默认是Double
var a = 3.1415f/3.1415F表示Float类型
小数点6位以内,用Float类型
小数点大于6位,用Double类型
for循环
- kotlin也有swift中的开区间、闭区间的概念
四种类型:[],(),[),(]
注意,所谓 “全开区间” 或者是 “左开右闭” 的区间,可以用其他两种方式代替
var nums = 1..10for (num in nums){print("$num, ")}println()var nums2 = 1 until 10for (num in nums2){print("$num, ")}
运行结果:
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
1, 2, 3, 4, 5, 6, 7, 8, 9,
step n
隔几个,打印1个
for (num in 1..20 step 2){print("$num, ")}
运行结果:1, 3, 5, 7, 9, 11, 13, 15, 17, 19,
reversed()
翻转打印
var nums2 = 1 until 10for (num in nums2.reversed()){print("$num, ")}
运行结果:9, 8, 7, 6, 5, 4, 3, 2, 1,
数据容器
容器是用于存放数据的载体。容器分为数组、集合
- 使用arrayOf创建数组
var array: Array<Any> = arrayOf("1", "2", 2, 3.11)
kotlin中的Any类型,等价于其他开发语言中的object类型
- 使用arrayOfNulls创建数组
创建一个指定大小的、所有元素都为空的数组,但必须指定集合中的元素类型
var array2 = arrayOfNulls<String>(5)
var array2 = arrayOfNulls<String>(2)array2[0] = "333"for (temp in array2){println(temp)}
运行结果:
333
null
- 动态创建数组
用接受数组大小,以及一个方法参数的Array构造方法,用作参数的方法能够返回给定索引的每个元素初始值
//数组的大小:4//遍历数组,i。取出每一个i,做i*i的操作,结果放入数组var array3 = Array(4){i -> (i*i)}array3.forEach { println(it) }
根据不同类型,创建特定类型的数组
- 指定类型
//指定个数var array4 = IntArray(2)array4[0] = 1array4[1] = 99array4.forEach { println(it) }
- 指定类型,指定个数,且每个元素一样
//创建一个长度为5的数组,里面每个元素都是100var array5 = IntArray(5){100}array5.forEach { println(it) }
- 指定类型,指定个数,且每个元素可按规则自定义
var array6 = IntArray(5){it*2}array6.forEach { println(it) }
数组的几种for-in循环遍历
var array6 = IntArray(5){it*2}//最简单的for (item in array6){println(item)}//使用indices,取indexfor(index in array6.indices){println("array6[${index}] = ${array6[index]}")}//使用withIndex,取出index和itemfor((index, item) in array6.withIndex()){println("array6[${index}] = ${item}")}
- 数组创建的时候,需要指定个数,而且创建完成后,不能更改数字个数(不能添加、删除元素),只能查询、更改元素值
这明显是个不可变数组,难度没有可变数组吗?还是说可变数组使用集合表示?
Kotlin集合
可变集合的大小可以动态改变
- List:是一个有序集合,元素可重复
- Set:是一组无重复元素的集合(无序)
- Map:是一组键值对(字典)
可变集合、不可变集合
在可变的列表中,有两个方法,推荐使用带mutable的,字如其意
例子:创建一个可变集合,做增、删操作
var array = mutableListOf<String>()//增加元素array.add("123")array.add("333")//在index的位置,增加元素array.add(1, "2")var array2 = mutableListOf<String>()array2.add("1")array2.add("3")//增加一个数组里面的内容array.addAll(array2)array.forEach { println(it) }//根据下标index移除元素array.removeAt(0)//根据value移除元素array.remove("333")array.forEach { println(it) }
listOf()创建集合的时候,必须指定元素类型、必须指定初始化数据
例子:创建一个不可变集合,做增、删操作
使用listOf创建一个list
使用witIndex()获取index的值,然后使用(x, y)两个参数接收
fun main(args: Array<String>){var array = listOf<String>("1", "2", "4", "9")for ((index, item) in array.withIndex()){println("$index "+"$item")}
运行结果:
0 1
1 2
2 4
3 9
例子:创建一个map
//导入TreeMap包
import java.util.TreeMapfun main(args: Array<String>){//创建一个mapvar map2 = TreeMap<String, String>()//赋值map2["good"] = "好"map2["bad"] = "坏"map2["study"] = "学习"println(map2["bad"])
}
readLine()
readLine():获取键盘输入的字,获取后是String?
类型的
函数
函数的格式:
kotlin:
func 函数名(参数名: 参数类型): 返回值类型{函数体
}
oc:
- (返回值类型)函数名(参数类型: 参数名) {函数体
}
只是组合方式不一样
函数的四要素:函数名、参数、返回值类型、函数体
都有
函数的简略写法:
fun sum(a: Int, b: Int): Int{return a+b
}
//如果函数体里面只有一条语句,而且该语句有返回值,则可以去掉{},并省略return,直接使用=:
fun sum2(a: Int, b: Int): Int = a+b
在kotlin中,函数和参数(变量)一样,都是一等公民
那个,也可以将函数表达式赋值给一个变量:
///等号右边,称为:函数表达式var i = {a: Int, b: Int -> a+b}println(i(3, 8))
还有另外一种写法:
//var j:后面,表面是一种类型,j是一个函数类型,两个入参,一个出参//=后面,是一个函数表达式var j:(Int, Int)->Int = {x, y -> x+y }println(j(1, 8))
也就是,函数也可以充当函数里面的参数,或者返回值
函数的分类
函数通过声明位置的不同,分为:
- 普通类的方法(对象方法)
- 静态类的方法(类方法)
- companion object伴生类的方法
companion:同伴,伴侣
普通方法:
class Person {fun test(){println("普通方法")}
}
调用的时候,先创建对象,才能调用
Person().test()
静态类方法
使用objec创建的类,称为静态类
不需要构建对象,可以通过类名,直接访问静态方法
object NumUtil {fun test(){println("这是一个静态方法")}
}
调用:NumUtil.test()
伴生类方法
如何在一个普通类里面,直接通过类调用方法呢?
可以将方法放在companion object{}里面即可
class Person {companion object{fun test2(){println("这是一个伴生类方法")}}
}
调用Person.test2()
方法的参数
- 默认参数
- 具名参数
- 可变数量的参数
默认参数
方法参数可以有默认值,当省略相应的参数时使用默认值
fun main(args: Array<String>){read(1,2)read(start = 2)
}fun read(offset: Int = 0, start: Int){println("offset=${offset}, start=${start}")
}
运行结果:
offset=1, start=2
offset=0, start=2
offset: Int = 0
就是默认值0
当不写默认值的时候,要指定后面的参数start
而这种在调用函数的时候,指定参数名的方法,就是具名参数
具名参数,指定具体名称的参数
举个例子:默认参数与具名参数
val PI = 3.14ffun main(args: Array<String>){//要写3.14f,3.0f,而不是3.14,3var temp = circleArea(3.14f, 3.0f)//调用的时候,明确写出radius=的方式,就是具名参数var temp2 = circleArea(radius = 3.0f)println(temp)println(temp2)
}//圆的面积
//默认Pi的值是PI
//通过在类型的后面添加 = 符号来设置参数的默认值。
fun circleArea(Pi: Float = PI, radius: Float): Float{return Pi*radius*2
}
- 如果最后一个参数是方法,那么,它既可以作为具名参数在括号内传入,也可以在括号外传入
举个例子
fun main(args: Array<String>){//作为具名参数,在括号内read1(1,2, action = {//最后一句可以省略return"123"})//放在括号外read1(start = 3){"321"}
}//最后一个参数是方法
//action:参数名
//()->String:没有参数,有返回值(String)的函数类型
fun read1(offset: Int = 0, start: Int, action: ()->String){//函数(),代表执行函数var temp = action()println("offset=${offset}, start=${start}, action=${temp}")
}
可变数量的参数Vararg
vararg:可变参数
方法的参数(通常是最后一个),可以使用vararg修饰标记:
fun main(args: Array<String>){varargFun(1, 100)varargFun(1, 3, 3,4,8)
}//使用vararg修饰
fun varargFun(a: Int, vararg v:Int){//v参数是一个数组println(v.forEach { println(it) })
}
- 一个函数,最多只能有一个可变参数
- 如果可变参数不是最后一个,那么可变参数后面的所有参数,都要用具名参数传值
Lambda表达式
Lambda表达式理解为一种语法糖
Lambda表达式里面有一个it
it不是关键字
在高阶方法中,如果Lambda表达式只有一个参数,那么可以使用it来使用此参数
异常捕获try-catch
举个🌰:计算器
fun main(args: Array<String>){println("请输入第一个数字:")var a = readLine()while (a == null || a?.count() == 0){println("不能输入空值,请重新输入数字:")a = readLine()}println("请输入第二个数字:")var b = readLine()while (b == null || b?.count()==0){println("不能输入空值,请重新输入数字:")b = readLine()}try {var num1 = a!!.toInt()var num2 = b!!.toInt()var result = sum(num1, num2)println("$num1 + $num2 = $result")}catch (error: java.lang.Exception){println(error)}
}fun sum(a: Int, b: Int):Int{return a+b
}
递归
举个例子:计算n的阶层
5的阶层 = 5 * 4 * 3 * 2 * 1 = 120
fun main(args: Array<String>){println(jieCeng(5))
}fun jieCeng(a: Int):Int{//结束条件if (a == 1) return 1//递归计算return a * jieCeng(a-1)
}
尾递归计算
fun main(args: Array<String>){var result = 0println(add(100, result))
}tailrec fun add(a: Int, result: Int):Int{println("计算机第${a}次计算, result=${result}")//结束条件if (a == 0) return 1//递归计算return add(a-1, result + a)
}
继承
- 如果想继承某个父类,则父类class前需要加
open
,允许被继承 - 如果想重写父类的某个方法,则子类在同名方法前加
overrid
,且父类在方法前加open
,允许被重写
接口和抽象类
- 接口是事物的能力,接口用的时候:接口名即可
- 抽象类是事物的本质,抽象类用的时候:抽象类+()
使abstract定义的类,被称为抽象类,里面的方法不需要实现
代理和委托
A委托B去做某事情
B代理A去做某事情
使用by 类名A()
,就可以实现类名A里面的方法
open class Father: IWashBowl by Son(){/**override fun wash() {println("我是爸爸,洗完一次10元")}* */}//或者这样调用
open class Father: IWashBowl by Son(){override fun wash() {println("我是爸爸,我收了妈妈的10元")Son().wash()println("我是爸爸,我不洗碗,但是赚了9元")}
}
单例
创建类的时候,不使用class修饰,而且使用object修饰,则只有一份
印章类sealed
sealed:封闭的、密封的
- sealed class更在意类型
- 枚举更在意数据
//跟enum类似,只有指定有限的类
sealed class Son {//记得加()class XXL(): Son()class XXM(): Son()fun sayHello(){println("hello")}
}fun main(args: Array<String>){//记得加(),才是对象var son1:Son = Son.XXL()var son2:Son = Son.XXM()var son3:Son = Son.XXM()var house = listOf<Son>(son1, son2, son3)for (v in house){if(v is Son.XXM){v.sayHello()}}
}
函数式编程
fun main(args: Array<String>){var names = listOf<String>("tom", "locy", "jack")//forEach函数names.forEach(action = haha)//闭包,匿名函数names.forEach{a -> println(a)}//闭包names.forEach{//默认参数itprintln(it)}
}//函数作为参数,前面使用var 参数名 =
//后面的函数名去掉
var haha = fun (name: String):Unit{println(name)
}
Kotlin高阶函数
一个函数,被用作参数或者返回值,则称为高阶函数
maxBy/minBy
找出数组array,里面age最大的item
array.maxBy{a.age}
找出数组array,里面height最小的item
array.minBy{a.height}
返回的是item对象
var item: Object = array.maxBy{it.age}
filter
- 过滤找到特定条件的对象集合
好几个条件并列执行:
var newArray = array.filter{(it.age>18) and (it.height>168) and (it.age<25)
}
map
- 把原数组某个属性,映射成新的集合
比如:把人群中的名字列出来,放入新的集合中
var newArray = oldArray.map{"${it.name} : ${it.age}"
}
结果:{name1: 18, name2: 30, name3: 19}
any
- 是否有满足某个条件的对象
返回的是一个boolean值
var temp: Boolean = array.any{it.age == 18
}
count
- 统计符合满足条件对象的个数
var count: Int = array.count{it.age<18
}
find
- 查找符合条件的对象,并返回第一个
var item: Object = array.finc{it.address=="北京"
}
groupBy
- 按照特征,把对象分为不同的组
var result: Array = array.groupBy{it.address
}
按照地址,把原来的数组,分别组成新的数组
DSL 领域特定语言
- 扩展函数
- 中缀表达式
函数使用infix
修饰后,.的操作,可以换成空格的操作
泛型
在java中,常见的泛型有:泛型类、泛型接口、泛型方法、泛型属性
在kotlin中,常见的泛型有:泛型接口/类、泛型字段、泛型方法、泛型约束、泛型中的out与in
泛型接口/类
举个例子:使用泛型定义一个接口
fun main(args: Array<String>){DrinkApple().drink("喝苹果汁")
}//接口,<T>泛型
interface Drink<T>{//不需要函数体fun drink(t: T)
}//定义一个类DrinkApple,实现Drink接口
class DrinkApple: Drink<String>{//使用override重写override fun drink(t: String) {println(t)}
}
泛型类
fun main(args: Array<String>){BlueColor("蓝色").printColor()BlueColor("红色").printColor2()
}//使用abstract修饰一个类,类名为Color
abstract class Color<T>(var t: T){//如果不实现函数,则也加abstractabstract fun printColor()//或者不加abstract,直接实现函数fun printColor2(){println("printColor2")}
}class BlueColor(var color: String): Color<String>(color){override fun printColor() {println("${color}")}
}
泛型参数
fun main(args: Array<String>){var box1 = Box<String>("123")println("${box1.value}")
}///(Box<T>)是一个整体,且不能省略<T>(省略的话,后面的T不识别)
class Box<T>(t: T){var value = t
}
泛型方法
这个。。。。,方法也是泛型?
类型参数要放在方法名的前面
fun main(args: Array<String>){//Kotlin中双冒号操作符:表示把一个方法当做一个参数,传递到另一个方法中进行使用,通俗的来讲就是引用一个方法。fromJson("{}", String::class.java)
}//在函数名前加<T>
fun <T> fromJson(json: String, tClass: Class<T>): T?{var t: T? = tClass.newInstance()return t
}
泛型约束
就是,对泛型进行约束,约束泛型是哪种类型的子类
//规定,泛型必须是JSONObject类型,或者其子类类型
fun <T: JSONObject> fromJson(json: String, tClass: Class<T>): T?{var t: T? = tClass.newInstance()return t
}
当泛型前后不一致的时候,需要借助out/in
扩展Extensions
扩展的好处:提高编码效率,减少代码量
扩展某个类一个新的功能,而无需继承该类
扩展一个方法:
fun main(args: Array<String>){Jump().test()Jump().test2()
}//正常创建一个类
class Jump{fun test(){println("test")}
}//扩展一个类的方法,只需要在类后面加.即可
fun Jump.test2(){println("test2")
}
Jump类,里面多了一个test2()方法
使用场景:当引用的第三方框架,想增加新的方法,就可以使用扩展
扩展属性
fun main(args: Array<String>){var android = "android"println("${android.lastChar}")
}//普通定义变量 var a: Char
//get() = this.get(length - 1)左边是get放,右边是实现
val String.lastChar:Char get() = this.get(length - 1)
Kotlin中常用的扩展
提供的扩展函数:
- let
- run
- apply
let
let: 作用域区域、避免判空
fun main(args: Array<String>){testLet("sss")testLet(null)
}//String?代表可能为空
fun testLet(str: String?){//当str为空的话,不执行后面的函数str?.let {var str2 = "android"println(str2 + it)}//let函数外面,不能访问str2
// str2
}
run
run函数,只接收一个lambda函数参数,以闭包形式返回,返回值为最后一行的值或者指定的return的表达式
在run函数中,可以直接访问实例的公有属性和方法
fun main(args: Array<String>){//调用函数,入参是Jump()var temp = testRun(Jump())println(temp)
}//创建一个类
class Jump{var a: Int = 10fun test(){}
}//函数
fun testRun(jump: Jump): String{jump.run {jump.test()//直接访问方法test()println(jump.a)//直接访问属性println(a)//最后一行,被作为返回值return "1111"}
}
apply
调用某个对象的apply函数,在函数范围内,可以调用该对象的任意方法,并返回该对象
作业:做一个计算器
package com.example.learnKotlinfun main(args: Array<String>){while (true){println("====请输入你的表达式====")var inputString: String? = readln()try {inputString?.let {calculate(inputString)println("====是否还继续?(y/n)====")var inputString2: String? = readln()inputString2?.let {if (it.equals("n")){System.exit(-1)}}}}catch (ex: java.lang.Exception){ex.printStackTrace()}}
}fun calculate(inputString: String){if(inputString.contains("+")){//trim()去掉空格//split()分割var nums = inputString.trim().split("+")var result = nums[0].toFloat() + nums[1].toFloat()println("${nums[0]}+${nums[1]}=${result}")}else if(inputString.contains("-")){var nums = inputString.trim().split("-")var result = nums[0].toFloat() - nums[1].toFloat()println("${nums[0]}-${nums[1]}=${result}")}else if(inputString.contains("*")){var nums = inputString.trim().split("*")var result = nums[0].toFloat() * nums[1].toFloat()println("${nums[0]}*${nums[1]}=${result}")}else if(inputString.contains("/")){var nums = inputString.trim().split("/")var result = nums[0].toFloat() / nums[1].toFloat()println("${nums[0]}/${nums[1]}=${result}")}else{println("输入错误")}
}
测试数据:
请输入你的表达式
8=0
输入错误
是否还继续?(y/n)
y
请输入你的表达式
8 * 8
8 * 8=64.0
是否还继续?(y/n)
请输入你的表达式
9-000
9-000=9.0