- 函数头
- 函数参数
(1)默认值参数
(2)具名函数参数 - Unit函数
- Nothing函数
- 反引号中的函数名
- 匿名函数
(1)匿名函数与隐式返回
(2)匿名函数参数
(3)it关键字
(4)匿名函数类型推断
(5)lambda表达式
(6)定义参数是函数的函数
(7)函数内联
(8)函数引用
(9)函数作为返回类型
(10)闭包
(11)lambda与匿名内部类 - 基本函数
(1)subString
(2)split
(3)replace
(4)== 与 ===
(5)for each
(6)数字类型
(7)安全转换函数
(8)Double转Int,Double类型格式化 - 标准函数库
(1)apply
(2)let
(3)run
(4)with
(5)also
(6)takeIf
(7)takeUnless
1.函数头
定义函数方法的基本形式
2.函数参数
(1)默认值参数
参数带有默认值时,可以省略参数
但是当默认值参数不在第一个时,需要指定参数名才能省略参数
(2)具名函数参数
3. Unit函数
当函数没有返回值时即用Unit,由于优秀的类型推断机制,Unit可以省略不写
当lambda表达式没有必须写Unit
4.Nothing类型
TODO函数的作用就是抛出异常
Nothing类型返回值永远为空
5.反引号中的函数名
在Java类中创建的类 如果Kotlin去调用,需要用··括起来
且可以用于创建一些花花的函数名
fun `**~special**`(){
print("asd")
}
6.匿名函数
定义时不取名字的函数
匿名函数通常作为整体传递给其他函数,或者从其他函数返回
可以根据需要指定特殊规则,轻松定制标准库里的内置函数
(1)匿名函数与隐式返回
- 匿名函数可以当做变量赋值给其他函数类型变量
- 函数的类型由 传入的参数 和 返回值类型 决定
- 和具名函数不一样,除极少数情况外,匿名函数不需要return关键字返回数据,匿名函数会自动或隐式返回函数体最后一行语句的结果
//变量的类型是一个匿名函数 传入参数 : 无参 -》 返回值是 : String类型
val blessingFunction : ()->String
//赋值
blessingFunction = {
val holiday = "New Yeay."
//隐式或自动返回函数体最后一行语句的结果
"Happy $holiday"
}
//函数类型的变量
print(blessingFunction())
(2)匿名函数参数
- 可以不带参数,也可以带一个、多个参数
- 需要带参数时,参数的类型放在匿名函数的类型定义中,参数名则放在函数定义中
//参数类型 //参数名
val blessingFunction2 : (String)-> String = { name ->
val holiday = "New Year"
"$it , Happy $holiday"
}
print(blessingFunction2("jack"))
(3)it关键字
- 定义只有一个参数的匿名函数时,可以用it关键字来表示参数名
- 定义多个参数时,it关键字就没有用了
//参数类型 //参数名
val blessingFunction2 : (String)-> String = { it ->
val holiday = "New Year"
"$it , Happy $holiday"
}
print(blessingFunction2("jack"))
(4)匿名函数类型推断
优秀的类型推断机制可以不用在函数参数申明位置写函数参数
但是必须要函数名后加参数类型(并没有优化)
val blessingFunction3 = { name : String, age : Int ->
val holiday = "New Year"
"$name is $age years old,Happy $holiday"
}
我还是比较喜欢这样写
val blessingFunction3 : (String,Int) -> String = { name , age ->
val holiday = "New Year"
"$name is $age years old,Happy $holiday"
}
(5)lambda表达式
- 匿名函数即为lambda
- 匿名函数的定义为lambda表达式
- 匿名函数返回结果即为lambda结果
(6)定义参数是函数的函数
//具名函数
fun showOnBoard(goodsNmae : String,getDisCountWordss : (String,Int) -> String){
//shuffled()打乱顺序
//last()取最后一个
val hour = (1..24).shuffled().last();
println(getDisCountWordss(goodsNmae,hour))
}
val aaa = { goodsName : String,houe : Int ->
val currendYear = 2021
//$currendYear年 -> 会报错 需要空格
//$currendYear 年 -> 打印出来会出一个空格
//${currendYear}年 -> 不会报错也不会多打印空格
"${currendYear}年,双11${goodsName}促销倒计时: ${houe}小时"
}
showOnBoard("卫生纸",aaa)
也可以直接将lambda表达式作为函数参数
showOnBoard("卫生纸",{ goodsName : String,houe : Int ->
val currendYear = 2021
"${currendYear}年,双11${goodsName}促销倒计时: ${houe}小时"
})
简化 ->
如果一个函数的lambda参数排在最后,或者是唯一的参数,那么括住lambda值参的一对圆括号就可以省略
showOnBoard("卫生纸"){ goodsName : String,houe : Int ->
val currendYear = 2021
"${currendYear}年,双11${goodsName}促销倒计时: ${houe}小时"
}
(7)函数内联
问题:
在JVM上,定义的lambda会以对象实例的形式存在,JVM会为所有同lambda打交道的变量分配内存,产生内存开销,带来严重的性能问题
解决:
函数内联,JVM就不需要使用lambda对象实例,避免变量内存分配,哪里需要lambda,编译器就会将函数体复制粘贴到哪里
使用lambda的递归函数无法内联
inline fun showOnBoard(goodsNmae : String,getDisCountWordss : (String,Int) -> String){
//shuffled()打乱顺序
//last()取最后一个
val hour = (1..24).shuffled().last();
println(getDisCountWordss(goodsNmae,hour))
}
(8)函数引用
要把函数作为参数传递给其他函数使用,除了传递lambda表达式,还可以传递函数引用,函数引用可以把一个具名函数转换成一个值参
使用lambda表达式的地方都可以使用函数引用
fun main() {
showOnBoard2("牙膏",::getDiscountName2)
}
fun getDiscountName2(goodsNmae: String,hour : Int) : String {
//shuffled()打乱顺序
//last()取最后一个
val currendYear = 2077;
return "${currendYear}年,双11${goodsNmae}促销倒计时: ${hour}小时"
}
//具名函数
fun showOnBoard2(goodsNmae : String,hanshu : (String,Int) -> String){
val hour = (1..24).random();
println(hanshu(goodsNmae,hour))
}
::引用函数
(9)函数作为返回类型
fun main() {
val configDiscountWords = configDiscountWords()
print(configDiscountWords("沐浴露"))
}
fun configDiscountWords() : (String) -> String {
val currendYear = 2077
val hour = (1..24).random()
return {goodsName ->
"${currendYear}年,双11${goodsName},促销倒计时: ${hour}小时"
}
}
(10)闭包
-
在kotlin中,匿名函数可以修改并引用定义在自己的作用域之外的变量,匿名函数引用着定义自身的函数里的变量
-
能接受函数或者返回函数的函数叫做高级函数,高级函数广泛应用于函数式编程当中
Kotlin中的lambda表达式就是闭包
fun configDiscountWords() : (String) -> String {
val currendYear = 2077
val hour = (1..24).random()
return {goodsName ->
"${currendYear}年,双11${goodsName},促销倒计时: ${hour}小时"
}
}
(11)lambda与匿名内部类
在java中,如果要传递函数
- 创建接口
- 实现匿名内部类 -> 或者实现接口,并将接口对象传入
public static void main(String[] args) {
showOnBoard("牙膏", new DiscountWords() {
@Override
public String getDiscountWords(String goodsName, int hour) {
return goodsName + hour + goodsName;
}
});
}
//接口
public interface DiscountWords{
String getDiscountWords(String goodsName,int hour);
}
//方法
public static void showOnBoard(String goodsName,DiscountWords discountWords){
int hour = new Random().nextInt(24);
System.out.println(discountWords.getDiscountWords(goodsName,hour));
}
Kotlin匿名内部类
fun main() {
// 变量名 变量类型是函数 赋值
val aaa : (goodsName : String,hour : Int) -> String = {goodsName,hour ->
val cur = 2021
"${cur}年,双11${goodsName}商品促销倒计时${hour}小时"
}
showOnBoard3("袜子",aaa);
println("")
print("___________")
println("")
showOnBoard3("洗发水",{goodsName : String,hour : Int ->
val cur = 2021
"${cur}年,双11${goodsName}商品促销倒计时${hour}小时"
})
println("")
print("___________")
println("")
showOnBoard3("沐浴露"){goodsName : String,hour : Int ->
val cur = 2021
"${cur}年,双11${goodsName}商品促销倒计时${hour}小时"
}
}
// 参数1 是String 参数2 是lambda表达式
private fun showOnBoard3(goodsName : String,getDiscouns : (String,Int) -> String) : Unit{
val hour = (1..24).random()
print(getDiscouns(goodsName,hour))
}
7.基本函数
(1)subString
java
const val NAME = "Jimmy's friend"
val indexOf = NAME.indexOf('\'')
val str = NAME.substring(0,indexOf)
Kotlin
val str2 = NAME.substring(0 until indexOf)
(2)split
split函数返回的是List集合,List集合又支持解构语法特性,它允许在一个表达式里给多个变量赋值,解构常用来简化变量赋值
const val NAMES = "Jack,Ross,Jason"
val split : List<String> = NAMES.split(',')
val(origin : String,dest : String,proxy : String) = NAMES.split(',')
println("${origin},${dest},${proxy}")
(3)replace
val str3 = "The people's public og China"
val str4 = str3.replace(Regex("[aeiou]")){
when(it.value){
"a" -> "8"
"e" -> "6"
"i" -> "9"
"o" -> "1"
"u" -> "3"
else -> it.value
}
}
(4)== 与 ===
在kotlin中
用 == 比较来比较两个字符串是否匹配
用 === 来比较 两个变量是否指向内存堆上同一对象
在Java中
用 == 做引用比较
用equals方法作结构
val str = "Jason"
val str2 = "Jason"
val str3 = "jason".capitalize()
//比较内容
println(str == str2)
//比较对象
//字符串常量池 字符串是不可变的
println(str === str2) //true
println(str === str3) //false
(5)for each
val str3 = "The people's public og China"
str3.forEach {
println("$it *")
}
(6)数字类型
(7)安全转换函数
Kotlin提供了toDoubleOrNull 和 toIntOrNull这样的安全转换函数
如果数值不能正确转换
与其触发异常还不如返回null
// val number : Int = "8.98".toInt() fasle
val number : Int? = "8.98".toIntOrNull() //null
(8)Double转Int,Double类型格式化
println(8.95256.toInt()) //8
println(8.95256.roundToInt()) //9 四舍五入
"%.2f".format(8.95756) //8.96 保留两位小数 四舍五入
8.标准函数库
(1)apply
apply函数可以看作是一个配置函数,可以传入一个接收者
然后调用一系列函数来配置它
如果提供lambda表达式,会返回配置好的接收者
val file1 = File("C:\\Users\\10185\\Desktop\\a.txt");
file1.setReadable(true)
file1.setWritable(true)
file1.setExecutable(false)
// this 隐式调用
val file2 = File("C:\\Users\\10185\\Desktop\\a.txt").apply {
setReadable(true)
setWritable(true)
setExecutable(false)
//返回file2对象
}
(2)let
let函数能是某个变量作用于其lambda表达式里,让it关键字能引用它
let与apply比较
(1)let会把接收者传递给lambda,apply什么都不传
(2)let返回lambda表达式最后一行,apply返回当前接收者
fun main() {
//使用let
val let = listOf<Int>(3, 2, 1).first().let {
//返回最后一行
it * it
}
println(let)
//不用let
val last = listOf<Int>(3, 2, 1).last()
val last2 = last * last
//使用let
formatGreaeting(null);
formatGreaeting("jack");
//不使用let
formatGreaeting2(null)
formatGreaeting2("ross")
}
fun formatGreaeting(guestName : String?) : String{
return guestName?.let {
"Welcome $it"
} ?: "what is your name"
}
fun formatGreaeting2(guestName : String?) : String{
return if (guestName == null){
"what is your name"
}else{
"Welcome ${guestName}"
}
}
(3)run
作用域与apply差不多
但是与apply不同的是:run函数不返回接收者,*返回的是lambda结果*
->true、false、最后一行数据
val file = File("C:\\Users\\10185\\Desktop\\a.txt")
val result = file.run {
//一次读完所有
readText().contains("great")
//返回true or false
}
run可以 函数引用,相较于传统的方法是支持链式调用
fun main() {
// 链式调用
"The People's Republic of China"
//函数引用
.run(::isLong) //返回true
.run(::showMessage) //返回字符串
.run(::println) //打印字符串
}
fun isLong(name : String) : Boolean{
return name.length >= 10
}
fun showMessage(isLong : Boolean) : String{
return if (isLong){
"Name is to long"
}else{
"Please rename"
}
}
(4)with
是run的变体,他们的功能行为是一样的
但with的调用方式不同,调用with时需要值参作为其第一个参数传入
val run = "The People's Republic of China".run {
length >= 10
}
val with = with("The People's Republic of China") {
length >= 10
}
(5)also
also和let功能相似
和let一样,also也是把接收者作为值参传给lambda
但是有一点不同:
also返回接收者对象,let返回lambda结果
因为这个差异,also尤其适合针对同一原始对象,利用其副作用做事
既然返回的是接收者,就可以*基于原始接收者对象执行额外的链式调用*
var fileContents:List<String>
//返回接收对象
val also : File = File("C:\\Users\\10185\\Desktop\\a.txt")
.also {
print(it.name)
}.also {
fileContents = it.readLines()
}
//返回执行结果
val let : String = File("C:\\Users\\10185\\Desktop\\a.txt").let {
fileContents = it.readLines()
it.name
}
(6)takeIf
takeIf需要传入一个接收者
takeIf函数需要判断lambda中提供的表达式,给出true、false的结果
如果判断结果是true ->返回接收者对象
如果判断结果是false->返回null
如果需要判断某个条件是否满足,再决定是否可以赋值变量或执行某项任务,takeIf就很有用
val readText = File("C:\\Users\\10185\\Desktop\\a.txt")
.takeIf {
it.exists() && it.canRead()
}?.readText()
println(readText)
//不使用takeIf
val file = File("C:\\Users\\10185\\Desktop\\a.txt")
if(file.exists() && file.canRead()){
println(file.readText())
}else{
print(null)
}
(7)takeUnless
与takeIf相反
val readText = File("C:\\Users\\10185\\Desktop\\a.txt")
.takeUnless {
it.isHidden
}?.readText()
println(readText)