文章是在学习Kotlin时的学习总结笔记,如果觉得写的不好,请直接批评指出!
注:文档中部分内容摘自学习时的视频中ppt,仅供学习交流使用。
Kotlin 是一个基于 JVM 的新的编程语言,由JetBrains开发。
静态强类型,与java100%兼容。
Kotlin可以编译成Java字节码,也可以编译成JavaScript,方便在没有JVM的设备上运行。
Kotlin已正式成为Android官方支持开发语言。
空类型安全
Lambda表达式
扩展方式(扩展函数和扩展属性)
类型推导
胜任java能做的所有事,更简单
没有分号
Intellij Idea或者Android studio等idea编辑器。
官网下载 IntelliJ IDEA 可执行文件:www.jetbrains.com/idea/
windows操作系统安装并激活 IntelliJ IDEA 可参考:blog.csdn.net/qq_35246620/article/details/61200815
官网下载 Android studio 可执行文件:developer.android.com/studio/index.html
Android Studio是基于IDEA开发的,但studio是一个纯粹的Android开发环境,并不支持诸如Groovy,scale。但Android Studio使用操作姿势与IntelliJ IDEA很相似。
下面我们编写我们第一个Kotlin程序,输出“Hello World!”
fun main(args: Array) {
println("Hello Horld!")
}
对Kotlin有了一个基本了解,下面我们再了解一下Kotlin的数据类型吧
在Kotlin当中,Boolean类型大多数情况相当于java中基本类型boolean,只有在必要的时候才会装箱成java中装箱类型Boolean。那我们到底选择哪一个呢,其实都交给编译器去处理了,我们不需要去担心。
val tbool:Boolean = true
val fbool:Boolean = false
//整形
val num1:Int = 8 //十进制 8
val num2:Int = 0XFF //十六进制 255
val num3:Int = 0b00000011 //二进制 3
val maxInt:Int = Int.MAX_VALUE //最大整型值
val minInt:Int = Int.MIN_VALUE //最小整型值
//长整形
val along:Long = 123432424342324
println(18L) //整型强转长整形
Long.MAX_VALUE //最大长整型值
Long.MIN_VALUE //最小长整型值
//短整形
val aShort:Short = 12345
Short.MAX_VALUE //最大短整型值
Short.MIN_VALUE //最小整型值
//字节
val aByte:Byte = 127
Byte.MAX_VALUE //Byte最大值 127
Byte.MIN_VALUE //Byte最小值 -128
//单精度
val aFloat:Float = 2.0F //在Kotlin中2.0默认表示双精度,所以需要加f强转 2.0
val another:Float = 1E3f //10的3次方 1000.0
Float.MAX_VALUE //最大单精度
Float.MIN_VALUE //最小单精度,但MIN_VALUE是最小的正数(负数的最大值),非0,加上-号。所以可用 -Float.MAX_VALUE去表示
//双精度
val aDouble:Double = 3.0
val anotherD:Double = 2.34354
Double.MAX_VALUE //最大双精度
Double.MIN_VALUE // //最小双精度,与Float.MIN_VALUE类似,也是一个正数
NaN,“not a number”一个不是数的数,
例如:
println(0.0F/0.0F) //输出结果 NaN
prinln(0.0F/0.0F ==Float.NaN) //输出结果 false
说明NaN的结果和其他NaN的结果不是相等的,比较起来没有意义,也不要去尝试。
字符对应java的Character
占两个字节,标示一个16位的Unicode字符
字符用单引号 ’ 引起来,例如:‘a’, ‘6’, ‘\n’
val aChar:Char = 'a'
val bChar:Char = '中'
val cChar:Char = '\u000f' //Unicode字符
不可隐式转换
val anInt:Int = 5
val aLong:Long = anInt.toLong() // 这样才可以转换
val aShort:Short = 12345
val aD:Double = aShort.toDouble()// 这样才可以转换
val string:String = "hello" //声明一个字符串
val fromChars:String = String(charArrayOf('h','e','l','l','l','o')) //字符数组
println(string == fromChars) //== 比较值
println(string === fromChars) //=== 引用
输出结果:分别是true和false。
所以“a==”比较的是内容是否相同,即类似java的equals,“a===b”比较对象是否相同
字符串模板特性
val arg1: Int = 0
val arg2: Int = 1
println("" + arg1 + " + " + arg2 + " = " + (arg1 + arg2))
println("$arg1 + $arg2 = ${arg1 + arg2}")
**原始字符串 “”" **
原始字符串用三个"标示,内部字符串无法转义。
var rawStr:String = """\t\n\$666\t哈哈哈"""
println(rawStr.length)
字符串.length获取字符串长度
###5、类与对象
与java中概念基本相同,只不过写法不同。
Any相当于java中的Object
class 妹子(性格: String, 长相: String, 声音: String): 人(性格, 长相, 声音)
class 帅哥(性格: String, 长相: String, 声音: String): 人(性格, 长相, 声音)
open class 人(var 性格: String, var 长相: String, var 声音: String){
init {
println("new 了一个${this.javaClass.simpleName}, ta性格:$性格, 长相:$长相, 声音:$声音")
}
}
fun main(args: Array) {
val 我喜欢的妹子: 妹子 = 妹子("温柔", "甜美", "动人")
val 我膜拜的帅哥: 帅哥 = 帅哥("彪悍", "帅气", "浑厚")
println(我喜欢的妹子 is 人) //判断是属于某个类
}
直接看实例及实例中注释:
//?返回值可以为空
fun getName(): String? {
return null
}
fun getLenght() {
// ?: 判断getName()如果为空,执行return
var name: String = getName() ?: return
var len = name.length
// !! 不进行非空判断
var len2 = getName()!!.length
var value:String? = "Hello World"
// !! 不进行非空判断
println(value!!.length)
//value 是String或!=null,后续可以不用在做null判断
if(value is String)
print(value)
if(value!=null)
print(value)
}
var parent:Parent = Child()
if(parent is Child){
//不像java还需要强转 ((Child)parent).getName()
parent.getName()
}
//类型转换异常
var child1:Child? = parent as Child
// as? 如果转化失败返回null
var child2:Child? = parent as? Child
open class Parent
class Child : Parent() {
fun getName(): String {
return "name"
}
}
一个数学上的概念,表示范围
ClosedRange的子类,InteRange最常用
基本写法:
0…100表示 [0,100]
0 until 100表示 [0,100)
(0…100).contains(i) 和 i in 0…100 均判断i是否在区间[0,100]中
示例:
var range: IntRange = 1..1024 //[0,1024]
var range_exclusive: IntRange = 1 until 1024 //[0,1024)
var range: IntRange = 1..1024 //[0,1024]
var range_exclusive: IntRange = 1 until 1024 //[0,1024)
print(range.contains(100))
print(100 in range)
print(range.isEmpty())
for(i in range)
{
print("$i,")
}
Kotlin中有两个关键字定义变量,这两个关键字外形看着差别很小就只差了一个字母,但实际差别很大的。
var是一个可变变量,这是一个可以通过重新分配来更改为另一个值的变量。这种声明变量的方式和java中声明变量的方式一样。
val是一个只读变量,这种声明变量的方式相当于java中的final变量。一个val创建的时候必须初始化,因为以后不能被改变。
什么是常量?
val = value,值类型
类似Java的final
不能重复赋值
举例:
运行时常量:val x = getX()
编译期常量:const val x = 2 会提高执行效率
什么是变量?
var = variable
举例:
var x = “HelloWorld”//定义变量
x = “HiWorld”//再次赋值
类型推导
编译器可以推导量的类型
val string = “Hello” //推导出String类型
val int = 5 //Int类型
val x = getString() + 5//String类型
先通过实例简单了解一下:
var arrayOfInt: IntArray = intArrayOf(1, 2, 3, 4)
var arrayOfChar: CharArray = charArrayOf('a', 'p', 'p', 'l', 'e')
var arrayOfString: Array = arrayOf("今天", "怎么", "不开心")
for(i in arrayOfInt){
print(i)
}
print(arrayOfChar.joinToString())//a,p,p,l,e
print(arrayOfInt.slice(1..2))//[2,3]
1、基本写法:
val array:Array = arrayOf(…)
2、基本操作
print array[i] 输出第i个成员
array[i] = “Hello” 给第i个成员赋值
array.length 数组的长度
为了避免不必要的装箱和拆箱,基本类型的数组是定制的
Java | Kotlin |
---|---|
int[] | IntArray |
short[] | ShortArrary |
long[] | LongArrary |
float[] | FloatArrary |
double[] | DoubleArrary |
char[] | CharArrary |
3、创建数组
(1)创建空数组,只读
val arrayEmpty = emptyArray()
(2)创建指定长度的可空数组
val array1 = arrayOfNulls(5)
for (i in 0..4) {
array1[i] = i
}
(3)创建指定长度数组
val array4 = Array(5, {0})
初始化长度为5,元素均为0的数组
val array5 : Array = Array(5, {""})
for (i in 0..2) {
array5[i] = i.toString()
}
初始化长度为5,元素均为""的数组,并为数组前3个元素赋值,“0”, “1”, “2”, “”, “”
(4)使用闭包创建数组
val array = Array(4, { i -> i * i }) //0,1,4,9,16
(5)使用Kotlin封装方法创建数组
val array1 = arrayOf(1, 2, 3, 4)
val array2 = intArrayOf(1, 2, 3, 4)
3、遍历数组
var arr:IntArray = intArrayOf(1,2,3,4,5,6,7,8,9,10,11)
// 迭代器遍历数组1
val it = arr.iterator()
for (item in it.iterator()) {
println(item)
}
// 迭代器遍历数组2
val it1 = arr.iterator()
it1.forEach {
println(it)
}
// forEach遍历数组
arr.forEach ForEach@{
if(it == "a") return@ForEach
println(it)
}
for((index,value) in arr.withIndex()){
print("$index -> $value,")
}
for(indexValue in arr.withIndex()){
print("${indexValue.index} -> ${indexValue.value},")
}
// 等等...
什么是函数?
以特定功能组织起来的代码块
fun函数名:[返回值类型]{[函数体]}
fun函数名 = [表达式] //返回值是Unit(相当于java中的void),可不写
匿名函数
fun([参数列表])…
举例:
val say = fun(name:String){ println(“Hello”) }
val say = fun(name:String) = println(“Hello”)
实际上就是匿名函数
写法**{[参数列表]->[函数体,最后一行是返回值]}**
举例:
val sum = {a:Int,b:Int->a+b}
返回的是a+b的和
类型表示举例
1、()->Unit无参,返回值为Unit
2、(Int)->Int传入整型,返回一个整型
3、(String,(String)->String)->Boolean传入字符串、Lambda表达式,返回Boolan
Lambda表达式的调用
1、用()进行调用
2、等价于invoke()
举例
val sum = {a:Int,b:Int->a+b}
sum(2,3)
sum.invoke(2,3)
Lambda表达式的简化
1、如果函数调用时,最后一个是lambda表达式,在传参的时候可以将它移出去
2、函数参数只有一个lambda表达式,调用时小括号可省略
3、lambda表达式只有一个参数,不写也可以,名字默认为it
4、实参、返回值与形参一致的函数可以用方式作为实参传入
属性
或者说成员变量,类范围内的变量
方法
或者说成员函数,类范围内的函数
函数和方法的区别
1、函数强调功能本身,不考虑从属
2、方法的称呼通常是丛类的角度出发
3、叫法不同
定义方法
写法与普通函数完全一致
定义属性
1、构造方法参数中var/val修饰的都是属性
2、类内部也可以定义属性
class Hello(val aField:Int,notAFlield:Int){
var anotherField:Float = 3f
}
aField 是属性
notAField 不是属性,只是普通构造方法的参数
属性的访问控制
属性可以定义getter/setter
举例:
var a: Int = 0
get() = field
set(value) {
field = value
}
val b:Float = 0f
//set(value){ field =value}
get() = field
属性初始化
1、属性的初始化尽量在构造方法中完成
2、无法在构造方法中初始化,尝试降级为局部变量
3、var用lateinit延迟初始化,val用lazy
4、可空类型谨慎用null直接初始化(不建议使用null初始化)
class LazyTest {
var a: Int = 0;
lateinit var str: String
lateinit var x1: X
val STR: String = “STR”
val x2: X by lazy {
print(“lazy X”)
X()
}
}
class X
fun main(args: Array) {
val e: LazyTest = LazyTest()
e.x2
}
1、任何类都可以定义或重载父类的基本运算符
2、通过运算符对应的具名函数来定义
3、对参数的个数要求,对参数和返回值类型不做要求
4、不能像Scala一样定义任意运算符
1、中缀表达式
只有一个参数,且用infix修饰的函数
fun main(args: Array) {
val b = Book();
b on "哈哈"
}
class Book {
infix fun on(str: String) {
print("Book on $str")
}
}
运行结果:Book on 哈哈
2、分支表达式
1)if…else
if(ab)…else if(ac)…else…
val x = if(b<0) 0 else b
val x = if(b<0) 0 //错误,赋值时,分支必须完备
3、when表达式
1)加强版switch,支持任意类型,不需些break
2)支持纯表达式条件分支(类似if)
3)表达式与完备性
举例:
fun main(args: Array) {
val x = 5
when (x) {
is Int -> println("整型")
in 0..10 -> println(" x in 0..10")
in 0..100 -> println(" x in 0..100")
5 -> println("x:$x 值是5")
}
val a = when{
x in 0..10 ->100
else ->0
}
print("a的值是$a")
}
1)for循环
a.基本写法
for(element in elements)…
b.给任意类实现Iterator方法
运行机制简单举例:
class MyIterator(val iterator:Iterator){
operator fun next():Int{
return iterator.next()
}
operator fun hasNext():Boolean{
return iterator.hasNext()
}
}
class MyIntList{
private val list = ArrayList()
fun add(int:Int){
list.add(int)
}
fun remove(int:Int){
list.remove(int)
}
operator fun iterator():MyIterator{
return MyIterator(list.iterator())
}
}
fun main(args: Array) {
val list = MyIntList()
list.add(1)
list.add(2)
list.add(3)
for (i in list){
println(i)
}
}
2)while循环
a. do…while(…)…
b. while(…)…
3)continue
跳过当前循环
4)break
终止循环
多层循环嵌套的终止结合标签使用
Outter@for(…){
Inner@while(i<0){if(…break@Outter)}
}
如果break后面没有@Outter,是把while给break了,会继续执行for循环,如果加了@Outter了,就是for循环给break了
try…catch
1、catch分支匹配异常类型
2、表达式,可以用来赋值,与if…else、when类似
例如:
fun main(args: Array) {
val a = try {
0 / 3
} catch (e: Exception) {
e.printStackTrace()
println("出现异常")
0
} finally {
println("执行finally")
}
println(a)
}
运行结果:
执行finally
3
finally
无论代码是否抛出异常都会执行
return try(x/y)catch(e:Exception){0}finally{…}
如果抛异常返回0,否则返回x/y,finally一定会执行,先执行finally再返回。
1)具名参数
给函数的实参附上形参
举例:
fun sum(arg1:Int,arg2:Int) = arg1+arg2
sum(arg1=2,arg2=3)
2)变长参数
a.某个参数可以接收多个值
b.可以不为最后一个参数
c.如果传参时有歧义,需要使用具名参数
示例如下:
Spread Opreator *
a.只支持展开的Array
b.只用于变长参数列表的实参
c.不能重载
3)默认参数
a.为函数参数指定默认值
b.可以为任意位置的参数指定默认值
c.传参时,如果有歧义,需要使用具名参数
1)简单实现计算器
fun main(args: Array) {
while (ok()) {
try {
println("请输入,例如:3 + 4")
val input = readLine() ?: break
val split = input.trim().split(" ")
if (split.size < 3) {
throw IllegalArgumentException("参数个数不对")
}
val a = split[0].toDouble()
val op = split[1]
val b = split[2].toDouble()
println("$a $op $b -> ${Opreator(op).apply(a, b)}")
} catch (e: NumberFormatException) {
println("您输入的数字有误")
} catch (e:IllegalArgumentException) {
println("参数有误")
}catch (e:Exception){
println("其他异常,${e.message}")
}
println("再来一发吗?[Y]")
val cmd = readLine()
if (cmd == null || cmd.toLowerCase() != "y") {
break
}
}
println("退出成功!")
}
fun ok(): Boolean {
return true
}
1)什么是接口
接口,直观理解就是一种约定
kotlin的接口与Objcet-C的Protocal比较类似
举例:
interface InputDevice{
fun input(event:Any)
}
2}接口
a.接口不能有状态
b.必须由类对其进行实现后使用
3)抽象类
a.实现了一部分协议的半成品
b.可以有状态,可以有方法实现
c.必须由子类继承后使用
4)抽象类和接口的共性
a.比较抽象,不能直接实例化
b.有需要子类(实现类)实现的方法
c.父类(接口)变量可以接受子类(实现类)的实例赋值
d.抽象类反映本质,接口体现能力
5)抽象类和接口的区别
a.抽象类有状态,接口没有状态
b.抽象类有方法实现,接口只能有无状态的默认实现
c.抽象类只能单继承,接口可以多实现
1)父类需要open才可以被继承,抽象类不需要
2)父类方法、属性需要open才可以被复写
3)接口、接口方法、抽象类默认为open
4)复写父类(实现接口)的方法需要override关键字
5)class D:A(),B,C
6)继承类时实际上是调用了父类构造方法
7)类只能单继承,接口可以多实现
接口代理
class Leader(val dr:Drive,val pp:PPT) by dr,P by pp
接口方法实现交给代理类实现
事例:
interface D{
fun drive()
}
interface P{
fun write()
}
class Drive:D{
override fun drive(){
println("开车")
}
}
class PPT:P{
override fun write(){
println("写ppt...")
}
}
//接口代理
class Leader(val dr:Drive,val pp:PPT):D by driver,P by write
fun main(args: Array) {
val lead = Leader(pp=PPT(),dr=Drive())
//接口代理调用
lead.drive()
lead.write()
}
接口方法冲突
1)接口方法可以有默认实现
2)方法名、方法参数以及返回值相同,否则会有冲突
3)子类(实现类)必须复写冲突方法
4)super<[父类(接口)名]>.[方法名]([参数列表])
事例:
open class A {
open fun x(): Int = 'A'.toInt()
}
interface B {
fun x(): Int = 'B'.toInt()
}
interface C {
fun x(): Int = 'C'.toInt()
}
class F(var y: Int) : B, C, A() {
override fun x(): Int {
return when {
y > 0 -> super.x()
y < 0 -> super.x()
else -> super.x()
}
}
}
fun main(args: Array) {
println(F(-1).x())//B
println(F(0).x())//C
println(F(1).x())//A
}
运行结果:66,67,65
注:internal在和java兼容方面依旧存在问题,暂不建议使用
1)只有一个实例的类
2)不能自定义构造方法
3)可以实现接口、继承父类
4)本质上就是单例模式最基本的实现
注:如果不考虑懒加载等因素,Kotlin中写单例,使用的是object。
针对使用静态方法的场景,请尽量考虑使用包级函数
Companion
1)每个类可以对应一个伴生对象
2)伴生对象的成员全局独一份
3)伴生对象的成员类似Java中的静态成员
4)静态成员考虑用包级函数、变量替代
5)JvmField和JvmStatic的使用
示例:
无论加不加**@JvmStatic和@JvmField**在kotlin都可正常调用,而Java若想调用,则必须添加这两个注解。
class Latitude private constructor(val d:Double){
companion object {
@JvmStatic
fun la(d: Double):Latitude{
return Latitude(d)
}
@JvmStatic
fun getD(latitude: Latitude):Latitude{
return Latitude(latitude.d);
}
@JvmField
val TAG:String = "TAG"
}
}
kotlin中调用
fun main(args: Array) {
var latitude = Latitude.la(6.66)
val latitude2 = Latitude.getD(latitude)
val tag = Latitude.TAG
}
java中调用
Latitude latitude = Latitude.la(3.234);
Latitude latitude2 = Latitude.getD(latitude);
String tag = Latitude.TAG;
1)方法重载
a.Overloads
b.名称相同、参数不同的方法
c.Jvm函数签名的概念:函数名、参数列表
d.跟返回值无关
2)默认参数
a.为函数参数设定一个默认值
b.可以为任意位置的参数设置默认值
c.函数调用产生混淆时用具名参数
3)方法重载和默认参数
a.二者的相关性以及**@JvmOverloads**(给java中调用kotin中的默认参数时使用)
事例:
class Overloads {
// fun a(): Int {
// return 0
// }
// @JvmOverloads
fun a(int: Int = 0): Int {
return int
}
fun a(str: String): Int {
return str.toInt()
}
}
fun main(args: Array) {
val ovarLoads = Overloads()
ovarLoads.a(1)
ovarLoads.a("2")
//kotlin中调用
println(ovarLoads.a())
}
b.避免定义关系不大的重载
c.不好的设计
List.remove(Int)
List.remove(Object)
为现有类添加方法属性
fun X.y():Z{…}
val X.m 注:扩展属性不能初始化,类似接口属性
Java调用扩展成员类似调用静态方法
事例:
fun main(args: Array) {
println("abc".times(6))
println("a" * 6)
println("a".a )
println("b".b)
}
//扩展方法
operator fun String.times(num: Int): String {
val sb = StringBuffer()
if (num > 0) {
for (i in 0 until num) {
sb.append(this)
}
}
return sb.toString()
}
//扩展属性
val String.a
get() = 5
var String.b
set(value){}
get() = 6
1)定义方法
val/var : by
2)代理者需要实现相应的setValue/getValue方法
事例:
class Delegate {
val x1 by lazy {
"Hello X"
}
var x2 by X()
var x3 by X()
}
class X {
private var value: String? = null
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
println("getValue---------$thisRef--------------------${property.name}")
return value ?: ""
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("setValue------------$thisRef----------------${property.name}-----------$value")
this.value = value
}
}
fun main(args: Array) {
val delegate = Delegate()
println(delegate.x1)
println(delegate.x2)
println(delegate.x3)
delegate.x3 = "666666666666666666666"
println(delegate.x3)
}
1)data class默认实现了copy、toString、hashCode、equals等方法
2)componentN方法
3)allOpen和noArg插件
内部类
a.定义在类内部的类
b.与类成员有相似的访问控制
c.默认是静态内部类,非静态用inner关键字
d.this@Outter,this@Inner的用法
示例:
内部类访问外部成员
匿名内部类
a.没有定义名字的内部类
b.类名编译时生成,类似Outter$1.class这样的类名
c.可继承父类、实现多个接口,与Java注意区别
示例:
kotlin中匿名内部类可以多实现
实例可数的类,注意枚举也是类
可以修改构造,添加成员
可以提升代码的表现力,也有一定的性能开销
1)子类可数
kotlin版本<1.1,子类必须定义为封闭类的内部类
v1.1,子类只需要与密封类在同一文件中或者是它的内部类
示例:
2)与枚举类似却又不同
枚举是实例可数,密封类是子类可数;
示例:
f(g(x))
传入或者返回函数的函数;
函数引用 ::println
;
带有Receiver的引用
var list = listOf(1, 2, 3, 4, 5, 6, 7)
var newList = ArrayList()
list.forEach {
var newElement = it * 2 + 3
newList.add(newElement)
}
//map
var newList2 = list.map {
it * 2 + 3
}
newList.forEach(::println)
newList2.forEach(::println)
/**
* 等同:var newList3 = list.map{
* it.toDouble
* }
*/
var newList3 = list.map(Int::toDouble)//函数引用
newList3.forEach(::println)
newList与newList2输出结果相同,而newList3数值是相同的,只不过转化成了double类型。
2. flatMap
先看示例:
var list = listOf(
1..30,
2..10,
100..120
)
var flatMapList = list.flatMap {
it
}
/**
* 等同于
*
* list.flatMap {
* it.map {
* "NO.$it"
* }
* }
*/
list.flatMap { intRange ->
intRange.map { intElement ->
"NO. $intElement"
}
}
3. reduce
示例:
4. fold和joinToString
fold充当有初始值的recude,其次还可以做一些变换,如字符串拼接,还有foldRight,foldIndex等;
joinToString,字符串拼接神器
示例:
**5. filter过滤 **
示例:
6. takeWhile
遇到第一个不符合条件的数据结束取数据,留下前面的作为一个新的集合返回;
示例:
7. let
示例:
8. apply
示例:
9. with
with和apply的区别
示例:
10. readText
示例:
11. readLines
把所有的行都读到;