变量申明
- 通过var关键字申明可变属性
- 通过val关键字申明不可变属性
- kotlin都是引用类型,无值类型
- 基本类型包括Int,String,Double,Float,Long,Short,Char,Boolean,基本与java一致。
var hello:String = "hello"
val world:String ="world"
hello = "hello2" //ok
world = "world2" // 提示常量不可以修改
函数
函数申明包含五部分
- 作用域 private/public,默认情况下所有函数都是public
- 关键字 fun
- 函数名
- 参数列表
- 返回值
private fun test(name:String,age:Int):String
默认参数
在申明函数时,可以指定默认参数
private fun test(x : String , y:Int = 0): String {
return "$x $y - ok"
}
参数顺序
函数发生调用时, 可以通过形参名来指定传入的参数,而不用管参数顺序.如果不指定形参名,则需按照顺序传入参数.
fun main() {
println( test(y=12 , x="123"))
}
private fun test(x : String , y:Int ): String {
return "$x $y - ok"
}
函数返回值
- 可以指定函数的返回值类型
- 当没有指定返回值类型时,默认返回的是kotlin.Unit类型
private fun testA(){
}
println(testA()) // kotlin.Unit
TODO
- TODO在kotlin中是一个函数,这个函数会抛出异常,返回Nothing类型
- Nothing是一个class,无法初始化
private fun testA(x : String , y:Int ): String {
TODO("to be done")
}
//输出
Exception in thread "main" kotlin.NotImplementedError: An operation is not implemented: to be done
at ClassTestKt.testA(ClassTest.kt:110)
at ClassTestKt.main(ClassTest.kt:106)
at ClassTestKt.main(ClassTest.kt)
特殊名字的函数
如包含空格,或者特殊字符的函数,用反引号引用,它的主要作用是当出现kotlin和java混编时,一些java中的函数命名在kotlin中可能是关键字,比如is,这时候引用需要使用``来标记
`a special func`()
private fun `a special func`(){
println("special func....")
}
匿名函数
- 匿名函数的类型由参数和返回值决定 ()->String
- 匿名函数的最后一条语句就是函数的返回值,不需要显式填写return
- 替代java中的匿名内部类或者接口的传递
- 匿名函数又经常被成为lambda表达式
匿名函数定义
匿名函数定义 var 函数名:函数类型 = {函数体}
//完整申明
var classTest1:(String , Int)->String = {name,age->
"classTest1"
}
println(test()) //test
println(test) //(String , Int) -> kotlin.String
println(test is Function<String>) //true
//简化申明
var test = {println("this is a function")}
匿名函数的参数
- 匿名函数可以不带,或带多个参数
- 当只有一个参数时,在匿名函数的函数体中用it表示这个参数
- 匿名函数的参数类型放在函数类型定义中,参数名放在函数的函数体中
//两个参数必须指明参数名,如 s ,i
val test2 : (String , Int)->String = {s,i->
"$s , $i ,1234"
}
//一个参数不需要指明,默认用it表示
val test3 : (String)->String = {
"$it ,test3"
}
//无参类型,可以省略()->returnType,返回类型自动推断
val test = {
val s = "hello"
"$s test3"
}
匿名函数作为函数参数
大括弧表示匿名函数
使用一对大括弧,表示一个匿名函数,根据形参类型来匹配函数参数。
println (test6({
"1234"
}))
fun test6(x: ()->String): String {
return "test6 ${x()}"
}
匿名函数是唯一参数或者最后一个参数的简写
最后面的括号可以省略
println (test6{
"1234"
})
fun test6(x: ()->String): String {
return "test6 ${x()}"
}
//lambda参数,从test7的括号内移出到括号外面了。
println(test7("jack "){name,i->
"$name - $i - ok"
})
fun test7(name :String , x: (String , Int)->String): String {
return "test6 ${x(name , 123)}"
}
省略写法与匿名函数的定义非常像,中间的=号为最重要的区分。
test7("jack "){name,i->
"$name - $i - ok"
}
val test2 = {name:String,i:Int->
"$name , $i ,test2"
}
函数内联 inline
- inline将标记为内联的函数的函数体拷贝到调用的方法里
- 主要目的是为了优化lambda表示式,匿名函数或者闭包
- 当没有使用内联标记时,会有以下两种情况
-
- 如果lambda没有捕获外部变量,也就是没有传入参数,lambda会自动产生一个静态类来表示lambda
-
- 如果有捕获外部变量,则每次都会新创建一个对象
内联方式
fun main() {
println(showABC{
"name is $it"
})
}
inline fun showABC(testC:(String)->String):String{
var outer = "jack"
return testC(outer)
}
public final class TestKt {
public static final void main() {
int $i$f$showABC = false;
String outer$iv = "jack";
int var3 = false;
String var4 = "name is " + outer$iv;
boolean var5 = false;
System.out.println(var4);
}
// $FF: synthetic method
public static void main(String[] var0) {
main();
}
@NotNull
public static final String showABC(@NotNull Function1 testC) {
int $i$f$showABC = 0;
Intrinsics.checkNotNullParameter(testC, "testC");
String outer = "jack";
return (String)testC.invoke(outer);
}
}
非内联方式
public final class TestKt {
@NotNull
private static final Function0 abc;
public static final void main() {
String var0 = showABC(abc);
boolean var1 = false;
System.out.println(var0);
}
// $FF: synthetic method
public static void main(String[] var0) {
main();
}
@NotNull
public static final Function0 getAbc() {
return abc;
}
@NotNull
public static final String showABC(@NotNull Function0 a) {
Intrinsics.checkNotNullParameter(a, "a");
return (String)a.invoke();
}
static {
abc = (Function0)null.INSTANCE;
}
}
函数引用传递
使用::来获取函数引用
fun main() {
println(showABC(::testRef))
}
fun showABC(testC:(String)->String):String{
var outer = "jack"
return testC(outer)
}
fun testRef(name:String ):String{
println("name $name ")
return "abc"
}
函数作为返回值的两种写法
val func = testReturnFunc("1")
println(func("10"))
//返回匿名函数
fun testReturnFunc(a:String):(String)->String{
return {input->
"1000 + $input + $a"
}
}
//返回著名函数
fun testReturnFunc(a:String):(String)->String{
return fun(input:String):String{
return "1000 + $input + $a"
}
}
闭包
- 函数和对其周围状态一起构成闭包,闭包使得函数内部可以对外部作用域定义的变量进行访问
闭包的作用域
fun test2(): () -> Int {
var str = 0
return fun(): Int {
return str++
}
}
fun main(args: Array<String>) {
val makeFun= test2()
println(makeFun())
println(makeFun())
println(makeFun())
println(makeFun())
}
//decode的java代码 ,仅仅new了一次test2的对象
public final class TestKt {
@NotNull
public static final Function0 test2() {
final IntRef str = new IntRef();
str.element = 0;
return (Function0)(new Function0() {
// $FF: synthetic method
// $FF: bridge method
public Object invoke() {
return this.invoke();
}
public final int invoke() {
IntRef var10000 = str;
int var1;
var10000.element = (var1 = var10000.element) + 1;
return var1;
}
});
}
public static final void main(@NotNull String[] args) {
Intrinsics.checkNotNullParameter(args, "args");
Function0 makeFun = test2();
int var2 = ((Number)makeFun.invoke()).intValue();
boolean var3 = false;
System.out.println(var2);
var2 = ((Number)makeFun.invoke()).intValue();
var3 = false;
System.out.println(var2);
var2 = ((Number)makeFun.invoke()).intValue();
var3 = false;
System.out.println(var2);
var2 = ((Number)makeFun.invoke()).intValue();
var3 = false;
System.out.println(var2);
}
}
条件判断
if /else if /else
fun main() {
var s = "test"
println("${if(s is String) "is string" else "not string" } ")
}
三元表达式
- kotlin没有三元表达式 可以采用 两种方式替代
if( bool ) b else c
//或者
b.takeIf{bool} ?:c
when
- kotlin没有switch/case,而是采用when来代替,when可以返回不同类型的值
- when必须有个else来做fallback
- 所有需要if /else if的情况都可以用when来代替
var s = "test"
var result = when(s){
"test"->1
"hello"->"abc"
"world"->3
else -> 4
}
string模板
类似于groovy和dart,可以通过${},将变量替换为相应的值,同时支持语句执行
println("value = $result class = ${result.javaClass} ${if(s is String) "is string" else "not string" } ")
空安全null
- java中运行时出现的异常都继承自RuntimeException,这些异常是不强迫要求try...catch的,但是其他编译器异常,需要处理。
- kotlin基于java中经常出现的运行时异常NullPointerException做了改进,强迫在编译期对空进行处理。
除非另有规定,否则变量不能为空。
fun testNull(){
var abc = "aaa"
abc = null //编译器提示异常 Null can not be a value of a non-null type String
}
可空变量必须显式申明
通过在定义时,加上?,表示变量可为空.
fun testNull(){
var abc:String? = "aaa"
abc = null
}
空安全函数调用
安全调用操作符 ?.
当变量为空时,不会调用后续方法
var abc:String? = "aaa"
abc?.chars()
Elvis 操作符 ?:
- 变量非空时为左边的值,为空时,取右边的值.
- 将空类型变量安全的转化为非空类型
fun testNull(){
var i = null
var j = i ?: 10
println(j)
}
!! 操作符
将可空类型变量转化为非空类型变量,如果变量为空,则抛出KotlinNullPointerException
var i:String? = null
println(i!!.length)
//输出
Exception in thread "main" java.lang.NullPointerException
at ClassTestKt.main(ClassTest.kt:111)
at ClassTestKt.main(ClassTest.kt)