kotlin学习
包
- 包关键词:package
- 定义包:package cn.zhiup.mobile
- kotlin中包的名称与目录结构无关只与定义包关键词package后面的包名有关我们可以在 cn.zhiup.mobile 目录结构中做如下定义:
package cn
复制代码
然后在使用的地方 用下面的方式导入:
import cn.*
复制代码
这样是正常的 ,因为包跟目录结构无关,只跟package后面的定义有关
导入
-
导入关键字 import
-
如何导入? import cn.zhiup.mobile.MainApp
-
可以导入哪些东西?
导入类
导入顶层函数及属性
导入类中的函数及属性
导入枚举常量
kotlin没有import static 所有的导入都由import完成
-
如果导入类的名字产生冲突则由 as 关键词给重命名消除歧义
import cn.zhiup.mobile.MainView
import cn.zhiup.mobile.pack.MainView as mView
复制代码
可见性修饰符
- 可见性修饰符关键字 ,private protected internal public
- 默认是 public的
- 顶层声明的可见性
public 随处可见
internal 模块内可见
protected 不适用于顶层声明
private 只在声明他的文件中可见
- 类中声明的可见性
public 能见到类声明的任何地方可见
internal 能见到类声明的本模块内部的任何地方可见
private 本类内部任何地方可见(被private修饰的内部类中的属性和方法,外部类不可见)
protected 与private 相同+子类可见
基本类型
数字
- Double 64位
- Float 32位
- Long 64位
- Int 32位
- Short 16位
- Byte 8位
//在kotlin中字符不是数字
-普通字符
100
Long 类型字符 100L
-十六进制
0x0F
-二进制
0b00001111
Double 类型
123.0 123.5e5
Float
123.0F
数字下划线
123_456_789 //使用下划线分割,字面值等于 123456789
复制代码
注意: kotlin中整形默认Int 浮点型默认Double
注意: kotlin不支持8进制
var l = 10 //10是Int类型
var j = 13.5 //13.5是Double类型
数字的比较
1.不同类型的整形之间不能进行比较,会报如下错误
Operator '==' cannot be applied to 'Int' and 'Long'
例:
如需进行比较需要进行下面转换
if(a.toLong()==b){}
复制代码
2.整形和浮点型之间也不能进行直接进行比较,会报如下错误
Operator '==' cannot be applied to 'Float' and 'Long'
如需进行比较需要进行下面转换
if(c==b.toFloat()){
}
复制代码
3.同理Double之间也不能直接进行比较 也需要做上面的转换操作
数字的转换
var a:Short = 1
var b:Int = a //错误 a不能赋值给b 因为类型不一样
var b:Int = a.toInt()//正确此时a被显式转换为Int
复制代码
每个数字类型都支持如下转换:
- toByte(): Byte
- toShort(): Short
- toInt(): Int
- toLong(): Long
- toFloat(): Float
- toDouble(): Double
- toChar(): Char
复制代码
字符 Char
- 字符用Char类型来表示
//定义字符
var a:Char = 'a'
复制代码
- 字符不能被当作数字
if('a'==97){//错误,字符不能当成数字进行比较,此处类型不兼容
}
复制代码
- 特殊字符可以用反斜杠转义
\t、
\b、
\n、
\r、
\'、
\"、
\\、
\$、
复制代码
- 字符显式转换(可以把字符转换成数字)
'c'.toInt()
复制代码
布尔 Boolean
- 布尔类型用Boolean表示,他有两个值 true,false
- 布尔的运算
|| – 短路逻辑或
&& – 短路逻辑与
! - 逻辑非
复制代码
数组 Array
- 创建数组
1.arrayOf创建数组
var arr = arrayOf(1,2,3)//创建了一个初始值为123的数组
2.构造方法创建数组
var arr = Array(5, { i -> (i * i).toString() })//创建一个 Array<String> 初始化为 ["0", "1", "4", "9", "16"]
复制代码
- 遍历数组
aar.forEach { println(it) }
复制代码
- 使用数组中的元素
println(arr[0])
复制代码
字符串 String
- 定义字符串
var s = "sssss"
var s:String = "ssssss"
复制代码
- 访问字符串中的某一个字符
s[i] //访问字符串中的第i个字符
复制代码
- 连接字符串
我们可以用加号来连接字符串
var str = "abc"+1//此时需要第一个元素是字符串
var str = str+str//可以字符串变量之间拼接
复制代码
- 字符串字面值
(1) 输出 原始字符串 使用三个引号(""")分界符括起来,内部没有转义并且可以包含换行以及任何其他字符:
val text = """
for (c in "foo")
print(c)
"""
这样定义输出字符串的原始样式
复制代码
(2) 输出 转义后的字符串
val s = "Hello, world!\n"//\n被识别为换行符
复制代码
- 字符串模板
与java不同的是kotlin提供了字符串模板以便我们输出或组合字符串
(1) 字符串包含变量
val i = 10
println("i = $i") // 输出“i = 10”
复制代码
(2) 字符串中包含表达式
val s = "abc"
println("$s.length is ${s.length}") // 输出“abc.length is 3”
复制代码
(3) 字符串字面值中也可以使用字符串模板
val price = """
${'$'}9.99
"""
//输出$9.99
复制代码
控制流if,when,for,while
if表达式
- if表达式传统用法
// 传统用法
var max = a
if (a < b) max = b
复制代码
- if表达式传统用法+else
// With else
var max: Int
if (a > b) {
max = a
} else {
max = b
}
复制代码
- if作为表达式
// 作为表达式
val max = if (a > b) a else b
复制代码
代码块最后一行那个作为if表达式返回的值
val max = if (a > b) {
print("Choose a")
a
} else {
print("Choose b")
b
}
复制代码
when
- 一般用法,类似于java switch
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> { // 注意这个块
print("x is neither 1 nor 2")
}
}
复制代码
- 多分支做相同处理操作的
when (x) {
0, 1 -> print("x == 0 or x == 1") //多分枝放到一块,然后用逗号隔开
else -> print("otherwise")
}
复制代码
- 用任意表达式作为分支条件
when (x) {
parseInt(s) -> print("s encodes x")
else -> print("s does not encode x")
}
复制代码
- 利用in 或 !in 来检测是否在某一个区间或集合中
when (x) {
in 1..10 -> print("x is in the range") //x是否在1-10的区间
in validNumbers -> print("x is valid") //x是否则validNumbers集合中
!in 10..20 -> print("x is outside the range")//x不在10-20区间 则为true
else -> print("none of the above")//以上都不是
}
复制代码
- 利用is 或 !is 来检测是否是某种类型
fun hasPrefix(x: Any) = when(x) {
is String -> x.startsWith("prefix") //检测为true时自动转换为String
else -> false
}
复制代码
- when取代if else
when {//此处没有(x)这种写法,直接利用箭头前面的表达式返回的布尔值来进行区分是否进入子分支。
x.isOdd() -> print("x is odd")
x.isEven() -> print("x is even")
else -> print("x is funny")
}
复制代码
职上 混合使用实例
for循环
for循环可以对任何迭代器对象进行遍历
- 简单使用
for (item in collection) print(item)
复制代码
- 循环体是代码块
for (item: Int in ints) {
// ……
}
复制代码
- 在数字区间迭代(×区间知识)
for(i in 1..10){
println(i) //输出1到10包含10[1,10]
}
for(i in 1 until 10){
println(i) //输出1到9 不包含10 [1,0)
}
for(i in 1..10 step 2)
{
println(i)//输出1,3,5,7,9
}
for(i in 1 until 10 step 3)
{
println(i)//输出1,4,7
}
for(i in 4 downTo 1){
println()//输出4,3,2,1
}
复制代码
- 对数组或list索引进行迭代
for (i in array.indices) {
println(array[i])
}
复制代码
- 使用withIndex进行
for ((index, value) in array.withIndex()) {
println("the element at $index is $value")
}
复制代码
while循环
while 循环与java相同
while (x > 0) {
x--
println(x)
}
do {
val y = retrieveData()//先执行,再判断
} while (y != null) // y 在此处可见
复制代码
返回与跳转
kotlin的返回跳转方式
-
return
直接从包围他的函数或匿名函数体内返回 复制代码
-
break
终止最直接包围他的循环 复制代码
-
continue
跳出本次循环直接进行下次包围他的循环 复制代码
kotlin break continue的标签
kotlin中任何表达式都可以用标签来标记,标签可以用标签名称加@符来表示(例如 lable@),要为一个表达式添加标签我们只需要在表达式前面添加即可
复制代码
- 标签的添加
loop@ for(i in 1..100){ //loop标签
}
复制代码
- 标签的使用
(1)break
loop@ for (i in 1..100) {
for (j in 1..100) {
if (……) break@loop //当满足条件时,跳转到for标签,这样可以直接退出两重循环 此处非常方便啊
}
}
(2)continue
loop@for(i in 1..100){
for (j in 1..20){
if(j%2==0){
continue@loop//满足条件时,用coninue来跳出这个循环来执行这个循环的下一次。
}
println("$i===$j")
}
}
(3)return
//直接返回给函数调用者
fun foo() {
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return // 非局部直接返回到 foo() 的调用者
print(it)
}
println("this point is unreachable")
}
//返回至标签处
fun foo() {
listOf(1, 2, 3, 4, 5).forEach lit@{
if (it == 3) return@lit // 局部返回到该 lambda 表达式的调用者,即 forEach 循环 等于三时不打印直接返回
print(it)
}
print(" done with explicit label")
}
//带返回值的标签处返回
return@a 1//吧数字1返回至标签1处
复制代码
集合
关键字 List Set Map
复制代码
函数
1.函数声明
fun funName():Unit{//声明一个函数名为funName 返回值为 Unit的函数 (Unit代表啥也不返回)
}
fun funName(){//声明一个函数名为funName 返回值为 Unit的函数 (Unit可省略)
}
fun funName():Int{//声明一个函数名为funName 返回值为 Int的函数 (想返回其他返回类型直接把Int改成相应的类型即可)
return 10
}
fun funName(i : Int):Int{//声明一个函数名为funName 参数为i 返回值为 Int的函数 (可以写多个参数)
return i*2
}
复制代码
- 函数的使用
var str = funName("sss") //直接以函数名括号参数的形式使用函数即可(funName("sss"))
复制代码
- 函数的默认参数
fun funName(i:Int = 2):Int{//默认参数直接在类型后面加等号及默认值即可
return i*2
}
复制代码
- 子类在重写带有默认参数的方法时,这个具有默认值的参数不能被重写为默认参数
open class A {
open fun foo(i: Int = 10) { …… }
}
class B : A() {
override fun foo(i: Int) { …… } // 这里的i不能有默认值,所以不能写成i:Int = 默认值
}
复制代码
- 利用命名参数来解决多默认参数的问题
fun funName(i:Int = 10,d:Double){//第一个是默认参数,第二个不是默认参数
}
//使用方法如下
funName(d=2.0) //d=2则是命名参数
注意:
当一个函数调用混用位置参数与命名参数时,所有位置参数都要放在第一个命名参数之前。
例如,允许调用 f(1, y = 2) 但不允许 f(x = 1, 2)。
复制代码
6.可变参数函数(vararg)
//定义
fun foo(vararg strings: String) { …… }
foo(strings = *arrayOf("a", "b", "c"))//*号是伸展操作符,及吧Array数组中的元素伸展。
复制代码
函数的作用域
kotlin中的函数可以在文件中声明,这意味着你不用非得建一个类,然后吧函数写到类里面
1.局部函数
(1) kotlin支持局部函数,即一个函数在另外一个函数内部
fun dfs(graph: Graph) {
fun dfs(current: Vertex, visited: Set<Vertex>) {
if (!visited.add(current)) return
for (v in current.neighbors)
dfs(v, visited)
}
dfs(graph.vertices[0], HashSet())
}
复制代码
(2) 局部函数可以访问外部函数(即闭包)的局部变量,所以在上例中,visited 可以是局部变量:
fun dfs(graph: Graph) {
val visited = HashSet<Vertex>()
fun dfs(current: Vertex) {
if (!visited.add(current)) return
for (v in current.neighbors)
dfs(v)
}
dfs(graph.vertices[0])
}
复制代码
2.成员函数
(1) 成员函数是在类内部定义的函数
class Sample() {
fun foo() { print("Foo") }
}
复制代码
(2) 成员函数的调用
Sample().foo() // 创建类 Sample 实例并调用 foo
复制代码
- 泛型函数
函数可以有泛型,通过函数名前<>来指定
fun <T> (t:T):T{//创建了一个参数是T类型,返回类型是T类型的函数
}
复制代码
- 扩展函数
- 高阶函数和lambda表达式
- 尾递归函数
高阶函数
高阶函数是一种特殊的函数它接收参数做返回值,或者返回一个函数
高阶函数基本结构
fun funName(str:String,add:(a:Int,b:Int)->Int):String
(1)funName 主函数名称
(2)str 主函数正常参数
(3)add 高阶函数的函数名
(4)(a:int,b:Int) 高阶函数的参数列表
(5)->分隔符(类似于lambda)
(6)Int高阶函数的返回值
(7)String主函数返回值
复制代码
- 将函数当作参数
(1)判断两个数的和是否大于两个数的差
package com.zhou.test
fun main(args:Array<String>){
println(isBig(19,0,::add,::del))//利用双冒号加函数名字来表示高级函数的函数参数
}
fun isBig(a:Int,b:Int,add:(a:Int,b:Int)->Int,del:(a:Int,b:Int)->Int):Boolean{
return add(a,b)>del(a,b)
}
fun add(a:Int,b:Int):Int{
return a+b
}
fun del(a:Int,b:Int):Int{
return a-b
}
复制代码
- 将函数当作返回值
fun funName(a:Int,b:Int):(Int,Int)->String{//返回一个连个参数类型都是Int,返回String的函数
return fun (a,b):String{return ""+a+b}//返回的函数
}
复制代码
lambda表达式
- lambda基本形式
{ x: Int, y: Int -> x + y } //完全形式
{it*it} //当只有一个参数时可以省略此前面参数列表及 ->
复制代码
- 高阶函数最后一个参数是函数的可以把lambda表达式移动到函数外
val product = items.fold(1) { acc, e -> acc * e }//lambda在函数外(一种很奇怪的写法)
复制代码
- 如果该 lambda 表达式是调用时唯一的参数,那么圆括号可以完全省略:
run { println("...") }
复制代码
- 下划线用于未使用的变量
map.forEach { _, value -> println("$value!") }
复制代码
内联函数(inline)
内联函数的意义就是通过inline关键字在编译时直接把函数部分代码放到被内敛的函数中。由于函数存在的意义就是为了重用,书写一次,使用多次。但这样做违背了这个原则,尽管这个过程是自动完成的。
××× 注意每个被内联的函数不要太大。否则会大大增加生成的.class文件的大小
复制代码
- 未使用inline
fun showArea(area:(name:String)->String):String{
return area("iphone")
}
fun main(args:Array<String>){
println(showArea({"{$it}美国"}))
}
复制代码
- 使用inline
inline fun showArea(area:(name:String)->String):String{
return area("iphone")
}
fun main(args:Array<String>){
println(showArea({"{$it}美国"}))
//或者
println(showArea(){"{$it}美国"})
}
复制代码
- 部分函数参数内联
inline fun showAreaPrice(area:(name:String)->String,noinline price:(price:String)->String):String{
return area(price("iphone"))
}
复制代码