您的位置:首页 > 移动开发 > Android开发

kotlin

2017-07-25 10:56 183 查看

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&g
4000
t;){
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()方法


<
ffbb
h6 id="1tostring输出">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)


还有待持久性的学习巩固最新知识,更多高级应用期待你们的分享。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息