安卓开发学习---kotlin版---笔记(一)
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..10
for (num in nums){
print("$num, ")
}
println()
var nums2 = 1 until 10
for (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 10
for (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] = 1
array4[1] = 99
array4.forEach { println(it) }
- 指定类型,指定个数,且每个元素一样
//创建一个长度为5的数组,里面每个元素都是100
var 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,取index
for(index in array6.indices){
println("array6[${index}] = ${array6[index]}")
}
//使用withIndex,取出index和item
for((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.TreeMap
fun main(args: Array<String>){
//创建一个map
var 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.14f
fun main(args: Array<String>){
//要写3.14f,3.0f,而不是3.14,3
var 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 = 0
println(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{
//默认参数it
println(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){
//如果不实现函数,则也加abstract
abstract 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 = 10
fun test(){
}
}
//函数
fun testRun(jump: Jump): String{
jump.run {
jump.test()
//直接访问方法
test()
println(jump.a)
//直接访问属性
println(a)
//最后一行,被作为返回值
return "1111"
}
}
apply
调用某个对象的apply函数,在函数范围内,可以调用该对象的任意方法,并返回该对象
作业:做一个计算器
package com.example.learnKotlin
fun 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