一、常量與變量(val,var)
- 1.什么是常量?
1 .val = value ,值類型;
2.類似Java的final;
3.不能重復(fù)賦值;
4.舉例:
運(yùn)行時(shí)常量:val x = getX(); 也就是程序運(yùn)行時(shí)才賦值;
編譯期常量:const val x = 2 ;也就是在編譯時(shí)期就賦值了;
- 2.什么是變量?
1.var=variable ;
2.可以再次賦值;
3.舉例:
var x = "HelloWorld"http://定義變量
x = "HiWorld"http://再次賦值
示例如下:
/**
* 常量
* 帶const為編譯期的常量
* 不帶const的val為運(yùn)行期常量
* 不能再次賦值
*/
const val FINAL_HELLO_WORLD:String = "Hello world"
//變量 能夠再次賦值
var helloworld:String = FINAL_HELLO_WORLD
二、類型推導(dǎo)
val string = "Hello"http://推導(dǎo)出String類型
val int = 5 //Int類型
var x = getString()+5 //String類型,加號(hào)在這里是連接符
三、函數(shù)--單一原則:
1.任何函數(shù)都是以fun開(kāi)頭的,緊接著的是方法名字,緊接著的是參數(shù)列表,緊接著的是返回值,緊接著是函數(shù)體;
fun <MethodName>(<ParamsName>:<ParamsType>):<returnValue>{}
2.如果無(wú)返回值,可以寫:Unit或者在參數(shù)列表后什么都寫;Unit類型Java中的void;
3.如果參數(shù)有多個(gè),以逗號(hào)分開(kāi);
4.如果一個(gè)函數(shù)只是返回一個(gè)函數(shù)的表達(dá)式的值,直接可以等于表達(dá)式即可;
5.函數(shù)的名字不是必須的,如果有個(gè)變量接受返回值即可編譯通過(guò);--請(qǐng)看下面的例子
6.函數(shù)作為類型的一種出現(xiàn)的,可以被用來(lái)賦值與傳遞的;
/**
* 函數(shù)
* methodName
* param1,param2是參數(shù)名
* String Int 是參數(shù)類型
* 備注:
* 1.任何函數(shù)都是以fun開(kāi)頭的;
* 2.如果參數(shù)有多個(gè),以逗號(hào)分開(kāi);
* 3沒(méi)有返回值在參數(shù)后添加:Unit,也可以不寫,這個(gè)Unit相當(dāng)于java里面的void;
* 4如果一個(gè)函數(shù)只是返回一個(gè)函數(shù)的表達(dá)式的值,直接可以等于表達(dá)式即可;
*/
fun methodName(param1:String,param2:Int):Unit {
}
//帶返回值的函數(shù)
fun testMethod(args:String):String{
return args
}
/**
* 如果一個(gè)函數(shù)只是返回一個(gè)函數(shù)的表達(dá)式的值,直接可以等于表達(dá)式即可
*/
fun returnFun0(param1: Int, param2: Int):Int {
return param1+param2
}
//上面的等價(jià)于下面的寫法:
fun returnFun(param1:Int,param2:Int) = param1+param2
/**
* 函數(shù)可以沒(méi)有名字的,如果有一個(gè)變量來(lái)接收的話即可
*/
val resultValue = fun(x: Int):Int {
return x
}
四、Lambda表達(dá)式
1.也就是匿名函數(shù);函數(shù)作為類型的一種出現(xiàn)的,可以被用來(lái)賦值與傳遞的;
2.寫法:{[參數(shù)列表] ->[函數(shù)體,最后一行是返回值]}
3.() -> Unit
--無(wú)參,返回值為Unit;
4.(Int) -> Int
--有參-傳入Int類型,返回一個(gè)整數(shù);
5.(String,(String) -> String) -> Boolean
--傳入字符串、Lambda表達(dá)式,返回Boolean;
- 1.Lambda表達(dá)式的調(diào)用
1.用括號(hào)()來(lái)表示方法的調(diào)用,是Kotlin中的運(yùn)算符,等價(jià)于invoke();這個(gè)invoke其實(shí)是運(yùn)算符重載的方法
2.Lambda表達(dá)式如果沒(méi)有參數(shù),箭頭 (->)就不用寫了;
3.Lambda表達(dá)式不是只有一行的,它的返回值是它表達(dá)式的最后一行的值;
val sum = {a:Int,b:Int ->a+b}
//用括號(hào)()來(lái)調(diào)用,等價(jià)于invoke();
sum(2,3)
或者使用:sum.invoke(2,3),二者完全等價(jià);
//Lambda表達(dá)式?jīng)]有參數(shù),箭頭可以不寫,如下:
val printHello = {
println("hello")//相當(dāng)于函數(shù)的函數(shù)體
}
//Lambda表達(dá)式不是只有一行的,它的返回值是它表達(dá)式的最后一行的值
val sum = {a:Int,b:Int ->
println("$a + $b = ${a+b}")//這一行也是表達(dá)式,不過(guò)返回的是Unit類型
a+b//這一行最為L(zhǎng)ambda的最后一行,它的值也是Lambda的返回值;
}
- 2.Lambda表達(dá)式的簡(jiǎn)化
- 1.函數(shù)參數(shù)調(diào)用時(shí)最后一個(gè)Lambda可以移出去;也就是說(shuō)如果一個(gè)函數(shù)調(diào)用時(shí),最后一個(gè)參數(shù)是Lambda表達(dá)式,我們傳入?yún)?shù)時(shí),那么這個(gè)參數(shù)可以被移出去;
- 2.函數(shù)的參數(shù)只有一個(gè)Lambda,調(diào)用時(shí)小括號(hào)可以省略;
- 3.如果一個(gè)Lambda只有一個(gè)參數(shù),可以不寫,可默認(rèn)為it;
- 4.入?yún)ⅰ⒎祷刂蹬c形參一致的函數(shù)可以用函數(shù)引用的方式作為實(shí)參傳入;
val sum = {a:Int,b:Int ->a+b}
//Lambda表達(dá)式在沒(méi)有參數(shù)時(shí),箭頭就不用寫了,如下:
//如下面的寫法,因?yàn)闆](méi)有參數(shù),箭頭就不用寫了:
val printHello = {
println("hello")//相當(dāng)于函數(shù)的函數(shù)體
}
//看下面的數(shù)組遍歷:
arg.forEach{element -> println(element) }
//如果一個(gè)Lambda傳入的參數(shù)只有一個(gè),可以不寫,可默認(rèn)為it;上面的等價(jià)于:
arg.forEach{ println(it) }
//函數(shù)的參數(shù)只有一個(gè)Lambda表達(dá)式時(shí),調(diào)用時(shí)可以將大括號(hào)移到小括號(hào)外面;
//上面的等價(jià)于下面的:
arg.forEach(){println(it)}
//這個(gè)小括號(hào)沒(méi)有什么用,可以把這個(gè)小括號(hào)省略
arg.forEach{println(it)}
//如果函數(shù)的參數(shù)和返回值與action的參數(shù)和返回類型一致,就可以用函數(shù)引用的方式,如下:
arg.forEach(::println)
五、循環(huán)語(yǔ)句
for循環(huán)如下:
for (i in args) {
println("輸出的是:$i")
}
for ((index, value) in args.withIndex()) {
println("$index -> $value")
}
for (indexValue in args.withIndex()) {
println("${indexValue.index}---->${indexValue.value}")
}
//或者使用下面的方式
args.forEach{
println(it)
}
}
//看下面的一個(gè)方法,return會(huì)把函數(shù)截?cái)啵瑹o(wú)法執(zhí)行到最后一行
fun main(args: Array<String>) {
args.forEach {
if (it == "c") return
println(it)
}
//你會(huì)發(fā)現(xiàn)如果上面的if條件觸發(fā)了 ,將跳出for循環(huán),并且執(zhí)行不到下面的代碼,原因是:
//上面的是表達(dá)式,而不是一個(gè)簡(jiǎn)單的遍歷,return會(huì)調(diào)出整個(gè)函數(shù)的執(zhí)行
println("The End")
}
//如果想只跳出it=="c"這個(gè)條件,循環(huán)繼續(xù)進(jìn)行,整個(gè)函數(shù)執(zhí)行完畢,可以如下:
fun main(args: Array<String>) {
args.forEach ForEach@ {
if (it == "c") return@ForEach
println(it)
}
println("The End")
}
while循環(huán)
var int = 5
while (int > 0) {
println("int---->$int")
int--
}
do {
println("int---->$int")
int--
}while (int>5)
如何跳出 或 跳過(guò)循環(huán)?
跳出或終止循環(huán):break
跳過(guò)循環(huán):continue
多層嵌套循環(huán)的終止需要結(jié)合標(biāo)簽來(lái)實(shí)現(xiàn)
Outter@ for (i in args) {
Inner@while (i.toInt() > 0) {
break@Outter //這里只是跳出外層的for循環(huán) break和@Outter之間不能有空格
}
}
六、異常捕獲(try catch finally)
kotlin里面的異常捕獲和Java中的異常捕獲用法一樣,最后都會(huì)調(diào)用finally;但Kotlin中的try、catch也是表達(dá)式,這點(diǎn)與Java中的不一樣,它可以有返回值,返回值是表達(dá)式的最后一行的值;
- catch分支匹配異常類型;
- 它是一個(gè)表達(dá)式,可以用來(lái)賦值,與if else when 表達(dá)式是完全類似的;
- finally 無(wú)論try catch 是否拋出異常都會(huì)執(zhí)行到;
注意此行代碼代表的含義:
return try{ x/y } catch(e:Exception){ 0 } finally{...}
代表:先執(zhí)行finally里面的代碼,再返回;如果無(wú)異常,返回x/y;如果有異常,返回0;
//kotlin中的try catch也是表達(dá)式,可以有返回值;返回值是表達(dá)式的最后一行的值;
val result = try {
args[0]
} catch (e: Exception) {
println("jinlaile")
0
} finally {
println("進(jìn)入finally代碼塊")
}
println(result)
輸出結(jié)果:
jinlaile
進(jìn)入finally代碼塊
0
七、成員方法、成員變量
- 屬性:或者說(shuō)是成員變量,類范圍內(nèi)的變量;構(gòu)造方法中val/var修飾的都是屬性;類內(nèi)部也是可以定義屬性的的;
/**
* aField和anotherField是類Hello的兩個(gè)屬性;
* 而notField不是屬性,只是普通構(gòu)造方法的參數(shù);
*/
class Hello(val aField: Int, notField: Int){
val anotherField:String = "hello"
}
- 方法:或者說(shuō)是成員函數(shù),類范圍內(nèi)的函數(shù);
在kotlin的類中,自動(dòng)為var修飾成員變量實(shí)現(xiàn)了get、set方法,val修飾的只有g(shù)et方法,因?yàn)関al修飾的是不可變的;如果想要在get、set方法中注入部分邏輯,需要我們重寫其get、set方法,如下;
/**
* Created by serenitynanian on 2017/6/27.
*/
class ClassMemberAndField {
//默認(rèn)訪問(wèn)控制是public
var b = 0
get() {
println("執(zhí)行打印邏輯")
//這個(gè)filed其實(shí)是上面b后真正的值0,這個(gè)filed只有在get、set方法中才能訪問(wèn)的到
return field
}
/**
* 類型java中的set方法
*
* public void setB(int b){
*
* this.b = b ;
* }
*/
set(value) {
println("執(zhí)行打印邏輯")
field = value
}
}
如果想要控制get、set的訪問(wèn)權(quán)限,可以在get、set方法前加訪問(wèn)限制符,比如protected
protected set(value) {
println("logic")
field = value
}
如果說(shuō)get、set中沒(méi)有部分邏輯,只是想要修改訪問(wèn)控制符,只需要如下寫即可:
class ClassMemberAndField {
//默認(rèn)訪問(wèn)控制是public
protected var b = 0
// get() {
// //這個(gè)filed其實(shí)是上面b后真正的值0,這個(gè)filed只有在get、set方法中才能訪問(wèn)的到
// return field
// }
// /**
// * 類型java中的set方法
// *
// * public void setB(int b){
// *
// * this.b = b ;
// * }
// */
// protected set(value) {
// println("logic")
// field = value
// }
//如果只想要控制訪問(wèn)權(quán)限,只需要這樣寫即可:
protected get
protected set
}
在kotlin中聲明成員變量時(shí),編譯器提醒你必須初始化,否則報(bào)錯(cuò);與java不同的時(shí),java會(huì)自動(dòng)給你初始化初始值;但初始值也占內(nèi)存的,所以有時(shí)我們只需要在使用時(shí)才想初始化,這樣怎么辦呢?
只需要在聲明成員變量時(shí)指定一個(gè)lateinit特殊字符就行了;但是在使用之前必須進(jìn)行初始化,否則報(bào)錯(cuò);
var age:Int = 0
//如果不指定lateinit 編譯器會(huì)提醒必須初始化或者abstract,
//指定后就是告訴編譯器,我后面會(huì)進(jìn)行初始化;
//如果你在初始化它之前就使用這個(gè)變量,會(huì)報(bào)錯(cuò)的;
lateinit var name:String
但是lateinit只能放在var修飾的可變變量前面,不能放在val修飾的變量前面;如果想要達(dá)到延遲初始化必須使用delegate,如下:
/**
* lazy需要傳入一個(gè)lambda表達(dá)式,一個(gè)無(wú)參的表達(dá)式返回一個(gè)T類型,
* 這個(gè)T類型就是下面的String類型;
*/
val address:String by lazy {
"dsfa"
}
總結(jié)下屬性訪問(wèn)控制:
var修飾的有g(shù)et、set方法;
val修飾的只有g(shù)et方法,沒(méi)有set方法,因?yàn)関al修飾的變量是不可變的;
總結(jié)下屬性初始化:
屬性的初始化盡量在構(gòu)造方法中進(jìn)行初始化完成;
無(wú)法在構(gòu)造方法中初始化,嘗試降級(jí)為局部變量;
var用lateinit延遲初始化,val用lazy;
可空類型謹(jǐn)慎使用null直接初始化;
八、表達(dá)式(中綴表達(dá)式、分支表達(dá)式、when表達(dá)式)
- 中綴表達(dá)式
- 只有一個(gè)參數(shù),且用infix修飾的函數(shù);
class Book {
infix fun on(place: String) {
}
}
fun main(args: Array<String>) {
//函數(shù)前沒(méi)有使用infix關(guān)鍵字,必須像平常調(diào)用函數(shù)一樣使用
Book().on("my desk")
//如果函數(shù)前面使用了infix,不用在方法前加. 不用在方法后加括號(hào)
Book() on "my dest"
}
2.使用了中綴表達(dá)式后,在調(diào)用時(shí),可以直接使用[對(duì)象] <函數(shù)名> [參數(shù)對(duì)象] 如上示例;
分支表達(dá)式(if),而不只是分支語(yǔ)句
分支表達(dá)式是可以有返回值的,返回值是分支語(yǔ)句中最后一行的值 ;
private const val DEBUG = 1
private const val USER = 0
fun main(args: Array<String>) {
// var mode = USER
// if (args.isNotEmpty() && args[0] == "1") {
// mode = DEBUG
// }
/**
* 從下面可以看出來(lái)if可不只是一個(gè)簡(jiǎn)單的分支語(yǔ)句,
* 它還是一個(gè)表達(dá)式,它是有返回值的,返回值是是分支表達(dá)式中最后一行的值;
* 在上面的分支語(yǔ)句中,只能定義var的mode,因?yàn)楹竺嬉薷模? * 如果使用分支表達(dá)式,就直接可以使用val修飾的mode了,使用如下:
*/
val mode =
if (args.isNotEmpty() && args[0] == "1") {
DEBUG
}else{
USER
}
println("請(qǐng)輸入用戶名:")
val username = readLine()
println("請(qǐng)輸入密碼:")
val password = readLine()
if (mode == DEBUG && username == USERNAME && password == PASSWORD) {
println("超管登錄成功")
} else if (username == USERNAME && password == PASSWORD) {
println("普通用戶登錄成功")
} else {
println("登錄失敗")
}
}
- when表達(dá)式
類似Java中的switch語(yǔ)句,由于case語(yǔ)句中只能判斷String,enum,byte,int等,并且在每個(gè)分支語(yǔ)句中都要寫break;在kotlin中沒(méi)有了switch語(yǔ)句了,使用了when表達(dá)式來(lái)代替了;
public void pause() {
switch (state) {
case BUFFERING:
case PLAYING:
doPause();
break;
default:
//什么都不做
}
}
//下面是kotlin中的when表達(dá)式
fun pause() {
when (state) {
PlayerKt.State.BUFFERING, PlayerKt.State.PLAYING -> doPause()
else -> {
}
}
}
when分支表達(dá)式只要第一個(gè)分支語(yǔ)句執(zhí)行了,不用使用break,后面的分支語(yǔ)句就不會(huì)執(zhí)行了
fun main(args: Array<String>) {
var x = 5
//when表達(dá)式只要第一個(gè)分支語(yǔ)句執(zhí)行了,不用使用break,后面的就不會(huì)執(zhí)行了
when (x) {
is Int -> println("Hello $x")
in 1..100 -> println("$x is in 1..100")//x的值是否在[1,100]
!in 1..100 -> println("$x is not in 1..100")//x的值不在[1,100]
in 1 until 5 -> println("$x is in[0,5)")//x的值是否在[0,5)之間,取不到5
args[0].toInt() -> println("x == args[0]")//這個(gè)是說(shuō)args[0]是否與x一樣
}
}
//when與if一樣,也有返回值,都是每一個(gè)分支最后一行表達(dá)式的值;
val mode = when {
args.isNotEmpty()&& args[0] == "1" -> 1
else -> 0
}
表達(dá)式總結(jié):
- if ... else ...這個(gè)用法基本和Java中一致;
- 表達(dá)式必須具備完整性,如果只有if沒(méi)有else編譯報(bào)錯(cuò);
var b = if (mode == 0) {
0
}
//如果沒(méi)有下面的分支語(yǔ)句,編譯報(bào)錯(cuò)
//賦值時(shí),分支必須完備
else {
1
}
- when表達(dá)式,加強(qiáng)版的switch,支持任意類型,也不用寫break;
支持純表達(dá)式條件分支(類似if)
val mode = when {
args.isNotEmpty()&& args[0] == "1" -> 1
else -> 0
}
必須具備完備性;
九、具名參數(shù),變長(zhǎng)參數(shù),默認(rèn)參數(shù)
- 具名參數(shù):就是在給函數(shù)傳入實(shí)參的時(shí)候,把形參也賦值上,也就是下面在調(diào)用sum函數(shù)時(shí),在傳入實(shí)參的時(shí)候,把a(bǔ)rg1 和 arg2也附帶上(arg1 = 1,arg2 = 2);
具名參數(shù)的順序可以不是固定的;
class ForDemo {
fun sum(arg1:Int,arg2:Int) = arg1 + arg2
}
fun main(args: Array<String>) {
var forDemo = ForDemo()
/**
* 在使用函數(shù)傳入實(shí)參的時(shí)候,把形參也賦上;
* 因?yàn)槭蔷呙麉?shù),所以形參的順序可以不固定
* 也就是明確告訴編譯器這個(gè)2是給arg2的,1是給arg1的
*/
forDemo.sum(arg2 = 2, arg1 = 1)
}
- 變長(zhǎng)參數(shù)
某個(gè)參數(shù)可以接收多個(gè)值;
變長(zhǎng)參數(shù)可以不為最后一個(gè)參數(shù);但是傳入實(shí)參時(shí),可以放在最后用具名參數(shù)表示即可;
如果傳參有歧義,需要使用具名參數(shù)指定某個(gè)特定參數(shù);
最常見(jiàn)的就是main函數(shù),它里面是個(gè)數(shù)組,我們可以將它改變?yōu)樽冮L(zhǎng)參數(shù);它的使用和數(shù)組完全一樣
var array = intArrayOf(1,23,43,25,56)
hello(3.0,1,123,321,2,string = "hello")
//*:Spread Operator這個(gè)符號(hào)現(xiàn)在只支持變長(zhǎng)參數(shù),且只支持array,不支持list
hello(3.0,*array,string = "hello")
fun hello(double:Double,vararg ints: Int,string:String) {
ints.forEach { ::println }
}
在Java中變長(zhǎng)參數(shù)只能是函數(shù)的最后一個(gè)參數(shù),否則編譯器無(wú)法識(shí)別的;
在kotlin中因?yàn)榇嬖诰呙瘮?shù),因此它可以放在任意位置;在傳入實(shí)參的時(shí)候可以用具名函數(shù)放在最后一個(gè)位置;
*:Spread Operator 這個(gè)不是一般的運(yùn)算符;
只支持展開(kāi)Array;
只用于變長(zhǎng)參數(shù)列表的實(shí)參;
不能重載;
- 默認(rèn)參數(shù)
1.就是在函數(shù)編寫時(shí),可以給函數(shù)任意位置的形參一個(gè)默認(rèn)值;在實(shí)際調(diào)用函數(shù)時(shí),這個(gè)參數(shù)可以不傳;
2.但是如果該默認(rèn)參數(shù)不是最后一個(gè),其他參數(shù)使用具名參數(shù)即可,不用寫默認(rèn)的;
3.傳參出現(xiàn)歧義時(shí),也就是編譯器報(bào)錯(cuò),需要使用具名函數(shù),具體請(qǐng)看第二條;
hello(3.0,1,123,321,2,string = "hello")
//* 這個(gè)符號(hào)現(xiàn)在只支持變長(zhǎng)參數(shù),且只支持array,不支持list
hello(ints = *array,string = "hello")
fun hello(double:Double = 3.0 ,vararg ints: Int,string:String) {
ints.forEach { ::println }
}