一、数值类型
(1)Kotlin中没有基本数据类型,都是对象,数值类型也是对象。
类型 | 位宽度 | 字节数 | 事例 | 范围 |
Byte | 8 | 1 | 1 | -128~127 |
Short | 16 | 2 | 2 | -32768~32767 |
Int | 32 | 4 | 123 | -2147483648~2147483647 |
Float | 32 | 4 | 123.5f | |
Long | 64 | 8 | 123L | |
Double | 64 | 8 | 123.5 |
注: 与java不同的是字符(char)不属于数值类型
2进制以0b开头:0b00001011
16进制以0x开头 :0x0F
8进制不支持
(2)基础数据类型的转换
由于不同的标识方式,较小类型并不是较大类型的子类型,较小的类型不能隐式转换为较大的类型
var a:Int=1
var b:Long=a //报错 request Long found Int
//正确的写法是显示转换
val b:Long=a.toLong();
每种数据类型都有下面的这些方法:
toByte() Byte
toShort() Short
toInt() Int
toLong() Long
toFloat() Float
toDouble() Double
toChar() Char
toString() String
自动类型转换
var a=1L
var b=a+100 //101 b为Long类型
(3)数字装箱,Int?标示可空引用时,不一定保留统一性(===比较内存地址 ==比较大小)
//情况一
var a:Int=1000
println(a===a) //true
//装箱,创建两个不同的对象(内存不一样,值一样)
val boxedA:Int?=a
val boxedAnotherA:Int?=a
println(boxedA===boxedAnotherA) //false
println(boxedA==boxedAnotherAB) //true
//情况二
var b:Int=5;
val boxedB:Int?=b
val boxedAnotherB:Int?=b
println(boxedB===boxedAnotherB) //true
println(boxedA==boxedAnotherB) //true
注:数字装箱值在-128到127之间时,是从IntegerCache.cache中拿的,而不在该范围则是每次创建一个Integer对象,所以上面的结果返回的不一致,不一定保留统一性
(4)位操作符
对于Int和Long类型,还有一系列的位操作符
shl(bits) – 左移位 (Java’s <<)
shr(bits) – 右移位 (Java’s >>)
ushr(bits) – 无符号右移位 (Java’s >>>)
and(bits) – 与
or(bits) – 或
xor(bits) – 异或
inv() – 反向
事例:
val a = 60
val b = 13
var c = -5
println(a and b) // 与 12
println(a or b) // 或 61
println(a xor b) // 异或 49
println(a.inv()) // 按位取反 -61
println(a.shl(1)) // 左移 120
println(a.shr(1)) // 右移 30
println(a.ushr(1)) // 无符号右移 30
二、字符
Kotlin中的字符用Char标示,必须是单引号" ' "包含起来的,不能直接与数字操作,如'0','a'
fun check(c:Char){
if(c==1){//错误,类型不兼容
}
}
fun decimalDigitValue(c: Char): Int {
if (c !in '0'..'9')
throw IllegalArgumentException("Out of range")
return c.toInt() - '0'.toInt() // 显式转换为数字
}
当需要可空引用时,像数字、字符会被装箱,装箱操作不一定会保留同一性。
三、布尔
布尔用Boolean类型表示 ,它有两个值:true和false
若需要可空引用布尔会被装箱
内置的布尔运算有:
|| | 短路逻辑或 |
&& | 短路逻辑与 |
! | 逻辑非 |
四、字符串
- 字符串是不可变的,方括号[ ]语法可以很不方便的获取字符串的某个字符
- 遍历字符串
var a="abc"
for(c in a){
println(c)
}
- 多行字符串 ,使用三个引号(""" """)
fun main(args: Array<String>) {
val text = """
多行字符串
多行字符串
""".trimMargin()
println(text) // 输出有一些前置空格,trimMargin方法会删除多余的空格
}
- 字符串模板$
var a="abc"
println("a=$a")//a=abc
println("$a.length is${a.length}")//abc.length is3
五、list
(1)list的创建
- 只读集合listOf
- 可变集合mutableListOf arrayListOf
- ArrayList
//不可变
var list=listOf("a","b")
//可变方式1
var list=mutableListOf("a","b")
list.add("ccc");
//可变方式2
var list=arrayListOf("a")
//可变方式3
var list= ArrayList<String>(0);
//不可变到可变
var list=listOf("aaa")
var list2=list.toMutableList()
(2)list遍历
- Iterator
var it=list2.iterator()
while(it.hasNext()){
println(it.next())
}
- for in循环
//通过下标
for(index in list2.indices){
println(list2[index])
}
//直接通过元素
for(item in list2){
println(item)
}
- withIndex()
for((index,value) in list2.withIndex()){
println("$index,$value")
}
- forEach
list2.forEach{
println(it)
}
六、Set
(1)Set的创建
- 只读集合 set setOf
- 可变集合MutableSet mutableSetOf
//不可变
var set1=setOf("aaa","bbb")
//可变
var set3=mutableSetOf("aaaa")
(2)set遍历
- for in
for(item in set2){
println(item)
}
- iterator
var it=set2.iterator()
while(it.hasNext()){
println(it.next())
}
- forEach
list2.forEach{
println(it)
}
七、Map
(1)map的创建
- 只读集合mapOf
- 可变集合mutableMapOf
//to方式
var map1=mapOf("key1" to "value1","key2" to "value2")
//Pair方式
var map3= mapOf(Pair("key1","value1"),Pair("key2","value2"))
var map4 = mutableMapOf(Pair("key1","value1"),Pair("key2","value2"))
(2)map遍历
- for in
for(item in map3){
println(item.key+""+item.value)
}
- iterator
var its = map3.iterator()
while (its.hasNext()) {
val item = its.next()
println(item.key + "=====" + item.value)
}
- forEach
map3.forEach{
key,value->println(key + "=====" + value)
}
八、运算
(1)数值运算
- 相等性检测 a==b a!=b
- 比较操作符 a<b a>b a<=b a>=b
- 检测数字是否在指定区间内,区间格式为 a..b 用法: x in a..b a !in a..b
val x=0
val a:Int=1
val b:Int=2
println(a!=b) //true
println(a>b) //false
println(x in a..b) //false
九、控制流
(1) if表达式
// 传统用法
var max = a
if (a < b) max = b
// 使用 else
var max: Int
if (a > b) {
max = a
} else {
max = b
}
// 作为表达式
val max = if (a > b) a else b
//可以把 IF 表达式的结果赋值给一个变量
val max = if (a > b) {
print("Choose a")
a
} else {
print("Choose b")
b
}
(2)when表达式(类似java的switch)
when(a){
1->println("aaa")
2->println("bbbb")
else->{
println("ddd")
}
}
注:在when中,else同switch的defalult
- 很多分支需要相同的方式处理,可以把多个分支条件放在一起,用逗号分隔
when(a){
1->println("aaa")
2,3->println("bbbb")
else->{
println("ddd")
}
}
- 分支是任意表达式
when(a){
a-b->println("aaa") //a-b这的表达式结果还是when中参数a的类型
else->{
println("ddd")
}
}
- in或者!in
when(a){
in 1..10->println("aaa")
else->{
println("ddd")
}
}
- 检测一个值是(is)或者不是(!is)一个特定类型的值
fun hasPrefix(x: Any) = when(x) {
is String -> x.startsWith("prefix")
else -> false
}
(3)for循环
(4)while do...while
do...while循环至少会执行一次
while (x > 0) {
println( x--)
}
println("----do...while 使用-----")
var y = 5
do {
println(y--)
} whi
(5)break和continue、return
return 默认从最直接包围它的函数或者匿名函数返回
break 终止最直接包围它的循环
continue 继续洗一次最直接包围它的循环
(6)break和continue标签
在Kotlin中任何表达式都可以用标签label来标记,格式:表示符@,如abc@
loop@ for (i in 1..100) {
for (j in 1..100) {
if (……) break@loop
}
}
十、开发基础
(1)包声明,引入类
代码文件开头一般为包的声明:
package com.example.ktolindemo
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
(2)注释
// 这是一个单行注释
/* 这是一个多行的
块注释。 */
(3)常量与变量
- var <标识符> :<类型>=<初始值>
- val <标识符> : <类型> = <初始化值> val关键字申明的常量只能赋值一次
- 变量和常量是没有默认值的,通过赋值就可以确定它类型,类型一旦确定这个类型就不可变;
- 变量和常量在成员上定义时必须初始化,在方法中可以先定义,然后初始化,但是在使用前必须初始化
- 要是变量持有null值,它必须可为null类型,需要在变量后面加上?后缀,将变量指定为可为null
//成员申明
var num:Int=2 //指定数据类型,不能赋值为其他类型
var n=2 //根据=后面的值自动推断类型为Int,不能赋值为其他类型
fun main(){
var n:Int //局部申明,先定义,后面再初始化
n=1
}
var num:String=null //这是错误的
var num:String? //申明可以null类型,对num进行操作时要注意其为null的时候
(4)NULL检查机制
fun main(str :String?){ //申明变量str可以null
//对str进行操作
情况一: if(str==null){} //判断为nul的情况
情况二: str!!.toInt() //使用!!断言运算符 如果!!左侧表达式结果为null,也就是这里str为null,则会抛出空指针异常,程序奔溃
情况三: str?.toInt() //使用?符号,如果str为null,则后面的函数不会执行,也不会报错,返回null
情况四:val ages2 = age?.toInt() ?: -1 //使用Elvis运算符?: 因此如果age为null,那么age2的结果为-1
}
- ?运算符在申明变量时表示变量可以为null,在使用变量时加?表示当变量为null时,运算符后面的表达式不会运行,也不会报错,变量值为null
- !!断言运算符 如果!!左侧表达式结果为null,那么会抛出空指针异常,结束进程
- ?: 使用Elvis运算符,如果有运算符左侧的表达式为null,那么使用运算符右的值
(5)在activity和fragment中初始化view控件,需要先申明,不能总是使用以下方式
class LoginFragment : Fragment() {
private var statusTextView: TextView? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
statusTextView = view.findViewById(R.id.status_text_view)
statusTextView?.setText(R.string.auth_failed)
}
}
所以可以使用lateinit关键词
class LoginFragment : Fragment() {
private lateinit var statusTextView: TextView
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
statusTextView = view.findViewById(R.id.status_text_view)
statusTextView.setText(R.string.auth_failed)
}
}
使用lateinit关键字修饰的变量,在外界不能直接判断其是否为null
class Person(name:String,age:Int) {
lateinit var num:String
fun numNULL():Boolean{
return ::num.isInitialized
}
}
fun main(){
var person=Person("测试",11)
if(person.numNUll){
}
}
注:Byte,Short,Int,Long,Float,Double类型变量用lateinit会报错
(6)属性
在Kotlin定义一个普通属性时,会为该属性生成一个field,gettter和setter,这个field称谓幕后字段,getter和setter在
谁后面就是谁
var(val) 属性名称:属性类型 = 属性初始值
[<getter>]
[<setter>]
var name:String=""
set(value) {}
get
val name:String=""
get
Kotlin自动生成了 setter |getter 产生幕后字段
重写了 setter |getter 不产生幕后字段
在重写了的 setter |getter中使用了field 产生幕后字段
- 自定义
var num: String? = null
get() {return field} //field是指当前变量
set(value) {
field = value
}
//自定义访问修饰符
private val age: Int=12
private get
十一、函数
- 函数定义
fun fun_name(参数名 :参数类型;参数名:参数类型) : 返回值类型{
有返回值时用 return ;
}
//无返回值使用Unit,可以省略不写,函数体中也不用写return
fun fun(a:Int,b:Int):Unit{
println(a+b);
}
fun fun(a:Int,b:Int){
println(a+b);
}
//返回值类型为Int类型
fun funInt(a:Int,b:Int):Int{
return a+b;
}
- 当函数体返回单个表达式的结果时直接使用表达式,可以将return关键字替换为赋值运算符,返回结果可以显示申明,也可以由系统自动推断
fun fun1(a:Int,b:Int):Int=a+b
fun fun1(a:Int,b:Int)=a+b
fun generateAnswerString(countThreshold: Int): String = if (count > countThreshold) {
"I have the answer"
} else {
"The answer eludes me"
}
- 函数参数可变,使用varargs关键字
fun <T> asList(vararg ts:T):List<T>{
var result=ArrayList<T>()
for(t in ts){
result.add(t)
}
return result
}
val list= asList(1,2,3);
- lambda(匿名函数)
//定义匿名函数 参数为连个Int类型,返回值也为Int类型
val sumLambda: (Int, Int) -> Int = {x,y -> x+y}
println(sumLambda(1,2)) // 输出 3
高级函数
一个函数将另一个函数作为参数,那么这个函数就是告高阶函数
//返回值是Int类型
//一个参数为String 另一个参数为函数(mapper为函数参数名,函数参数为String,结果为Int,函数体需要调用者写)
fun stringMapper(str: String, mapper: (String) -> Int): Int {
// Invoke function
return mapper(str)
}
//调用
stringMapper("Android", { input ->
input.length
})
十二、类
(1)类定义和用法
//有结构体的类
class Test{
}
//当类没有结构体的时候,大括号可以省略
class Test
//对象的创建
var test=Test()
注:对象的创建与java语法不一样,不用new
(2)类的主构造函数
- 一个类可以有一个主构造函数,主构造函数是类头的一部分,如果主构造函数没有注解或者可见性修饰符,constructor关键字可以省略;主构造函数中声明的参数,他们默认为类的共有字段,可以直接使用,如果不希望被别的类访问到这个变量,可以用private修饰
class Person constructor(name:String){
var name1:String?=name //方式1,直接赋值
var name2:String?=null //方式2,在init块中赋值
init{ //主构造函数传的参数在init中初始化
this.name2=name
}
}
//省略constructor
class Person(name String){
}
//主构造函数有注解或可见性修饰符,这个constructor关键词是必修的,并且修饰符在它前面
class Person public @Inject constructor(name :String){
}
- 主构造函数没有方法体,如果有额外的代码需要在构造方法中执行,需要放到init代码块(多个init代码块,顺序执行)中执行
class Person(var name:String) {
init{
}
}
(3)类的次级构造函数
- 类的次级构造函数需要写在类体中,
- 如果类有主构造函数,每个次构造函数需要委托给主构造函数,可以直接委托或者通过别的次构造函数间接委托
//类有主构造函数
class Person(name:String){
constructor(name:String, parent :String):this(name){
}
}
- 如果类有次级构造函数,没有主构造函数(实际上系统为他创建了空参的主构造函数),一起按自定义主构造函数来处理。
//情况一:没有主构造函数
class Person{
constructor(name :String):this(){
}
}
- 如果一个非抽象类没有声明构造函数(主构造函数或次构造函数),它会产生一个没有参数的构造函数。构造函数是public。如果你不想你的类有公共的构造函数,你就得声明一个空的主构造函数
(5)继承
Kotlin中所有类都继承Any类,它是所有类的超类,对于没有超类型声明的类是默认超类
Any默认提供了三个函数: equals() hashCode() toString()
- 类被继承,一个类想被继承,要使用open关键词进行修饰
open class Father{
}
- 属性重写 使用override,属性必须具有兼容类型,每一个声明的属性都可以通过初始化程序或者getter方法被重写。
open class Person {
open var x:Int
}
class Student : Person{
override var x:Int
}
- 函数覆写,在基类中,函数默认为final修饰,不能被子类重写,如果允许子类重写该函数,需要添加关键字open修饰它,子类重写方法使用关键词override
open class Person {
open fun action(){
}
}
class Student : Person{
override fun action(){
}
}
(6)抽象类
abstract class Person{
val TAG = this.javaClass.simpleName // 自身的属性
// 自身的函数
fun test(){
// exp
}
abstract var name : String // 抽象属性
abstract fun init() // 抽象方法
}
注:抽象类本身具有普通类特性,以及组成部分,抽象成员只有定义没有实现,都是被abstract修饰符修饰
抽象类不能被直接实例化
其抽象子类必须全部重写带abstract修饰的属性和方法
十三、接口
(1)接口的声明和实现 interface
interface IPerson{
fun cry()
}
十四、委托和代理
十五、Lambda
(1)Lambda表达式的特点
- Lambda表达式总是被大括号扩着
- 其参数(如果存在)在 -----> 之前声明,参数类型可以省略
- 函数体(如果存在)在 ------>后面
(2)语法
- 无参数的情况
var/val 变量名={操作的代码}
// 源代码
fun test(){ println("无参数") }
// lambda代码
val test = { println("无参数") }
// 调用
test() => 结果为:无参数
- 有参数的情况
var/val 变量名:(参数类型,参数类型,...) -> 返回值类型={参数1,参数2,...->操作参数的代码}
等价于(表达式的返回值类型会根据操作的代码自推到出来)
var/val 变量名={参数1:类型,参数2:类型,...->操作参数的代码}
// 源代码
fun test(a : Int , b : Int) : Int{
return a + b
}
// lambda
val test : (Int , Int) -> Int = {a , b -> a + b}
// 或者
val test = {a : Int , b : Int -> a + b}
// 调用
test(3,5) => 结果为:8
- lambda表达式作为函数中的参数的时候
fun test(a:int,参数名:(参数1:类型,参数2:类型...)->表达式返回类型){
}
// 源代码
fun test(a : Int , b : Int) : Int{
return a + b
}
fun sum(num1 : Int , num2 : Int) : Int{
return num1 + num2
}
// 调用
test(10,sum(3,5)) // 结果为:18
// lambda
fun test(a : Int , b : (num1 : Int , num2 : Int) -> Int) : Int{
return a + b.invoke(3,5)
}
// 调用
test(10,{ num1: Int, num2: Int -> num1 + num2 }) // 结果为:18