在2019年的Google 1/0大会上Kotlin被选为Android开发首选语言
Kotlin文件会被Kotlin 编译器编译成Java字节码文件,字节码文件会被jar工具打包成jar包,最终会被各平台的打包工具输出成我们的应用程序
Byte、Short、Int、Long、Float、Double
数组的创建
// arrayOf
val array: Array = arrayOf(1, 2, 3)
// arrayOfNulls
val array1: Array = arrayOfNulls(3)
array1[0] = 4
array1[1] = 5
array1[2] = 6
// Array(5)的构造函数
val array2: Array = Array(5) { i ->
(i * i).toString()
}
// intArrayOf(), doubleArrayOf()
val x: IntArray = intArrayOf(1, 2, 3)
println("x[0] + x[1] = ${x[0] + x[1]}")
// 大小为5,值为【0,0,0,0,0】的整型数组
val array3 = IntArray(5)
// 大小为5,值为【1,1,1,1,1】的整型数组
val array4 = IntArray(5) { 1 }
// 大小为5、值为【0,1,2,3,4】的整形数组(值初始化为其索引值)
val array5 = IntArray(5) { it * 1 }
println(array5[4])
数组的遍历
// 数组遍历
for (item in array) {
println(item)
}
// 带索引遍历数组
for (i in array.indices) {
println("$i -> ${array[i]}")
}
// 遍历元素(带索引)
for ((index, item) in array.withIndex()) {
println("$index -> $item")
}
// forEach遍历数组
array.forEach {
println(it)
}
// forEach增强版
array.forEachIndexed { index, item ->
println("$index -> $item")
}
集合的可变形与不可变性--以mutable为前缀的为可变集合
//不可变集合
val stringList: List = listOf("one", "two", "one")
//可变集合
val numbers: MutableList = mutableListOf(1, 2, 3, 4)
集合排序:
val number3 = mutableListOf(1, 2, 3, 4)
// 随机排序
number3.shuffle()
// 从小到大
number3.sort()
// 从大到小
number3.sortDescending()
// 使用sortBy进行排序,适合单条件排序
lauguageList.sortBy { it.score }
// 使用sortWith进行排序,适合多条件排序
lauguageList.sortWith(compareBy({
it.score
}, { it.name }))
方法声明
//方法申明
fun learn(days: Int): Boolean {
return days > 100
}
fun 方法(参数):返回值{
方法体
}
方法可以直接定义在文件中
package com.lf.testkotlin
fun functionLearn(days: Int): Boolean {
return days > 100
}
成员方法:类().成员方法
fun main() {
Person().test1()
}
class Person {
fun test1() {
println("成员方法")
}
}
静态方法(类方法):使用伴生对象来实现类方法 类.类方法
fun main() {
Person.test2()
}
class Person {
companion object {
fun test2() {
println("companion object 实现类方法")
}
}
}
工具类:Object关键字来修饰,内部所有的方法都是静态的
package com.lf.testkotlin
object NumUtil {
fun double(num: Int): Int {
return num * 2
}
}
fun main() {
NumUtil.double(2)
}
单表达式方法:当方法返回单个表达式时,可以省略花括号并且在 = 之后指定代码体即可
fun double(x: Int): Int = x * 2
参数默认值:方法参数可以有默认值,当省略相应的参数时使用默认值,与其java相比,减少重载数量
fun read(b: Array, off: Int = 0, len: Int = b.size) {
}
可变数量的参数:vararg
fun append(vararg str: Char): String {
val result = StringBuffer()
for (char in str) {
result.append(char)
}
return result.toString()
}
局部方法:在方法的内部创建方法
fun magic(): Int {
fun foo(v: Int): Int {
return v * v
}
val v1 = (0..100).random()
return foo(v1)
}
无参数的情况
val/var 变量名 = { 操作的代码 }
eg:
//源代码
fun test() {
println("无参数")
}
//lambda代码
val test1 = { println("无参数") }
有参数的情况
val/var 变量名 : (参数的类型, 参数类型, ...) -> 返回值类型
= {参数1, 参数2, ... -> 操作参数的代码}
// 等价于,即表达式的返回值类型会根据操作的代码自推导出来。
val/var 变量名 = {参数1: 类型, 参数2: 类型, ... ->
操作参数的代码}
eg:
// 源代码
fun test2(a: Int, b: Int): Int {
return a + b
}
// lambda代码
val test3: (Int, Int) -> Int = { a, b -> a + b }
//或者
val test4 = { a: Int, b: Int -> a + b }
it是在当一个高阶方法中Lambda表达式的参数只有一个的时候可以使用it来使用此参数
// 这里举例一个语言自带的一个高阶方法filter,此方法的作用是过滤掉不满足条件的值
val arr = arrayOf(1, 3, 5, 7, 9)
// 过滤掉数组中元素小于5的元素,取其第一个打印。这里的it就表示每一个元素。
println(arr.filter { it < 5 }.component1())
testClosure(1)(2) {
println(it)
}
在使用Lambda表达式的时候,可以用下划线(_)表示未使用的参数,表示不处理这个参数
val map = mapOf("key1" to "value1", "key2" to "value2", "key3" to "value3")
map.forEach { (key, value) ->
println("$key \t $value")
}
// 不需要key的时候
map.forEach { (_, value) -> println(value) }
函数作为参数
/**
* eg: 实现一个能够对集合元素进行求和的高阶函数,并且每遍历一个集合元素要有回调
*/
fun List.sum(callback: (Int) -> Unit): Int {
var result = 0
for (v in this) {
result += v
callback(v)
}
return result
}
// 调用
val list = listOf(1, 2, 3)
val result = list.sum { println("it:${it}") }
println("${result}")
函数作为返回值
/**
* eg: 实现一个能够对集合元素进行求和的高阶函数,并且返回一个声明为(scale: Int)-> Float的函数
*/
fun List.toIntSum(): (scale: Int) -> Float {
println("第一层函数")
return fun(scale): Float {
var result = 0f
for (v in this) {
result += v.toInt() * scale
}
return result
}
}
// 调用
val listString = listOf("1", "2", "3", "4")
val result2 = listString.toIntSum()(2)
println("计算结果:${result2}")
特性:
好处:
举例:
实现一个接受一个testClosure方法,该方法要接受一个Int类型的v1参数,同时能够返回一个声明为(v2: Int, (Int) -> Unit)的函数,并且这个函数能够计算v1与v2的和。
fun testClosure(v1: Int): (v2: Int, (Int) -> Unit) -> Unit {
return fun(v2: Int, printer: (Int) -> Unit) {
printer(v1 + v2)
}
}
// 调用
testClosure(1)(2) {
println(it)
}
把里面的字段给解构出来
data class Result(val message: String, val code: Int)
fun test11() {
var result = Result("message", 0)
// 解构
val (message, code) = result
println("message:${message} code:${code}")
}
val fun1 = fun(x: Int, y: Int): Int = x + y
fun literal() {
// 定义了一个变量tmp,而该变量的类型就是(Int)-> Boolean
var temp: ((Int) -> Boolean)? = null
// { num -> (num > 10) } 就是方法字面值
temp = { num -> (num > 10) }
println("temp(11):${temp(11)}")
}
/**
* 主构造方法(主构造方法constructer()可以省略)
*/
class KotlinClass constructor(name: String) {
// 次构造方法
constructor(view: View, name: String) : this(name) {
println("name:$name")
}
constructor(view: View, name: String, index: Int) : this(name) {
println("name:$name,index:$index")
}
}
父类必须用open修饰,需要被覆盖的方法也需要open修饰,需要被覆盖的属性也需要open修饰
open class Animal(age: Int) {
init {
println(age)
}
open val foot: Int = 0
open fun eat() {
}
}
class Dog : Animal {
constructor(age: Int) : super(age)
override val foot = 4
override fun eat() {
super.eat()
}
}
//其初始器(initializer)、getter和setter都是可选的。如果属性类型可以从初始器(或者从其getter返回值)中推断出来,也可以省略
var [: ] [ = ]
[]
[]
eg:
val simple: Int? // 类型Int、默认getter、必须在构造方法中初始化
//定义了一个自定义的getter,每次访问该属性时都会调用它。
val isClose: Boolean
get() = Calendar.getInstance().get(Calendar.HOUR_OF_DAY) > 11
//定义了一个自定义的setter,每次给属性赋值时都会调用它
var score: Float = 0.0f
get() = if (field < 0.2f) 0.2f else field * 1.5f
set(value) {
println(value)
}
属性延迟初始化
lateinit var shop: Shop2
fun setup() {
shop = Shop2()
}
fun test() {
// ::表示创建成员引用或类引用
if (::shop.isInitiaized) println(shop.address)
}
/**
* 抽象方法
*/
abstract class Printer {
abstract fun print()
}
class FilePrinter : Printer() {
override fun print() {
}
}
/**
* 接口
*/
interface Study {
val time: Int//抽象的
fun discuss()
fun learnCourses() {
println("Android 架构师")
}
}
class StudyAS(override val time: Int) : Study {
override fun discuss() {
}
}
必须要有至少一个参数,并且不能被定义成open或者抽象的,不能被继承。
/**
* 数据类,可以有自己的类体,包括属性和方法
*/
data class Address(val name: String, val number: Int) {
var city: String = ""
fun print() {
println(city)
}
}
open class Address2(name: String) {
open fun print() {
}
}
class Shop2 {
var address: Address2? = null
fun addAddress(address2: Address2) {
this.address = address2
}
}
fun test3() {
// 如果超类型有一个构造方法,则必须传递适当的构造方法参数给它
Shop2().addAddress(object : Address2("Android") {
override fun print() {
super.print()
}
})
}
fun foo() {
val adHoc = object {
var x: Int = 0
var y: Int = 0
}
println(adHoc.x + adHoc.y)
}
/**
* 对象的声明
*/
object DataUtil {
fun isEmpty(list: ArrayList): Boolean {
return list?.isEmpty()
}
}
class Student(val name: String) {
companion object {
val student = Student("Android")
fun study() {
println("Android 架构师")
}
}
}
fun testStudent() {
println(Student.student)
Student.study()
}
泛型接口
interface Drinks {
fun taste(): T
fun price(t: T)
}
class Sweet {
val price = 5
}
class Coke : Drinks {
override fun taste(): Sweet {
println("Sweet")
return Sweet()
}
override fun price(t: Sweet) {
println("Coke price:${t.price}")
}
}
泛型方法
/**
* 泛型方法
*/
fun fromJson(json: String, tClass: Class): T? {
// 获取t的实例
val t: T? = tClass.newInstance()
return t
}
泛型约束
在kotlin中out代表协变,in代表逆变,为了加深理解我们可以将kotlin的协变看成java的上界通配符,将逆变看成java的下界通配符
//和一般的声明很类似,只是在class前面加上了annotation修饰符
annotation class ApiDoc(val value: String)
@ApiDoc("修饰类")
class Box {
@ApiDoc("修饰字段")
val size = 8
@ApiDoc("修饰方法")
fun test() {
}
}
给注解类加注解
- @Target:定义注解能够应用于哪些目标对象;
- @Retention:注解的保留期;
- @Repeatable:标记的注解可以多次应用于相同的声明或类型;
- @MustBeDocumented:修饰的注解将被文档工具提取到API文档中;
@Target
接受一个vararg可变数量的参数,可以同时指定多个作用的目标对象,参数类型限定为AnnotationTarget
public enum class AnnotationTarget {
CLASS,// 表示作用对象有类、接口、object对象表达式、注解类
ANNOTATION_CLASS,//表示作用对象只有注解类
TYPE_PARAMETER,//表示作用对象是泛型类型参数(暂时还不支持)
PROPERTY,//表示作用对象是属性
FIELD,//表示作用对象是字段,包括属性的幕后字段
LOCAL_VARIABLE,//表示作用对象是局部变量
VALUE_PARAMETER,//表示作用对象是函数或构造函数的参数
CONSTRUCTOR,//表示作用对象是构造函数,主构造函数或次构造函数
FUNCTION,//表示作用对象是函数,不包括构造函数
PROPERTY_GETTER,//表示作用对象是属性的getter函数
PROPERTY_SETTER,//表示作用对象是属性的setter函数
TYPE,//表示作用对象是一个类型,比如类、接口、枚举
EXPRESSION, //表示作用对象是一个表达式
FILE,//表示作用对象是一个File
@SinceKotlin("1.1")
TYPEALIAS//表示作用对象是一个类型别名
}
@Retention
接收一个AnnotationRetention类型的参数,该参数有个默认值,默认是保留在运行时期
@Retention元注解取值主要来源于AnnotationRetention枚举类
public enum class AnnotationRetention {
SOURCE,//源代码时期(SOURCE):注解不会存储在输出class字节码中
BINARY,//编译时期(BINARY):注解会存储在class字节码中,但是对反射不可见
RUNTIME//运行时期(RUNTIME):注解会存储在class字节码中,也会对反射可见
}
//交换列表中的两个元素的位置
fun MutableList.swap(index1: Int, index2: Int) {
val temp = this[index1]
this[index1] = this[index2]
this[index2] = temp
}
//泛型扩展方法
fun MutableList.swap2(index1: Int, index2: Int) {
val temp = this[index1]
this[index1] = this[index2]
this[index2] = temp
}
//获取字符串中的最后一个元素
fun String.lastChar():Char = this.get(this.length - 1)
class Jump {
companion object {}
}
fun Jump.Companion.print(str: String) {
println(str)
}
let
//原型
fun T.let(f: (T) -> R): R = f(this)
//举例
fun testLet(str: String?) {
// 避免为null的操作
str?.let {
println(it.length)
}
//限制作用域
str.let {
val str2 = "let作用域"
println(it + str2)
}
}
run
只接收一个lambda函数为参数,以闭包形式返回,返回值为最后一行的值或者指定的return的表达式,在run函数中可以直接访问实例的公有属性和方法。
//原型
fun T.run(f: T.() -> R): R = f()
//举例
data class Room(val address: String, val price: String, val size: Float)
fun testRun(room: Room) {
room.run {
println("Room:$address,$price,$size")
}
}
apply
调用某对象的apply函数,在函数范围内,可以任意调用该对象的任意方法,并返回该对象。
//原型
fun T.apply(f: T.() -> Unit): T { f(); return this }
//举例
fun testApply() {
ArrayList().apply {
add("1")
add("2")
add("3")
}.let {
println(it)
}
}
从结构上来看apply函数和run函数很像,唯一不同点就是它们各自返回的值不一样,run函数是以闭包形式返回最后一行代码的值,而apply函数的返回的是传入对象的本身。
使用Kotlin扩展为控件绑定监听器减少模版代码
//为Activity添加find扩展方法,用于通过资源id获取控件
fun Activity.find(@IdRes id: Int): T {
return findViewById(id)
}
//为Int添加onClick扩展方法,用于为资源id对应的控件添加onclick监听
fun Int.onClick(activity: Activity, click: () -> Unit) {
activity.find(this).apply {
setOnClickListener {
click
}
}
}
//使用
val textView = find(R.id.text)
R.id.text.onClick(this) {
textView.text = "kotlin扩展"
}
//使用方法
//1.gradle中引入插件:
apply plugin: 'kotlin-android-extensions'
//2.在代码中导入:
import kotlinx.android.synthetic.main.<布局>.*
//3.若需要调用View的合成属性,同时还应该导入
import kotlinx.android.synthetic.main.view.*
//4.最后可以通过控件id来访问这些控件了
//java
public class CustomView extends FrameLayout {
public CustomView(@NonNull Context context) {
super(context);
}
public CustomView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public CustomView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
//kotlin
class CustomKotlinView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
}