kotlin学习
参考网址:http://www.jianshu.com/p/6063dee97eca
http://blog.csdn.net/qq562029186/article/details/72519360
一、常量、变量、特殊符号的使用
1、常量的定义 val
2、变量的定义 var
3、特殊符号的使用
1)?的含义:在kotlin中单独使用?表示可为空;
如:
kotlin代码书写格式:
var result = str?.length
java代码书写格式:
if(str == null)
result = null; // 这里result为一个引用类型
else
result = str.length; // 这里result为Int
2)!!的含义:在kotlin中表示一定不能为空;
如:
var str : String? = null
var result : Int = str!!.length
3)?:的含义:在kotlin中表示三元操作符(即三目运算符)
如:
var str : String? = null
var result = str?.length ?: -1
//等价于
var result : Int = if(str != null) str.length else -1
二、定义类
1、属性修饰符
annotation //注解类
abstract //抽象类
final //类不可继承,默认属性
enum //枚举类
open //类可继承,类默认是final的
访问权限修饰符
private //仅在同一个文件中可见
protected //同一个文件中或子类可见
public //所有调用的地方都可见
internal //同一个模块中可见
2、构造器的定义
注:kotlin中的类定义同时也是构造函数,这个时候是不能进行操作的,所以kotlin增加了一个新的关键字init用来处理类的初始化问题,init模块中的内容可以直接使用构造函数的参数。
class Person(name:String,age:int){
init{
//do some thiing
}
}
翻译成java为:
final public class Person{
public Person(String name,int age){
init();
}
private void init(){
//do some thing
}
}
3、java中加入final为不可继承,而kotlin中定义类默认前面带有修饰符final,所以,如果想继承该类,在最前面加上open或者abstract即可。即:
open class Person(name : String, age : int) {
init{
// do some thing
}
}
4、如果init中没有操作,则可以省略
class Person(name : String, age : int)
5、如果连参数也没有,甚至可以这么写
class Person
6、但是当构造参数中的参数、类型变化时可能需要不只是一个构造函数,需要多组构造函数来处理不同view上的数据时,使用constructor加上参数,后面用this加上主构造函数的参数。
次级构造函数
class Person(name:String){
var a = 1
init{
log("This is --> primary constructor,a = $a,name = $name")
}
constructor(name:String,age:Int): this(name){
log("This is --> secondry constructor,a = ${++a}, age = $age")
}
}
// 翻译成java
final class Person{
int a = 1;
public Person(String name){
log("This is --> primary constructor, a=$a, name=$name")
}
public Person(String name, int age){
this(name);
log("This is --> secondry constructor, a=${++a}, age=$age")
}
}
特别注意:
1)同理,如果参数全是固定值,则kotlin会默认创建一个无参数的构造函数;
2)固定值的参数在调用该构造函数时可以不用传。
3)如果P1(name,age)双参,P2(name,age,1)三参,那么kotlin中默认会调用双参的P1来执行,不会去执行P2;
三、定义函数方法
1、求和
方式一:
带有两个 Int 参数、返回 Int 的函数
fun sum(a:Int , b :Int):Int{
return a+b;
}
fun main(args: Array<String>){
print("sum of 3 and 5 is")
print(sum(3,5))
}
result:sum of 3 and 5 is 8
方式二:
将表达式作为函数体、返回值类型自动推断的函数
fun sum(a:Int,b:Int) = a + b
fun main(){
println("sum of 19 and 23 is ${sum(19,23)}")
}
result:sum of 19 and 23 is 42
方式三:
函数返回无意义的值
fun printSum(a:Int,b:Int):Unit{
println("sum of $a and $b is ${a+b}")
}
fun main(args:Array<String>){
printSum(-1,8)
}
result:sum of -1 and 8 is 7
方式四:
Unit 返回类型可以省略
fun printSum(a:Int,b:Int){
println("sum of $a and $b is ${a+b}")
}
fun main(args:Array<String>){
printSum(-1,8)
}
2、数组
使用泛型Array代替数组
Java:
public static void main(String[] args){
// do some thing
}
Kotlin:
fun main(args : Array<String>){
// do some thing
}
3、条件语句
fun max(a:Int,b:Int){
if(a > b)
return a
else
return b
}
//或者简写成:
fun max(a:Int,b:Int) = if( a > b) a else b
4、循环
1)for循环
使用in关键字
fun main(args:Array<String>){
for(arg in args)
print(arg)
for(i in args.indices)
print(arg[i])
for((index,value) in args.withIndex()){
println("index : $index,value : $value")
}
}
2)while循环
和JAVA使用基本一致
fun main(args : Array<String>){
var i = 0
while(i < args.size){
print(args[i++])
}
var j = 0;
do{
print(args[j])
}while(++j<args.size)
}
3)when表达式
和switch用法类似
无返回值时用法:
var x = 3
when (x){
1 -> print(1)
2 -> print(2)
else -> print(5)
}
有返回值
var x = 3
var result = when(x){
1 -> 1
2 -> 2
else -> 5
}
处理相同分支的简写
when(x){
0,1 -> print("x == 0 or x == 1")
else -> print("otherwise")
}
任意表达式做分支的条件
is 表示检查一个值
in 表示范围区间
如果有多个条件同时满足,则调用第一个满足条件的分支
when(x){
parseInt(s) -> print("s encode x")
else -> print("s does not encode x")
}
fun parseInt(str : String) = str.toInt()
when(x){
in 1..10 -> print("x is in the range")
in validNumbers -> print("x is valid")
!in 10..20 -> print("x is outside the range")
else -> print("none of the above")
}
//不去校验任何参数
when{
a is Int -> log("a is Int") // a is Int
else -> log("unknow type")
}
4)if表达式
var a = 3
var b = 5
var max : Int
if(a > b)
max = a
else
max = b
log("普通 max : $max") //普通 max : 5
也可简写成:
max = if(a > b) a else b
log("表达式 max : $max") //表达式 max : 5
5)返回和跳转
JAVA中使用:return、break、continue
Kotlin中新增跳出循环操作:使用标识符@定义label,使用在多层循环的内层向外层跳转。
break和label结合跳转到指定循环层,即使用label1@在跳出循环层的循环操作前面,然后执行完什么操作就跳出时加入代码 break@label1
var list = arrayof(1,2,3)
var child = arrayof("a","b","c")
var result = ""
label1@ for(num in list){ // --> 第一层循环
result +="($num)"
for(word in child){ // --> 第二层循环
if(word.equals("b")){
break@label1 // --> break + label
}
result += word
}
}
log(result) //输出 (1)a
continue和label结合跳转到指定循环层,继续下一次循环。当条件满足时跳出内层循环继续执行外层循环。
label2@ for(num in list){ // --> 第一层循环
result += "($num)"
for(word in child){ // --> 第二层循环
if(word.equals("b")){
continue@label2 // --> continue + label
}
result +=word
}
}
5、继承
在java中使用的是extend,kotlin中继承变成 :
继承五步曲:
1)使用:来做继承
2)父类使用open声明类,不然默认final,无法继承
3)父类构造函数中有参数则在继承时带上
4)子类中使用constructor创建的次级构造函数初始化时使用super关键字
5)父类中的方法如果需要重写,则需要在该方法前加上open关键字,同样,子类如果需要修改或重载该方法也需要在引用处加上override关键字。
1、多级构造函数的继承
父类:
open class ParentClass(name:String){
var a = 1
init{
log("This is --> primary constructor, a=$a, name=$name")
}
}
子类:
//无次级构造函数的写法
class Child(name:String,age:Int) : ParentClass(name){
}
//有次级构造函数的写法:
class Child2 : ParentClass {
constructor(name :String) :super(name){
log("Child2 age : $name")
}
constructor(name:String,age :Int) :super(name){
}
}
2、方法重载
//父类
open class ParentClass(name: String) {
open fun publicMethod(){
log("I am public")
}
}
//子类
class Child(name: String, age : Int) : ParentClass(name){
override fun publicMethod() {
super.publicMethod()
}
}
3、同名方法
当这个父类中有2个方法同名,参数相同时,kotlin中使用尖括号来进行标记父类,以示区分。
//父类
open class ParentClass(name :String){
open fun publicMethod(){
log("I am public from ParentClass")
}
}
interface ParentInterface(name :String){
fun publicMethod(){
log("I am public from ParentInterface")
}
}
//子类
class Child(name :String,age :Int) :ParentClass(name),ParentInterface{
override fun publicMethod(){
super<ParentClass>.publicMethod()
super<ParentInterface>.publicMethod()
}
}
4、抽象
抽象主要是将不同的实现交给不同的子类去完成
//父类
abstract class ParentClass(name: String) {
abstract fun abstractMethod()
}
//子类
class Child(name: String, age : Int) : ParentClass(name){
override fun abstractMethod() {
//To implemented it
}
}
5、属性声明、get、set的使用
1)访问权限
var name :String = ""
private set //但是不能使用private get,因为get的访问权限默认是和属性一致的
2)自定义getter和setter
var有set和get,val只有get
// 自定义get
var size: Int = 2
get() = if (field > 10) 15 else 0
// 调用
var pf = PropertiesFields()
pf.size = 5
Log.d("text", "size : ${pf.size}")
pf.size = 20
Log.d("text", "size : ${pf.size}")
// 输出
size : 0
size : 15
// 自定义set
var size: Int = 2
set(value) {
field = if (value > 10) 15 else 0
}
// 调用和输出同上
3)后端变量
在kotlin的getter和setter是不允许本身的局部变量的,因为属性的调用也是对get的调用,因此会产生递归,造成内存溢出。
kotlin为此提供了一种我们要说的后端变量,也就是field。编译器会检查函数体,如果使用到了它,就会生成一个后端变量,否则就不会生成。我们在使用的时候,用field代替属性本身进行操作。
4)延迟初始化属性(lateinit)
延迟属性类似于JAVA中的懒汉式单例模式
//延迟初始化声明
lateinit var late : String
fun initLate(){
late = "I am late"
}
//先调用方法,再调用属性
var pf = PropertiesFields()
pf.initLate()
Log.d("text",pf.late)
//输出
I am late
注:一定要确保属性是被初始化过的,这样再调用方法,再调用属性,否则会出现属性的NULL异常现象。
6、 接口interface
1)定义接口、方法和属性
接口属性默认是abstract,在接口中不能初始化,必须在实现类中进行初始化,并且在初始化时要加override修饰
在具体类中可以不用覆写接口中的实现方法,直接调用
interface Wing{
fun fly(){
Log.d("text", "Wing -> fly") // 这里实现了接口的方法
}
}
//JAVA中引入接口中的方法使用implement关键字,而kotlin中使用冒号:
class InterfaceLesson : Wing{
override fun fly(){
super.fly() // 在具体类中可以不用覆写接口中的实现方法,直接调用
}
}
// 输出
Wing -> fly
2)继承和多继承重载
接口只能继承接口
a、子类中调用父类方法需要使用super方法调用接口来实现
b、接口没有实现方法,子类必须实现方法且不能调用super
c、多个接口中都有相同的方法,无论接口中是否实现,子类都必须实现该方法。如果二个接口中都没有实现,则子类不能使用super;如果有一个接口实现了,则子类可以使用super且自动调用实现的方法;如果二个接口都实现了该方法,则子类在调用super时需要使用尖括号对调用的父方法进行声明。
例1:
interface Foot{
fun run()
}
interface Car{
fun run()
}
class InterfaceLesson : Foot, Car{
override fun run() {
}
}
例2:
interface Foot{
fun run()
}
interface Car{
fun run() {
Log.d("text", "Car -> run1")
}
}
class InterfaceLesson : Foot, Car{
override fun run() {
super.run()
}
}
// 输出
Car -> run1
例3:
interface Foot{
fun run() {
Log.d("text", "Foot -> run")
}
}
interface Car{
fun run() {
Log.d("text", "Car -> run1")
}
}
class InterfaceLesson : Foot, Car{
override fun run() {
super<Foot>.run()
}
}
// 输出
Foot -> run
7、 扩展Extension
1)扩展方法
java中调用toast需要写成封装的工具类或者BaseActivity中实现toast方法,而kotlin中则不需要这样做,只要在需要的Activity或者Fragment中创建fun方法,实现context的扩展,增加toast方法即可。
// 对Context的扩展,增加了toast方法。为了更好的看到效果,我还加了一段log日志
fun Context.toast(msg : String){
Toast.makeText(this,msg,Toast.LENGTH_SHORT).show()
Log.d("text","Toast msg : $msg")
}
// Activity类,由于所有Activity都是Context的子类,所以可以直接使用扩展的toast方法
class MainActivity : AppcompatActivity(){
override fun onCreate(savedInstanceState : Bundle?){
......
toast("Hello,Extension")
}
}
//输出
Toast msg : Hello,Extension
2)扩展属性
扩展属性也有set和get方法,并且必须要实现这2个方法,不然编译会出错。
//1、 扩展了一个属性paddingH,并给属性增加set和get方法
var View.paddingH : Int
get() = (paddingLeft + paddingRight) / 2
set(value) {
setPadding(value,paddingTop,value,paddingBottom)
}
//2、设置值 activity中通过textview调用
text.paddingH = 100
3)静态扩展
kotlin中的静态用关键字companion表示,它不是修饰属性或方法,而是定义一个方法块,在方法块中的所有方法和属性都是静态的,静态部分的访问和java一致,直接使用类名+静态属性/方法名就可以
//定义静态部分
class Extension {
companion object part{
var name = "Extension"
}
}
//通过类名+属性名直接调用
toast("hello,${Extension.name}")
//输出
Toast msg : hello,Extension
注:companion object一起是修饰关键字,part是方法块的名称。其中,方法块名称part可以省略,如果省略的话,默认缺省名为Companion
8、数据类
保持数据或状态,在JAVA中用bean或entity或自定义model或sp保存持久等等,而kotlin中用data关键字来处理。
data class PersonData(var name : String, var age : Int)
使用data修饰的类叫做数据类,编译器自动从主构造函数定义的全部特性中得到以下成员:
equal()/hashCode()
toString 格式是 "PersonData(name = PersonData,age = 20)"
componentN()方法对应按声明顺序出现的所有属性
copy()方法
1)toString输出
// 定义数据类和普通类
data class PersonData(var name : String , var age : Int)
class PersonNormal(var name : String,val age : Int)
// 分别初始化并进行toString输出
fun test(){
var personD = PersonData("personData",20)
var personN = PersonNormal("personNormal",20)
Log.d("test",personD.toString())
Log.d("test",personN.toString())
}
//输出
PersonData(name = PersonData,age = 20)
包名.类名PersonNormal@地址(26b13e2)
2)copy
修改类中的内容(修改上面的PersonData中的内容)
var personC = personD.copy("Person Copy") //默认第一个参数
var personC = personD.copy(age = 100) //可指定需要修改的参数
Log.d("test",personC.toString())
//输出
PersonData(name = person Copy,age = 100)
3)变量的映射
编译器自动生成的componentN()方法
var personD = PersonData("PersonData",20)
var(name,age) = personD //多声明可翻译成如下
//var name = f1.component1()
//var age = f1.component2()
Log.d("test", "name = $name, age = $age")
//输出
name = PersonData, age = 20
4)数据的序列化
JAVA中通过Parcelable插件自动进行序列化,而kotlin中暂时只能自己实现。
data class PersonData(var name : String, var age : Int, val sex : String) : Parcelable{
override fun writeToParcel(p0: Parcel?, p1: Int) {
p0?.writeString(this.name)
p0?.writeInt(this.age)
p0?.writeString(this.sex)
}
override fun describeContents(): Int {
return 0
}
constructor(source: Parcel) : this(source.readString(), source.readInt(), source.readString())
companion object {
@JvmField val CREATOR: Parcelable.Creator<PersonData> = object : Parcelable.Creator<PersonData> {
override fun createFromParcel(source: Parcel): PersonData {
return PersonData(source)
}
override fun newArray(size: Int): Array<PersonData?> {
return arrayOfNulls(size)
}
}
}
}
##还有待追新研究下…
9、泛型
1)JAVA中泛型的使用
from是生产者,to是消费者。
extends关键字限定类型上限,super关键字限定类型下限。
//定义类
class Data<T>{}
//定义接口
interface Data<T>{}
//定义方法
public <T> void data(T t){}
//使用通配符
//用?表示任何类型,结合extends和super关键字可以限定类型的上限和下限
class Data<T>{}
// extends关键字限定类型上限,表示类型必须是String或String的子类
public void dataUpper(Data<? extends String> d){}
// super关键字限定类型下限,表示类型必须是String或String的父类
public void dataLower(Data<? super String> d){}
//使用通配符解决数据调用问题
fun copy(from: Array<A>, to: Array<A>) {
for (i in from.indices)
to[i] = from[i]
}
2)kotlin中泛型的使用
kotlin中添加了协变注解修饰符:in和out(in对应to,out对应from)
in T:来确保Source的成员函数只能消费T类型,而不能返回T类型,我们也称in修饰的参数为“消费者”
out R:来确保Source的成员函数只能返回R类型,而不能消费R类型,我们也称out修饰的参数为“生产者”
out类似于java中的extends,用来界定类型上限,in类似于java中的super,用来界定类型下限。
//定义类
class Data<T>(var t : T)
//定义接口
interface Data<T>
//定义函数
fun <T> logic(t : T){}
//使用通配符解决数据调用问题
fun copy(from: Array<out A>, to: Array<in A>) {
for (i in from.indices)
to[i] = from[i]
}
另附:
kotlin中新增—–> 星号投射(还需挖掘)
interface Function
10、嵌套类、内部类
1)嵌套类
class Outter{
class Nested{
fun execute(){
Log.d("test", "Nested -> execute")
}
}
}
// 调用
Outter.Nested().execute()
//输出
Nested -> execute
2)内部类
内部类需要使用关键字inner修饰
class Outter{
val testVal = "test"
inner class Inner{
fun execute(){
Log.d("test", "Inner -> execute : can read testVal=$testVal")
}
}
}
// 调用
val outter = Outter()
outter.Inner().execute()
// 输出
Inner -> execute : can read testVal=test //可以读取到testVal的值,即外部类中的值
compare:
嵌套类中可以直接创建实例
val nested : Outter.Nested()
内部类不能直接创建实例,需要通过外部类调用
val outter = Outter()
outter.Inner().execute()
嵌套类不能引用包装类的成员,内部类会带有一个对外部包装类的对象的引用,可以访问外部类中的成员属性和成员函数(如上面例子中的testVal)。
匿名内部类(自定类,实现方法logic,然后再在新类的主程序中调用)
当然还有其他匿名内部类的实现方式,
java中的参考:http://www.cnblogs.com/nerxious/archive/2013/01/25/2876489.html
class Num{
fun logic(a:Int,b:Int){
println("a:$a,b:$b")
}
}
fun main(args: Array<String>){
val num = Num()
num.logic(1,2)
}
//输出
a:1,b:2
11、类委托和委托属性
委托模式是最常用的设计模式的一种,在委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。
1)类委托
interface Base{
fun print()
}
class BaseImpl(val x : Int) :Base{
override fun print(){
Log.d(JTAG, "BaseImpl -> ${x.string()}")
}
}
class Printer(b : Base) : Base by b
fun test(){
val b = BaseImpl(5)
Printer(b).print()
}
//输出
BaseImpl -> 5
Printer没有实现接口Base的方法print(),而是通过关键字by,将实现委托给了b,而输出也和预想的一样。
2)委托属性
语法:val/var <属性名>: <类型> by <表达式>
在 by 后面的表达式是该 委托, 因为属性对应的 get()(和 set())会被委托给它的 getValue() 和 setValue() 方法。 属性的委托不必实现任何的接口,但是需要提供一个 getValue() 函数(和 setValue()——对于 var 属性)。
class Example{ //委托类
var property : String by DelegateProperty()
}
class DelegateProperty{ //被委托类
var temp = "old"
operator fun getValue(ref: Any?,p:KProperty<*>):String{
return "DelegateProperty --> ${p.name} --> $temp"
}
operator fun setValue(ref: Any?, p: KProperty<*>, value: String){ //setValue方法只有委托属性为var时存在,委托属性为val时不存在该方法
temp = value
}
}
fun test(){
val e = Example()
Log.d(JTAG, e.property)
e.property = "new"
Log.d(JTAG, e.property)
}
// 输出
DelegateProperty --> property --> old
DelegateProperty --> property --> new
注:
1、如果委托属性是只读属性,暨val,则被委托类需要实现getValue方法
2、如果委托属性是可变属性,暨var,则被委托类需要实现getValue方法和setValue方法
3、getValue方法的返回类型必须是与委托属性相同或是其子类
4、getValue方法和setValue方法必须要用关键字operator 标记
3)委托属性
三种标准委托属性
延迟属性、可观察属性、map属性
延迟属性:
只有在第一调用的时候才会初始化,在定义的时候不进行初始化
val lazyValue: String by lazy {
Log.d(JTAG, "Just run when first being used")
"value"
}
fun test(){
Log.d(JTAG, lazyValue)
Log.d(JTAG, lazyValue)
}
// 输出
Just run when first being used
value
value
可观察属性:
可观察属性对应的是我们常用的观察者模式
class User {
var name: Int by Delegates.observable(0) {
prop, old, new -> Log.d(JTAG, "$old -> $new")
}
var gender: Int by Delegates.vetoable(0) {
prop, old, new -> (old < new)
}
}
fun test(){
val user = User()
user.name = 2 // 输出 0 -> 2
user.name = 1 // 输出 2 -> 1
user.gender = 2
Log.d(JTAG, user.gender.string()) // 输出 2
user.gender = 1
Log.d(JTAG, user.gender.string()) // 输出 2
}
Delegates.observable() 接受两个参数:初始值和修改时处理程序(handler)。该方法是在被赋值且执行完set方法后才被执行;
Delegates.vetoable()该方法在set执行之前被触发,它返回一个Boolean,如果为true才会继续执行set;否则保持原值。
如上例:第一次赋值 user.gender = 2时,由于2>0,所以old
map映射:
可以使用映射实例自身作为委托来实现委托属性。
class User(val map: Map<String, Any?>) {
val name: String by map
val age: Int by map
}
val user = User(mapOf(
"name" to "John Doe",
"age" to 25
))
println(user.name) // Prints "John Doe"
println(user.age) // Prints 25
这也适用于 var 属性,如果把只读的 Map 换成 MutableMap 的话:
class MutableUser(val map: MutableMap<String, Any?>) {
var name: String by map
var age: Int by map
}
12、函数式编程
class Number{
//声明函数
fun sum(a:Int,b:Int) = a + b
//声明方法
fun logic(a:Int,b:Int,calc:(Int,Int) -> Int){
println("calc : ${calc(a,b)}")
}
}
fun main(args:Array<String>){
val num = Number()
num.logic(1,2,num::sum)
}
//输出
calc : 3
格式: 参数名: (参数类型,参数类型…) -> 输出类型
参数名不一定是方法名,参数类型是这个函数参数的输入类型,如果是无参函数,括号()不可省略!
返回结果用函数返回
class Number{
//声明函数
fun sum(a:Int,b:Int) = a + b
//声明方法
fun logic(a:Int,b:Int,calc:(Int,Int) -> Int){
println(“calc : ${calc(a,b)}”)
}
//调用函数方法返回结果
fun getSumMethod() = this::sum
}
fun main(args:Array<String>){
val num = Number()
num.logic(1, 2, num.getSumMethod())
}
//输出
calc : 3
kotlin中函数可作为一个普通的类,一个参数,一个返回,剩下一个实现方法调用
13、Lambda的使用
class Number{
//声明方法
fun logic(a:Int,b:Int,calc:(Int,Int) -> Int){
println("calc : ${calc(a,b)}")
}
}
fun main(args:Array<String>){
val num = Number()
num.logic(1,2,{x,y -> x+y})
}
//输出
calc : 3
Lambda方式:num.logic(1, 2, {x,y -> x+y})
注:
- Lambda表达式总是被大括号{}包围着
- 函数体跟在 -> 右边,左侧是参数,多个参数用逗号,分割
当函数数字面值只有一个参数,它的声明可以省略(连同 ->),名称为it;而当函数是无参函数时,主程序中调用时也可省略 ->
单参函数:
fun oneParams(one : (Int) -> Int){
println("oneParams : ${one(5)}")
}
fun main(args : Array<String>){
val num = Num()
num.oneParams({it*2})
}
//输出
oneParams : 10
无参函数:
fun empty(emptyM : () -> Unit){
emptyM()
}
fun main(args : Array<String>){
val num = Num()
num.empty({println("empty method")})
}
//输出
empty method
当函数中有某个参数没有用到,可以使用下划线_代替。
fun unusedParams(unused : (Int,Int) -> Int){
println("unusedParams : ${unused(5,10)}")
}
fun main(args : Array<String>){
val num = Num()
num.unusedParams{ _,used -> used*2 }
}
//输出
unusedParams : 20
当函数中最后一个参数是一个函数时,可以把它放在括号()外面
class Num {
fun logic(a: Int, b: Int, calc: (Int, Int) -> Int){
println("calc : ${calc(a,b)}")
}
fun sum(a: Int, b: Int) = a + b
}
fun main(args : Array<String>){
val num = Num()
// 写法1
num.logic(1, 2, {x : Int,y : Int -> x+y})
// 写法2
num.logic(1, 2){x : Int,y : Int -> x+y}
// 写法3
num.logic(1, 2){x,y -> x+y}
}
//如果有多行数据时:
num.logic(1, 2, {x,y ->
println("extra line")
x+y
})
num.logic(1, 2){x,y ->
println("extra line")
x+y
}
匿名函数
fun(x: Int, y: Int): Int = x + y
fun(x: Int, y: Int): Int {return x + y}
Lambda 表达式或者匿名函数(以及局部函数和对象表达式) 可以访问其 闭包 ,即在外部作用域中声明的变量。 与 Java 不同的是可以修改闭包中捕获的变量:
var sum = 0
ints.filter { it > 0 }.forEach {
sum += it
}
print(sum)