小白学Kotlin,认真做笔记,认真做总结,
感谢:
https://blog.csdn.net/sino_crazy_snail/article/category/7450715/1?
https://www.jianshu.com/nb/2528012
https://www.cnblogs.com/Jetictors/p/9227498.html#
首先感谢三位博主的精彩文章,我看其他博客习惯把参考博客挂在自己博文最下面,小白觉得不太合适,对老师得足够尊重,所以在前面首先表达对三位老师的感谢!
目录
2.1.2.public、protected、private、internal
2.3.3.变量可空 ?(修饰符 变量名 : 类型? = 值)
2.5.12.try-with-resource语法(≥JDK7)
2.7.7.内部类和嵌套类(inner and nested)
2.7.8.对象表达式 and 对象声明(Object Expressions and Declarations)
2.8.2.数组转换为集合toList() / toMutableList()
Kotlin
https://blog.csdn.net/sino_crazy_snail/article/category/7450715/1?
https://www.jianshu.com/nb/2528012
https://www.cnblogs.com/Jetictors/p/9227498.html#
Kotlin是一个基于JVM的新的静态编程语言,由JetBrains开发。已开源!
在2017年5月18日谷歌在I/O开发者大会上宣布,将Kotlin语言作为安卓开发的一级编程语言。已正式成为Android官方开发语言。
kotlin是一种全栈的开发语言,可以编译成Java字节码,也可以编译成JavaScript,方便在没有JVM的设备上运行。可以用它进行开发web、web后端、Android等。
一.Kotlin的特性
1.全面支持Lambda表达式
2.数据类Data classes
3.方法字面量和内联方法Function literals & inline functions
4.方法扩展Extension functions
5.空安全Null safety
6.智能转换Smart casts
7.字符串模板String templates
8.主构造方法Primary constructors
9.类委托Class delegation
10.类型推判Type inference
11.单例Singletons
12.声明点变量Declaration-site variance
13.区间表达式Range expressions
二.Kotlin的优势
1.Kotlin更加易表现(可以少写很多代码)。
2.Kotlin兼容Java的语言,Kotlin可与Java语言无缝通信。这意味着我们可以在Kotlin代码中使用任何已有的Java库;同样的Kotlin代码还可以为Java代码所用。
3.Kotlin比Java更简洁且更安全:
(1)通过支持variable type inference,higher-order functions (closures),extension functions,mixins and first-class delegation等实现。
(2)在代码中很少需要在代码中指定类型,因为编译器可以在绝大多数情况下推断出变量或是方法返回值的类型。
4.Kotlin默认访问权限public
5.Kotlin对比Java:
(1)Java中,类名必须和.Java文件相同;在kotlin中,同一个文件可以写很多个类,并且名字可以是任意的。
(2)Java中,package声明路径和文件夹路径一致;Kotlin中,package声明路径不等价于文件夹路径,源文件可以放在项目的任何位置,kotlin中每行代码结束不需要分号。
三.Kotlin的语法
2.1.Kotlin修饰符
2.1.1.final、open、abstract
(1)final 不能被重写 在kotlin中默认所有的class和method都是final属性
(2)open 可以被重写 需要被明确指出
(3)abstract 必须要重写 不能被实例化,默认具有open属性。
(4)override 重写超类的方法 如果没有被指定为final,则默认具有open属性
备注:
(1)kotlin中,abstract的用法同Java,且abstract修饰符默认同时具有open属性!!!
(2)接口类默认open,接口定义的方法默认为abstract,且接口类、方法不能被修饰为final,所以接口类基本不会用open,final,abstract修饰
2.1.2.public、protected、private、internal
1.包(top-level):
直接在包内(脱离类),定义方法,属性,类,对象,接口 …… 与class同级,在编译成class的时候,会把Top-level的属性和方法创建到以类名+Kt为名的class文件中。
(1)public:公有。默认修饰符,被其修饰的在任何位置都能访问
(2)private:私有。只能在当前源文件内使用
(3)internal:模块。在同一模块内使用
(4)protected:私有 + 子类拥有。无效修饰符,只用于类和接口内
package demo.kotlindemo
private fun privateFun() {} // 只在 xx.kt 文件内可访问
public var publicParam: Int = 5 // 这个属性在任何地方都可以访问
internal val internalParam = 6 // 在同一个模块(module)内可以访问
2.类与接口
(1)public:共有。默认修饰符,被其修饰的在任何位置都能访问
(2)private:私有。只在这个类之内可以访问
(3)internal:模块。在同一模块内使用
(4)protected:私有 + 子类拥有。在当前类及其子类内访问
2.2.Kotlin数据类型
Kotlin中一切皆为对象,没有基础数据类型的分类。像Integer,Float或者Boolean等类型全部都会作为对象存在,且与Java非常相似。
2.2.1.数值类型
Type |
Bit width(字宽) |
字面常量 |
Double |
64 |
123.5 |
Float |
32 |
123.5f |
Long |
64 |
123L |
Int |
32 |
123 |
Short |
16 |
0xFF_EC_DE_5E |
Byte |
8 |
0b00001011 |
Kotlin的数值类型字面常量可以使用下划线对数字进行分组,使数字常量更易读。例如: 0xFF_EC_DE_5E,1_000_000
1.数值比较
Kotlin的数值类型都是对象,所以在比较两个数字的时候,就有比较数据大小和比较两个对象是否相同两种情况:
(1)三个等号 === 表示比较对象地址
(2)两个 == 表示比较两个值大小
val a: Int = 10000
a === a // true,值相等,对象地址相等
// 另外创建两个不同的Int对象,都赋值a
val a1: Int? = a
val a2: Int? = a
// 虽然属于不同对象,但是值是相等的,都是10000
a1 === a2 // false,值相等,对象地址不一样
a1 == a2 // true,值相等
2.位运算符
仅适用于Int和Long类型:
shl(bits) – 左移位 (Java’s <<)
shr(bits) – 右移位 (Java’s >>)
ushr(bits) – 无符号右移位 (Java’s >>>)
and(bits) – 与
or(bits) – 或
xor(bits) – 异或
inv() – 反向
例如:
// Java
val bitwiseOr = FLAG1 | FLAG2
val bitwiseAnd = FLAG1 & FLAG2
// Kotlin
val bitwiseOr = FLAG1 or FLAG2
val bitwiseAnd = FLAG1 and FLAG2
3.类型转换
较小的类型不会被隐式转换为更大的类型,故而系统提供了显式转换。
toByte() => 转换为字节型
toShort() => 转换为短整型
toInt() => 转换为整型
toLong() => 转换为长整型
toFloat() => 转换为浮点型
toDouble() => 转换为双精度浮点型
toChar() => 转换为字符型
toString() => 转换为字符串型
例1.数字类型中不会自动转型。你不能给Float变量分配一个Int。必须要做一个明确的类型转换:
var age = 18
var weight:Float = age.toFloat()
例2.字符(Characters)不能直接作为一个数字来处理。必须要做一个明确的类型转换:
val c: Char='c'
val i: Int = c.toInt()
2.4.2.布尔类型(Boolean)
操作符:
' || ' => 逻辑或(或者)
' && ' => 逻辑与(并且)
' ! ' => 逻辑非(取反)
2.4.3.字符型(Char)
字符常量用单引号‘ ’表示,且不能直视为数字
1.类型转换:
var var1 = char1.toByte()
var var2 = char1.toInt()
var var3 = char1.toString()
var var4 = char1.toFloat()
var var5 = char1.toShort()
2.转义字符:
\t => 表示制表符
\n => 表示换行符
\b => 表示退格键(键盘上的Back建)
\r => 表示键盘上的Enter键
\\ => 表示反斜杠
\' => 表示单引号
\" => 表示双引号
\$ => 表示美元符号,如果不转义在kotlin中就表示变量的引用了
2.4.4.字符串型(String)
1.String 遍历:
一个String可以像数组那样访问,并且被迭代:
var s = "charon"
var c = s[2]
for (a in s) {
Log.e("@@@", a +"");
}
2.String 查找
(1)获取首个元素
下标访问
var str = "Kotlin String"
Log.i("TAG", "first = " + str[0])
first()
var str = "Kotlin String"
Log.i("TAG", "first = " + str.first())
first{} 找到第一个符合表达式的字符
var str = "Kotlin String"
Log.i("TAG", "first o = " + str.first {
it == 'o'
})
first() / first{} 如果字符串为空串时,first() 方法会抛出异常
firstOrNull() / firstOrNull{} 如果字符串为空时,firstOrNull() 方法会返回null
find{} 通过操作findOrNull{} 实现
(2)获取最后一个元素
下标访问
var str = "Kotlin String"
Log.i("TAG", "last = " + str[str.length - 1])
last()
var str = "Kotlin String"
Log.i("TAG", " last = " + str.last())
last{} 找到最后一个符合表达式的字符
var str = "Kotlin String"
Log.i("TAG", "last o = " + str.last {
it == 'o'
})
last() / last{} 如果字符串为空串时,last() 方法会抛出异常
lastOrNull() / lastOrNull{} 如果字符串为空时,lastOrNull() 方法会返回null
findLast{} 通过操作lastOrNull{} 实现
(3)查找对应下标元素
indexOf == indexFirstOf
lastIndexOf == indexLastOf
3.String 截取(subString同Java)(subSequence)
subString:
(1)subString(startIndex);
(2)subString(startIndex,endIndex);
(3)subString(rang)
subSequence:
(1)subSequence (startIndex,endIndex);
(3)subSequence (rang)
4.String 替换
(1)replace(oldChar,newChar,ignoreCase = false)
作用:把原字符串中的某一个字符全部替换成新的字符。然后返回新的字符串
参数:
oldChar: 需要替换的字符
newChar: 新的字符
ignoreCase : 是否引用Java中的replace()方法。默认值为false,即用Java的replace()方法
源代码:
public actual fun String.replace(oldChar: Char, newChar: Char, ignoreCase: Boolean = false): String {
if (!ignoreCase)
return (this as java.lang.String).replace(oldChar, newChar)
else
return splitToSequence(oldChar, ignoreCase = ignoreCase).joinToString(separator = newChar.toString())
}
(2)replace(oldValue,newValue,ignoreCase = false) 同(1),知识参数换成了字符串
(3)replace(regex,replacement)
作用:根据定义的正则规则去匹配源字符串,把满足规则的字符串替换成新的字符串。
参数:
regex: 正则表达式
replacement: 新的字符串
(4)replace(regex: Regex, noinline transform: (MatchResult) -> CharSequence)
作用:根据定义的正则规则去匹配源字符串,把满足规则的字符串通过transform{}高阶方法映射的新字符串替换。
参数:
regex: 正则表达式
transform: 高阶方法
str.replace(Regex("[0-9]+ ")) {
"replace"
}
(5)replaceFirst()
作用:把满足条件的第一个字符或字符串替换成新的字符或字符串
(6)replaceBefore()
作用:截取满足条件的 第一个 字符/字符串 后面的字符串,且包含满足条件 字符/字符串 自身,并在其前面加上新的字符串
Demo:
var str = "Kotlin String"
Log.i("TAG", str.replaceBefore('o',"houqiang"))
// logcat: houqiangotlin String
(7)replaceBeforeLast ()
作用:截取满足条件的 最后一个 字符/字符串 后面的字符串,且包含满足条件 字符/字符串 自身,并在其前面加上新的字符串。
(8)replaceAfter()
作用:截取满足条件的 第一个 字符/字符串 前面 的字符串,且包含满足条件 字符/字符串 自身,并在其后面加上新的字符串。
(9)replaceAfterLast()
作用:截取满足条件的 最后一个 字符/字符串 前面 的字符串,且包含满足条件 字符/字符串 自身,并在其后面加上新的字符串。
5.String 分割(split同Java)(splitToSequence)
6.String.count() / String.count{}
(1)String.count():统计字符串长度 return length
(2)String.count{}:统计重复字符个数
var str = "Kotlin String"
Log.i("TAG", "last dddd = " + str.count {
it == 'o'
})
7.String.其他
isEmpty() : 其源码是判断其length是等于0,若等于0则返回true,反之返回false。不能直接用于可空的字符串
isNotEmpty() : 其源码是判断其length是否大于0,若大于0则返回true,反之返回false。不能直接用于可空的字符串
isNullOrEmpty() : 其源码是判断该字符串是否为null或者其length是否等于0。
isBlank() : 其源码是判断其length是否等于0,或者判断其包含的空格数是否等于当前的length。不能直接用于可空的字符串
isNotBlank() : 其源码是对isBlank()方法取反。不能直接用于可空的字符串
isNotOrBlank() : 其源码判断该字符串是否为null。或者调用isBlank()方法
plus():字符串连接str1.plus(str2)
reversed():字符串反转str.reversed()
8.String 模板
Kotlin中,对于字符串变量,如果需要在一个字符串中拼接该字符串变量,需要用到${},大括号内可以是变量,也可以是表达式,方法等
Java中:“the user name is ” + userName
Kotlin中:“the user name is ${userName}”
2.4.5.数组型(Array)
1.arrayOf()
创建一个带有可变类型参数的泛型数组对象
var arr1 = arrayOf(1, 2, 3, 4, 5) // 等价于[1,2,3,4,5]
var arr2 = arrayOf("0", "2", "3", 'a', 32.3f)
var value3 = arr2[3]
var value4 = arr2[4]
// 以下报错:request: Float ... found:Comparable
var valueFloat:Float = arr2[4]
2.arrayOfNulls()
创建一个指定数据类型且指定长度的数组,数组中元素初始值为null
// 创建一个Int型,长度为3的数组,且初始值为null
var arr = arrayOfNulls<Int>(3)
3.工厂方法Array()
第一个参数表示数组元素的个数,第二个参数则为使用其元素index组成的表达式,用于初始化元素值
String型数组:长度5,初始化值:“0”“2”“4”“6”“8”
var arr = Array(5, { it -> (it * 2).toString() })
var arr = Array(5) { it -> (it * 2).toString() }
Float型数组:长度5,初始化值:0f 2f 4f 6f 8f
var arr: Array<Float> = Array(5) { it -> (it * 2).toFloat() }
4.Kotlin数组类型:
ByteArray => 表示字节型数组
ShortArray => 表示短整型数组
IntArray => 表示整型数组
LongArray => 表示长整型数组
BooleanArray => 表示布尔型数组
CharArray => 表示字符型数组
FloatArray => 表示浮点型数组
DoubleArray => 表示双精度浮点型数组
备注:Kotlin中不支持字符串型数组StringArray
2.3.Kotlin变量
2.3.1.变量声明
(1)val:只读变量(类似于java的final类型)一个不可变对象意味着它在实例化之后就不能再去改变它的状态了。
val name: String = "charon"
(2)var:可读可写
var age: Int = 18
(3)Kotlin变量类型可以省略,编译器可以自己去推断出具体的类型:
var age = 18 // int
val name = "charon" // string
var height = 180.5f // flat
var weight = 70.5 // double
var person: Person? = null
备注:在kotlin中,建议如果一个变量除非是有必要被修改时,才声明为var,否则一律生命为val。
2.3.2.变量初始化
一般情况下,局部变量(方法内声明的临时变量)可以只声明,不要求必须做初始化;但是类成员变量声明要求必须初始化,如果不想做初始化,需要使用延迟初始化策略:
1.lateinit
类内声明的属性必须初始化,如果设置非null的属性,应该将此属性在构造器内进行初始化。
假如想在类内声明一个非null属性,在需要时再进行初始化(最典型的就是懒汉式单例模式),与Kotlin的规则是相背的,此时我们可以声明一个属性并延迟其初始化,此属性用lateinit修饰符修饰:
(1)只能用于var变量
(2)不能用于可null变量
(3)不能用于基本数据类型(Int,Float,Double等),String类型是可以的
(4)声明后,必须在访问前进行初始化赋值,否则会抛出异常:UninitializedPropertyAccessException
class MainActivity : AppCompatActivity() {
lateinit var name: String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var test = MainActivity()
// 要先调用方法让其初始化
test.init()
// 再使用其属性
Log.e("@@@", test.name)
}
fun init() {
// 延迟初始化
name = "charon"
}
}
在使用的时候,一定要确保属性是被初始化过的,通常先调用初始化方法,否则会有异常。
如果只是用lateinit声明了,但是还没有调用初始化方法就使用,哪怕你判断了该变量是否为null也是会crash的。
private lateinit var test: String
private fun switchFragment(position: Int) {
if (test == null) {
LogUtil.e("@@@", "test is null")
} else {
LogUtil.e("@@@", "test is not null")
check(test)
}
}
会报kotlin.UninitializedPropertyAccessException: lateinit property test has not been initialized
2.by lazy {}
private val test by lazy { "haha" }
private fun switchFragment(position: Int) {
if (test == null) {
LogUtil.e("@@@", "test is null")
} else {
LogUtil.e("@@@", "test is not null ${
test}")
check(test)
}
}
输出:test is not null haha
3.总结:
by lazy{}只能用在val类型而lateinit只能用在var类型
lateinit不能用在可空的属性上和java的基本类型上,否则会报lateinit错误
2.3.3.变量可空 ?(修饰符 变量名 : 类型? = 值)
可空类型变量使用前需要先判断后使用:
(1)if … else
(2)?. 判空
可空类型变量?.属性 / 方法,适用于方法链式调用场景,可有效避免NullPointException。如果变量为null,表达式返回null
val builder : Test.Builder? = Test.Builder().setName("Lily")?.setSex("nv")?.setAge(10)
1.if … else判空
2.?. 判空 – 简单判空
可空类型变量?.属性 / 方法,适用于方法链式调用场景,可有效避免NullPointException。如果变量为null,表达式返回null
fun nullTest(): Int {
var list = listOf<Int>(1, 2, 3, 4)
return list?.size
}
3.?. 判空 – 表达式判空
类Java三目判空表达式如果不为空则...否则...(类Java三目表达式)
fun nullTest(): Int {
var list = listOf<Int>(1, 2, 3, 4)
return list?.size ?: 123
}
4.let操作符
使用?符号,加非空判断,.let{ }不为空时执行
var list = listOf<Int>(1, 2, 3, 4)
list?.let {}
5.?: 操作符
判空时,如果为空,则返回默认值
var value: Int? = 0
var nextValue = value ?: -1
6.!!操作符
在使用一个可空类型变量时,在该变量后面加上!!操作符,会显示的抛出NullPointException异常
var value: String? = ""
var nextValue = value!!.length
7.as?操作符(as 操作符)
as操作符表示类型转换,A as B:把A转换成B类型返回,如果转换失败,会抛出ClassCastException
// 会抛出ClassCastException异常
val value: Int = "String" as Int
Log.i("TAG", "$value")
as?针对as操作符,使用同as,区别在于如果转换事变,直接返回null,不会抛出异常
// 直接返回null,不会抛出ClassCastException异常
val value: Int? = "String" as? Int
Log.i("TAG", "$value")
8.总结
(1)抛出NullPointerException的情况
在可空类型变量的使用时,用了!!操作符
显式抛出空引用异常 throw NullPointerException()
(2)抛出ClassCastException的情况
在类型转换中使用了as操作符
使用了toXXX()方法不能转换的情况下
(3)So,尽量避免使用的操作符
尽可能的不要使用 !!操作符,多使用 ?: 、?. 操作符,以及let{} 方法
尽可能的使用 as? 操作符去替换掉 as ,在不确定是否可以安全转换的情况下不使用toXXX()方法
2.4.Kotlin常量
Kotlin中用val修饰的是只读变量,val前加const修饰符的才是常量,const只能修饰val不能修饰var。已知值的属性可以使用const修饰符标记为编译期常量(类似java中的public static final)。
这种属性必须同时满足以下条件:
(1)顶层(Top-level)或对象成员(object 修饰的类)或在伴生对象(companion object)中声明
(2)以String或基本类型进行初始化
(3)没有自定义getter
// 1. 顶层声明
const val NUM_A : String = "顶层声明"
// 2. 在object修饰的类中
object TestConst{
const val NUM_B = "object修饰的类中"
}
// 3. 伴生对象中
class TestClass{
companion object {
const val NUM_C = "伴生对象中声明"
}
}
2.5.Kotlin表达式
2.5.1.If表达式(有值)
在kotlin中,if是一个有值表达式,而不仅仅是一个statements
fun max(x: Int, y: Int) : Int {
return if (x > y) x else y
}
代码中,if (x > y) x else y 和Java中三目运算一样:(x > y) ?x :y
So,上述方法也可表达为:
fun max(x: Int, y: Int): Int = if (x > y) x else y
或者
fun max(x: Int, y: Int) = if (x > y) x else y
2.5.2.for循环
for 可以对 任何提供迭代器的对象 进行迭代;for循环数组时,被编译为一个基于索引的循环,并不会创建一个迭代器对象。
1.使用for循环关键字遍历
Kotlin为for循环定制了一套用于扩展循环规则的关键字
(1)普通循环
for (str in args) {
Log.i("TAG", str)
}
(2)until:表示循环范围(递增 ++),使用方式 n until m,即 ≥n 且 <m
// 循环5次,且步长为1的递增
for (i in 0 until 5){
Log.i("TAG", "str$i")
}
(3)符号‘..’:表示循环范围(递增 ++),使用方式 n..m,即≥n 且 ≤m
// 循环6次,且步长为1的递增
for (i in 0..5