Kotlin基础 - 第七章lambda表达式
kotlin中的lambda表达式
#### [kotlin官方文档 https://www.kotlincn.net/docs/reference/](https://www.kotlincn.net/docs/reference/) ####
lambda即lambda表达式,简称lambda。本质上是可以传递给其它函数的一小段代码。有了lambda,可以轻松地把通用代码结构抽取成库函数。lambda最常见的用途是和集合一起配合。kotlin甚至还拥有带接收者的lambda,这是一种特殊的lambda。
lambda的表达式和成员引用
lambda简介:作为函数参数的代码块
代码中存储和传递一小段行为是常有的任务。在老版本Java中很多需要匿名内部类来实现(java8也引入了lambda,大为改观),语法太过啰嗦。
函数式编程提供了另外一种解决方案:把函数当作值来对待。可以直接传递函数,而不需要先声明一个类再传递这个类的实例。使用lamdba表达式不仅会使代码更简洁,并且可以高效地直接传递代码快作为函数参数。
笔者注,后文我会省略大段的lambda理论文字的介绍,因为在我的认知中,接触了java8(尤其是Android)的一定对lambda有一定的认知,如果没有请自行查阅资料。
//点击监听 java lambdabtn.setOnClickListener(view ->...);//点击监听 kotlin lambdabtn.setOnClickListener { ... }
kotlin的lambda相对于java8的lambda语法更简洁,事实上它们做的事情是一样的,都是替代了匿名内部类对象。
良好的编程风格主要原则之一就是避免代码中的任何重复。kotlin的lambda可以帮助避免代码重复(因为对集合执行通常遵循通用模式)。
//建立数据类Person作为数据源data class Person(val name: String, val age: Int) {}
假设需求为找到列表年龄最大的人,平时我们打代码可能是这样的。
data class Person(val name: String, val age: Int) {}//未使用lambda表达式fun findTheOldest(people:List<Person>){var maxAge=0 //存储最大年龄var theOldest:Person?=null //存储年龄最大的人for (person in people){if (person.age>maxAge){ //循环赋值比现在年龄大的改变最大值maxAge=person.agetheOldest=person}}println(theOldest)}//数据源val people = listOf(Person("jack", 29),Person("nick", 23),Person("jone", 26))findTheOldest(people)//Person(name=jack, age=29)
如果我们使用lambda表达式,我对门的代码直接可以简化成下面的样子
class Person(var name: String, var age: Int) {}val people = listOf(Person("小明", 12),Person("小红", 12),Person("小花", 12),Person("小美", 12),Person("小帅", 12))println(people.maxBy { it.age % 2 }!!.name) //小帅
maxby函数可以在任何集合上调用,且只需要一个实参:一个函数,指定比较哪个值来找到最大元素。{it.age}就是实现了这个逻辑的lambda。它接收一个集合中元素作为实参(it引用)并且返回用来比较的值。如上述代码中,集合元素是Person对象,用来比较的是存储在其age属性中的年龄。
如果lambda刚好是函数或者属性委托,可以用成员引用替换。
val people = listOf(Person("小明", 12),Person("小红", 14),Person("小花", 20),Person("小美", 10),Person("小帅", 13))//注意两者的区别这种传递属性值的方式应当使用在括号里面println(people.maxBy(Person::age)!!.name) //小花
主使用成员引用的方式传递闭包(代码块)的时候仅可以传递属性值
lambda表达式语法
如前所述(更多详情自行查阅资料或参 见初识lambda) lambda把一小段行为进行编码,既能当作值传递又能独立声明到一个变量中存储。
var sum = { x: Int, y: Int -> x + y }println(sum(1, 6)) //7
也可以无意义的直接调用lambda表达式
//直接调用无意义(等同于直接执行lambda具体代码){ println(42)}()//42
正确姿势应该使用kotlin库函数run。
//正确姿势un调用run { println(42) }//42
回到people的例子,不用任何简明语法来重写。
//未简化的标注lambdapeople.maxBy({ P:Person ->P.age} )
kotlin有一种语法约定,如果lambda表达式是函数调用的最后一个实参,它可以放到括号外面。
//lambda是函数调用的最后一个实参,可以放到()外people.maxBy( ){ P:Person ->P.age}
当lambda是函数唯一实参时,还可以去掉()
//lambda是函数唯一实参,可以省略()people.maxBy{ P:Person ->P.age}
三种语法语义完全一样,但是最后一种更易读。但是当lamdba有两个或多个实参时,不能把超过一个的lambda放到外面,推荐使用常规语法。
把当然,lambda也能作为命名实参传递
val people = listOf(Person("小明", 12),Person("小红", 14),Person("小花", 20),Person("小美", 10),Person("小帅", 13))var str = people.joinToString(separator = " ", transform = { people: Person -> people.name })println(str) // 小明 小红 小花 小美 小帅
因为只有一个实参,所以可以放在括号外
var str = people.joinToString(separator = " ") { people: Person -> people.name }println(str) // 小明 小红 小花 小美 小帅
甚至可以根据类型推导特性而移除参数类型。
//显示的写出参数推导类型var str = people.joinToString(separator = " ") { people: Person -> people.name }//推导出参数类型var str = people.joinToString(separator = " ") { people -> people.name }println(str)
类型推导与局部变量一样,如果能成功被推导,就不需要显示的指定。
使用默认参数名称(注意)
//使用默认参数名称people.maxBy { it.age} //"it"是自动生成的参数名称
默认名称it只会在实参名称没有显示的指定时候才会生成。it能大大缩短简化代码,但是不应该滥用,尤其是在lambda嵌套情况下,最好显示声明lambda参数。否则很难搞清it引用的到底是哪个值,本末倒置。
如果用变量存储lambda,就没有可以推断出参数类型的上下文,必须显示的指定参数。
//变量存储lambda,必须显示指定参数类型var getAge = {person:Person -> person.age}//输出结果:Person(name=小花, age=20)println(people.maxBy(getAge))
lambda当然也能包含更多语句。
val sum = { x: Int, y: Int ->println("this is $x and $y and sum is")x+y}//this is 1 and 2 and sum is// 3println(sum(1,2))
在作用域访问变量
lambda表达式有个形影不离的概念:从上下文中捕捉变量。
当在函数内部声明一个匿名内部类的时候,能够在这个匿名内部引用这个函数的参数和局部变量。lambda同样可以。
//使用lambda进行循环的函数fun printMessagesWithPrefix(messages:Collection<String>,prefix:String){//接收lambda作为实参,指定对每个元素的操作messages.forEach{//在lambda中访问函数的参数println("$prefix and $it")}}printMessagesWithPrefix(errors,"Error:")//Error: and 403 Forbidden// Error: and 404 not Found
这里kotlin和java的一个显著区别就是,在kotlin中不会仅局限于访问final变量,在lambda内部也可以修改这些变量。
fun printProblemCounts(respones: Collection<String>) {//声明在lambda内部访问的变量var clientErrors = 0var serverErrors = 0respones.forEach {if (it.startsWith("2")) {//在lambda中修改变量clientErrors++}else if(it.startsWith("4")||it.startsWith("5")){serverErrors++}}println("$clientErrors for client,$serverErrors for server")}printProblemCounts(arrayListOf("504","201"))// 1 for client,1 for server
kotlin允许在lambda中内部访问非final变量甚至修改它们,我们称这些变量被lambda捕捉。
默认情况下,局部变量的生命期被限制在这个函数中。但是如果它被lambda捕捉了,使用这个变量的代码可以被存储并稍后执行。原理是当你捕捉final变量的时候,它的值和使用这个值的lambda代码一起存储。而对于非变量来说,它的值被封装在一个特殊包装器中,当改变这个值时候,对包装器的引用会和lambda一起存储。
//模拟捕捉可变变量类(捕捉的实现原理)//注意类中的局部参数必须使用 var修饰class Ref<T>(var value: T)val counter = Ref(2)//并不是变量被捕捉,但是存储字段值实际值是可以修改的val inc = { counter.value++ }//简单理解,对象的地址不可变,但是对象的var属性是可以被访问和修改的//实际代码隐藏包装器var counter =0val inc = {counter++}
当捕捉变量var时候,它的值会如上述代码一样作为Ref类的一个实例被存储下来,Ref是final的当然能轻易捕捉,而实际值又是存储在其字段里,所以能在lambda里修改。
着重强调注意的是,如果lambda被用作事件处理器或者在其它异步执行的情况,对局部变量修改只会在lambda执行时候发生。
//错误代码fun tryToCountButtonClicks(button: View): Int {var clicks = 0button.setOnClickListener{clicks++}return clicks}
函数返回值始终是0,因为onClick是在函数返回后调用(java的接口回调),即此函数执行完后才会执行onClick,正确姿势是不应该把clicks存储在函数的局部变量中。
成员引用
kotlin和java8一样,如果把函数转换成一个值,就可以直接转换它使用::运算符转换。
data class Person(var name: String, var age: Int) {//成员引用val getName = Person::name////等价val getName = { person: Person -> person.name }}
成员引用提供简明语法,来创建一个调用单个方法或者访问单个属性的函数值。::把类名称和引用成员(一个方法或属性)隔开。
不管引用的是函数还是属性,都不应该在成员引用的名称后面加()。成员引用和调用该函数的lambda具有一样的类型。
//成员引用people.maxBy(Person::age)
成员引用还可以引用顶层函数
//顶层函数fun salute()= println("Salute")//引用顶层函数,::salute当作实参传递给了lambdarun(::salute)println(run(::salute))//Salute
如果lambda要委托一个或者多个参数的函数,提供成员引用代替非常方便。
//将lambda委托给sendEmail函数val action={person:Person,msg:String ->sendEmail(person,msg)}//成员引用代替,上面的代码和下面是等价的val nextAction = ::sendEmail
也可以使用构造方法引用存储或者延期执行创建类的实例。
data class Person(var name: String, var age: Int) {//成员引用val getName = Person::name////等价val getName2 = { person: Person -> person.name }}//调用委托createPerson为Person的构造函数val createPerson = ::Personval p = createPerson("小红", 27)//Person(name=小红, age=27)println(p)
其实可以理解为将方法的调用代码存储在变量 createPerson 中
也可以用同样的方式来引用扩展函数。
//Person的扩展函数fun Person.isAdult()=age>=23//成员引用,等价person.isAdultval isHave =Person::isAdult
对应代码
data class Person(var name: String, var age: Int) {//成员引用val getName = Person::name////等价val getName2 = { person: Person -> person.name }//Person的扩展函数fun isAdult() = age >= 25}//成员引用,等价person.isAdultval isHave = Person::isAdultval p = createPerson("小红", 27)//trueprintln(isHave(p))
集合式函数API
笔者注:集合的lambda函数式是存在于所有支持的语言中的,所以笔者会简略带过这些函数的效果,着重于这些函数在kotlin里的运用。如果有心了解更多,请自行查阅资料(java推荐lambda函数式编程或了解rxjava操作符)
filter和map
-
filter即过滤,它会遍历集合并选出应用给定lambda后返回未true的元素。使用它可以移除不满足条件的元素(数据源并不会改变)
val list = listOf(1, 2, 3, 4, 5, 6)println(list.filter { it % 2 == 0 })//[2, 4, 6]val people = listOf(Person("小明", 22),Person("小红", 24),Person("小花", 29),Person("小美", 30),Person("小帅", 19))println(people.filter { it.age > 24 })//[Person(name=小花, age=29), Person(name=小美, age=30)]
-
map对集合每一个元素应用给定的函数并把结果收集到一个新集合,即元素变换。
val list = listOf(1, 2, 3, 4, 5, 6)println(list.map { it * 2 })//[2, 4, 6, 8, 10, 12]val people = listOf(Person("小明", 22),Person("小红", 24),Person("小花", 29),Person("小美", 30),Person("小帅", 19))println(people.map {if (it.age > 24) {it.age = it.age + 4}it})//[Person(name=小明, age=22), Person(name=小红, age=24), Person(name=小花, age=33), Person(name=小美, age=34), Person(name=小帅, age=19)]
-
lamdba当然可以链式调用。
val people = listOf(Person("小明", 22),Person("小红", 24),Person("小花", 29),Person("小美", 30),Person("小帅", 19))// 找出年所有成年人,把他们的年龄加4println(people.filter { it.isAdult() }.map {if (it.age > 24) {it.age = it.age + 4}it})//[Person(name=小花, age=33), Person(name=小美, age=34)]
-
使用filter找出年龄最大的人
//找出年龄最大的人(此代码有不足,每次遍历都会查找最大年龄,性能损耗大,同一个事只做一次)println(people.filter { it.age == people.maxBy(Person::age)?.age })//[Person(name=小美, age=30)]
此代码的问题在于,它会对每个人都会重复寻找最大年龄的过程,如果集合里有百个,千个人(元素),简直要原地爆炸。
-
改进方案
//先找出集合中年龄最大的人的年龄val maxAge = people.maxBy(Person::age)?.age//然后再用filter去过滤println(people.filter { it.age == maxAge})//[Person(name=小美, age=30)]
如果没有必要当然就不需要重复计算。lambda表达式隐藏了底层操作的复杂性,必须牢记自己的代码在干什么。
- 也能对map进行过滤和变换。
filterKeys(过滤map的键),mapKeys(变换map的键),filterValues(过滤map的值),mapValues(变换对应的map值)
val numbers = mapOf(0 to "ZERO", 1 to "ONE", 2 to "TWO")//获取map里小于2的keyprintln(numbers.filterKeys { it < 2 })//{0=ZERO, 1=ONE}////把key变换为valueprintln(numbers.mapKeys { it.value })//{ZERO=ZERO, ONE=ONE, TWO=TWO}//过滤map的valueprintln(numbers.filterValues { it.startsWith("O") })//{1=ONE}//转换map的value为小写println(numbers.mapValues { it.value.toLowerCase() })//{0=zero, 1=one, 2=two}
- all,any,count,find:对集合的判断应用
检查集合中所有的元素是否都符合某个条件(又或是是否存在符合的元素)。它们是通过all和any函数表达。count为检查有多少个元素满足判断式,find函数返回第一个符合条件的元素。
val people = listOf(Person("小明", 22),Person("小红", 24),Person("小花", 29),Person("小美", 30),Person("小帅", 19))//年龄是否满足23val isAge23 = { person: Person -> person.age < 23 }//检查集合看是否所有元素满足(all)println(people.all(isAge23))//false//检查集合中是否至少存在一个匹配的元素(any)println(people.any(isAge23))//true//检查有多少个元素满足判断式(count)println(people.count(isAge23))//true//找到第一个满足判断式的元素(find)//如有多个匹配返回其中第一个元素,没有返回null。同义函数firstOrNull。println(people.find(isAge23))//Person(name=小明, age=22)//找到倒数第一个满足判断式的元素(find)//如有多个匹配返回其中第一个元素,没有返回null。同义函数lastOrNull。println(people.findLast(isAge23))//Person(name=小帅, age=19)
-
值得一提的是!all(不是所有)加上某个条件,应该用any取反
val list = listOf(1, 2, 3)////此种方式不推荐println(!list.all { it < 2 })//false//推荐此种方式定义(lambda参数中条件取反)println(list.any { it >= 2 })//true
注意:all 、any 对立条件一般采用对 lambda 表达式取反来达到取反的目的
-
再就是count和.size。count方法只是跟踪匹配元素的数量,不关心元素本身,所以更高效。.size需要配合filter过滤从而中间会创建新集合用来存储。
//.size的方式(具体使用情况看实际,只关心数量不推荐此方式)println(people.filter (isAge23).size)
groupBy:把列表转换成分组的map
如果需要把不同的元素划分到不同的分组,使用groupaBy事半功倍。
val people = listOf(Person("小明", 22),Person("小红", 22),Person("小花", 29),Person("小美", 30),Person("小帅", 29))//使用group按年龄分组,返回结果是mapprintln(people.groupBy{it.age})//输出结果: {22=[Person(name=小明, age=22), Person(name=小红, age=22)], 29=[Person(name=小花, age=29), Person(name=小帅, age=29)], 30=[Person(name=小美, age=30)]}
每一个分组都存储在一个列表中,这里结果类型实质是Map<Int,List<Person>>
,mapKeys
或 mapValues
函数也能作用于它。
再来看个例子,按首字母区分
val list = listOf("a", "ab", "abc", "b")//值得注意的是,这里的first不是String的成员,而是一个扩展(可成员引用)println(list.groupBy(String::first))//{a=[a, ab, abc], b=[b]}
flatMapflatten:处理嵌套集合元素
相信如果了解Rxjava的话,那么一定会对flatMap不会陌生。flatMap会根据作为实参给定的函数对集合中的每个元素做变换后,然后把多个列表合并成一个新的列表。
val list2 = listOf("ab", "cd", "ef")////flatMap变换成新集合println(list2.flatMap { it.toList() })[a, b, c, d, e, f]
再举个例子,有一堆藏书,每本书可能有一个作者或者多个作者。
//title表示书名,authors表示作者的集合class Book(val title: String, val authors: List<String>)val books = listOf(Book("三国演义", listOf("罗贯中")),Book("水浒传", listOf("施耐庵","罗贯中")),Book("红楼梦", listOf("曹雪芹","程伟元","高鹗")))//flatMap变换合并,toSet移除重复元素println(books.flatMap { it.authors }.toSet())
这里需要注意的是,如果集合元素不需要变换,而只是想合并成一个新的集合的时候,不必使用
flatMap
,应该使用flatten
函数。
惰性集合操作:序列
如果对java8的lambda熟悉,一定会知道stream流的存在。上面大部分的lambda函数会及早的创建中间集合(每一步中间结果都被存储在一个临时列表)。因此如果数据过多的话链式调用就会特别低效。而序列恰好能避免创建这些临时中间对象,从而解决这一问题。
val people = listOf(Person("小明", 24),Person("小红", 22),Person("小花", 28),Person("小美", 30),Person("小帅", 29))var list2 = people.asSequence() //把初始集合转成序列.sortedBy(Person::age) //排序.map { it.name } //序列支持和集合一样的api.filter { it.startsWith("小") } //过滤.toList() //把结果序列转换未序列表println(list2) //[小红, 小明, 小花, 小帅, 小美]
惰性集合操作入口就是Sequence接口,这个接口就是表示可以逐个列举的元素序列。Sequence只提供一个方法,iterator(用来从序列里获取值)。
Sequence接口强大在于操作实现的形式。序列中元素求值是惰性的。值得注意的是asSequence()是扩展函数。
执行序列操作:中间和末端操作
序列操作分为两类:中间的和末端。一次中间操作返回值是另一个序列(知道如何变换原始序列中的元素)。而一次末端操作返回的是一个结果,这个结果可能是集合,元素,数字或者其它从初始集合的变换序列中获取的任意对象。
people.asSequence() .map (Person::name) //中间操作.filter { it.startsWith("j") } //中间操作.toList() //末端操作
中间操作始终都是惰性的。
//不会输出任何内容,lambdaa变换被延期,只有获取结果时才会被调用(末端操作)listOf(1,2,3,4).asSequence().map { print("map($it) ");it*it }.filter { print("filter($it)");it%2==0 }
只要函数的的返回值不是
Sequence
整个函数就会被执行
加上末端操作
listOf(1,2,3,4).asSequence().map { print("map($it) ");it*it }.filter { print("filter($it)");it%2==0 }.toList() // 末端操作触发执行所有延期计算//map(1) filter(1)map(2) filter(4)map(3) filter(9)map(4) filter(16)
着重注意的是计算顺序,序列的操作是按顺序应用在每一个元素上:处理第一个元素后,再完成第二个元素,以此类推。
这也意味着有部分元素根本不会发生任何变换,举个map和find的例子。先把一个数字映射成它的平方,然后找到第一个比3大的条目。
println(listOf(1, 2, 3, 4).asSequence().map {println("执行了 $it" )it * it}.find { it > 3 })
执行结果
执行了 1执行了 24
可见满足条件后中间的函数就不再执行了,大大的提高了执行效率
如果同样的操作被应用在集合上,那么map结果会先被求出来,然后会把中间集合中满足的判断式的元素找出来。而对于序列来说,惰性方法意味着可以跳过处理部分元素。这也是及早求值(用集合)和惰性求值(用序列)的区别。
集合上执行操作的顺序是会影响性能的。再举个例子,用不同的操作顺序找出上述数据源person集合中长度小于某个限制的人名。
先map再filter
val people = listOf(Person("小明", 24),Person("小红", 22),Person("小花", 28),Person("小美", 30),Person("小帅", 29))//先map再filterprintln(people.asSequence().map {print("map 执行了 $it ,")it.name}.filter {println("filter 执行了 $it ,")it=="小花"}.toList())
输出结果
map 执行了 Person(name=小明, age=24) ,filter 执行了 小明 ,
map 执行了 Person(name=小红, age=22) ,filter 执行了 小红 ,
map 执行了 Person(name=小花, age=28) ,filter 执行了 小花 ,
map 执行了 Person(name=小美, age=30) ,filter 执行了 小美 ,
map 执行了 Person(name=小帅, age=29) ,filter 执行了 小帅 ,
[小花]
先filter再map
val people = listOf(Person("小明", 24),Person("小红", 22),Person("小花", 28),Person("小美", 30),Person("小帅", 29))
//先filter再map
println(people.asSequence().filter {println("filter 执行了 $it ,")it.name == "小花"}.map {print("map 执行了 $it ,")it.name}.toList())
结果当然是一样的,不同的是如果map在前,那么是每个元素都进行变换后在去过滤,而filter在前,则是先过滤在变换(被过滤掉的不会进行变换)。在实际中根据需求来执行,一定要时刻的记着自己的代码在干什么。
kotlin的序列实际上就是java8里stream(流的概念)的翻版,遗憾的是序列尚且未实现流的重要特性:并行执行流操作。
创建序列
- asSequence()是用在集合上创建序列,而创建序列还有另一个函数generateSequence。给定此函数创建的序列,这个函数会计算出下一个元素。
整数举例
//100以内所有自然数和val naturalNumbers = generateSequence(0) { it+1 }val naturalNumbersTo100 = naturalNumbers.takeWhile { it<=100 }//sum触发延时操作计算结果println(naturalNumbersTo100.sum()) //5050
字符串举例
val naturalNumbers = generateSequence("a") { "$it ${it.length}" }val naturalNumbersTo100 = naturalNumbers.takeWhile {println(it)it.length <= 20}println(" ")println(naturalNumbersTo100.toList())
- 另一个常用的场景是父序列,如果元素的父元素和它的类型相同,我们可能会对它所有祖先组成的序列的特质感兴趣。举个查询文件是否在隐藏目录中的例子。
列举所有父文件
fun File.listParentFile() = generateSequence(this) { it.parentFile }val file = File("C:\\Users\\m1362\\Documents\\Tencent Files\\1107472286\\FileRecv")println(file.listParentFile().toList().forEach {println(it)})//运行结果 : C:\Users\m1362\Documents\Tencent Files\1107472286\FileRecvC:\Users\m1362\Documents\Tencent Files\1107472286C:\Users\m1362\Documents\Tencent FilesC:\Users\m1362\DocumentsC:\Users\m1362C:\UsersC:\
查找隐藏文件
//File的扩展函数,generateSequence函数创建了文件的父序列fun File.isInsideHiddenDirectory()=generateSequence(this){it.parentFile}.any{it.isHidden}val file = File("/Users/svtk/.HiddenDir/a.txt")println(file.isInsideHiddenDirectory())//true
一样是通过提供第一个元素和获取每个后续的元素来提供实现。
使用Java函数式接口
kotlin的lambda能无缝和JavaAPI互操作。
举个例子,常见的在android中点击事件的监听(此处省略常规写法和java8的lambda写法,只举kotlin和javaAPI互操作的写法)。
btn.setOnClickListener { view ->Log.e("cb","点击了") }
这种接口被称为函数式接口,又被称SAM接口,SAM代表单抽象方法。kotlin允许调用接收函数式接口作为参数方法使用lambda,来保证代码的整洁和简洁。
lambda当作参数传递Java
-
lambda可以传给任何期望函数式接口的方法。
//Javavoid postponeComputation(int dealay,Runnable computation);
-
在kotlin中,可以调用它并把一个lambda作为实参传给它。编译器会自动把它转换成一个Runnable实例。
//底层优化,没有外部变量的引用复用 runnable对象 ,有引用创建新的对象postponeComputation(1000){println(42)}
注意的是,“一个Runnable的实例"时,是指一个"一个实现了Runnable接口的匿名类的实例”。编译器会帮忙创建它。
也可以通过显示的创建实现Runnable的匿名对象达到同样效果。
//把对象表达式作为函数式接口实现传递,强制每次调用创建新的对象Demo().postponeComputation(1000,object :Runnable{override fun run() {TODO("not implemented") //To change body of created functions use File | Settings | File Templates.println(42)}})
不同的是,当显示声明对象时,每次调用都会创建一个新的实例。而使用lambda如果没有访问任何来自定义函数的变量,相应的匿名类实例可以在多次调用后重用(即只会创建一个Runnable的实例)。
-
因此完全等价的实现应该显示object声明,在Runnable实例里存储变量,每次调用使用此变量。
public class Demo {//Javapublic void postponeComputation(int dealay, Runnable computation) {computation.run();}}//全局变量,程序中只有一个实例val runnable = Runnable { println(42) }fun handleComputation(){//函数调用时候会作用同一个对象,强制使用同一个对象Demo().postponeComputation(1000, runnable)}
如果lambad从包围它的作用域捕捉到变量,那么就不可能每次都用通一个对象了,而是会使用新的实例,每次调用都会生成新的实例(否则就成了单例)。本质上这样的行为底层不是一个lambda,而是一个特殊类实例,类名是函数名称加上后缀衍生($调用次数)。
为lambda创建一个匿类及该类实例方式只对期望函数式的java方法有效。
SAM构造方法
SAM构造方法是编译器生成的函数,可以让执行从lambda到函数式接口实例的显式转换。可以在编译器不会自动应用转换的上下文中使用它。比如我们有一个方法返回的是一个函数式接口的实例,不能直接返回一个lambda,要用SAM构造方法把它包装起来。
//使用SAM构造方法来返回值fun createAllDoneRunnable(): Runnable {return Runnable { println("all down") }}createAllDoneRunnable().run() //all down
SAM构造方法的名称和底层函数式接口的名称一样。SAM构造方法只接收一个参数——一个被用作函数式接口单抽象方法体的lambda——并返回实现了这个接口类的一个实例。
除返回值以外,SAM构造方法还可以用在需要把lambda生成的函数式接口实例存储在一个变量中的情况。如在多个view上重用同一个监听器,这里仍以android点击监听做例。
val listener = View.OnClickListener { view ->val text = when (view.id){R.id.btn -> "first button"R.id.btn1 ->"Second button"else ->"UnClick"}println(text)}btn.setOnClickListener(listener)btn1.setOnClickListener(listener)
需要注意的是,lambda
内部没有匿名对象那样的 this
,所以没办法引用到lambda转换成的匿名类实例。从编译器角度看,lambda
是一个代码块,不是一个对象,也不能当作一个对象的引用。lambda
中的 this
引用的是指向包围它的类。所以如果事件监听器在处理事件时还需要取消它自己,不能使用 lambda
这样做。这种情况使用实现了接口的匿名对象。在匿名对象内,this
关键字指向该对象实例,可以把它传给移除监听器的 API
。
带接收者的lambda:with与apply
kotlin
中的 lambda
独特功能在于:在 lambda
函数体内可以调用一个不同对象的方法,而且无需借助任何额外限定符。这样的 lambda
同时也被成为带接收者的 lambda
。kotlin
标准库中的 with
函数和 apply
函数就是带接收者的 lambda
,它们用途广泛而又方便。
with
- with函数用来对同一个对象执行多次操作,而不需要反复把对象名称写出来。举个例子,先不用with去构建字母表。
传统代码
//不使用with函数来构建字母表fun alphabet(): String {val result = StringBuilder()for (latter in 'A'..'Z') {result.append(latter)}result.append("Ok!")return result.toString()}println(alphabet()) //ABCDEFGHIJKLMNOPQRSTUVWXYZOk!
-
使用with重构。
//使用with函数来构建字母表fun alphabet(): String {val result = StringBuilder()return with(result) {//指定接收者的值,会调用它的方法for (latter in 'A'..'Z') {this.append(latter) //通过显示的"this"来调用接收者方法}append("Ok!") //也可以省略thistoString() //从lambda返回值}}println(alphabet()) //ABCDEFGHIJKLMNOPQRSTUVWXYZOk!
with函数把它 的第一个参数转换成作为第二个参数传给它的lambda的接收者,既可以显示通过 this
引用访问也可以省略(不使用任何限定符)。
值得一提的是,lambda
是一种类似普通函数的定义方式,而带接收者的 lambda
是类似扩展函数定义的方式。
-
进一步重构成终极版,使用with和一个表达式函数体。
fun alphabet() =with(StringBuilder()) {for (latter in 'A'..'Z') {append(latter)}append("Ok!")toString()}println(alphabet()) //ABCDEFGHIJKLMNOPQRSTUVWXYZOk!
apply
with
返回值是执行 lambda
的代码结果,该结果是 lambda
最后一个表达式。但是如果想返回的是接收者对象而不是执行 lambda
的结果,就该使用 apply
了。
apply
函数几乎和 with
函数一样,唯一区别就是 apply
会始终返回作为实参传递给它的对象(接收者对象),依旧拿上面的 with
函数例子举例 apply
的用法。
fun alphabet() =StringBuilder().apply() {for (latter in 'A'..'Z') {append(latter)}append("Ok!")}.toString()println(alphabet()) //ABCDEFGHIJKLMNOPQRSTUVWXYZOk!
apply
被声明成扩展函数。它的接收者变成了作为实参的 lambda
的接收者。执行 apply
的结果就是 StringBuilder
。
许多情况下 apply
都极其有效,其中一种就是在创建一个对象实例并需要用正确的方式初始化它的一些属性的时候。在 kotlin
中,可以在任意对象上使用 apply
。
//使用apply创建Textviewfun createrTextView(contetx:Context){TextView(contetx).apply { text="this is apply create"textSize= 20.0FsetPadding(10,0,0,0) }}
函数的返回值是 TextView
with
函数和 apply
函数是最基本最通用的使用带接收者的 lambda
例子。更多具体例的函数也可以使用这种模式。比如标准库函数 buildString
。
//使用buildString创建字母表fun alphabet() = buildString { for (letter in 'A'..'Z'){append(letter)}append("Ok!")}
buildString
函数优雅的完成了借助 StringBuilder
创建 String
的任务。我们可以使用带接收者的 lambda
构建 DSL
,以及如何定义自己的函数来调用接收者 lambda
。后续见分晓。
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
相关文章
- python程序发布工具没开发完共享一下先
链接:https://pan.baidu.com/s/118qEn02PIkEVy878LSjkng 提取码:nm181、各种支持库需要自己安装。例如安装pandas:命令行,进入dlls文件夹,运行:python -m pip install pandas2、发布python程序:新建一个python项目,然后列表选中它,单击右键,发布。这个操作会修改set…...
2024/4/24 12:08:57 - 【MySQL】innodb_undo_tablespaces
MySQL5.7设置innodb_undo_tablespaces = 2 报错如下: 2020-06-09T04:40:07.800321-05:00 0 [ERROR] InnoDB: Expected to open 2 undo tablespaces but was able to find only 0 undo tablespaces. Set the innodb_undo_tablespaces parameter to the correct value and retry…...
2024/5/8 19:19:24 - Java图片分割与合并
Java图片分割与合并Java在这里插入代码片图片分割与合并(自己用到了记录一下) Java在这里插入代码片图片分割与合并(自己用到了记录一下)/*** @param fileName 图片名称* @param newFileName 分割后的图片名称* @param rows 高* @param cols 宽* @descrip…...
2024/4/24 12:08:56 - IP-Guard安装客户端后,但是在控制台却看不到安装了客户端的电脑(或显示为灰色)?
安装客户端后,但是在控制台却看不到安装了客户端的电脑(或显示为灰色)?在安装客户端后,如果在控制台看不到安装了客户端的电脑;1.首先请确认您 安装客户端数量是否已超过了购买的数量,可能通过控制台的“工具”菜单->“计算机”打开计算机管理,查看其授权信息。2.请确…...
2024/5/8 22:51:41 - jsonPath语法规则,小白秒懂必备
jsonPath语法规则 【有问题或错误,请私信我将及时改正;借鉴文章标明出处,谢谢】 参考文献: JSONPATH官网在《数据预处理》的案例JSON文件的导入与导出中,path的填写需要遵照json规则,故写这篇博客。jsonpath只需要大致懂一些符号和使用,如果需要深入了解可进入官网详解。…...
2024/4/24 12:08:53 - P4933 大师(DP)
P4933 大师(DP) 题意:给定序列求所有子序列中为等差数列的个数。(序列长度为1,2被认为是等差序列,空序列不是等差序列)。 思路:很巧妙的dpdpdp,因为a[i]≤2e4a[i]\leq 2e4a[i]≤2e4,数字较小。所以考虑设dp[i][j]dp[i][j]dp[i][j]表示以下标iii的数字结尾公差为jjj的等差序…...
2024/5/8 13:18:18 - ubuntu下创建新用户并添加到sudoers文件中
1. 切换为root用户,获取到添加新用户的权限su root2. 执行下列命令添加一个新用户,设要添加的用户名为:honyadduser hony然后根据系统提示进行密码和注释性描述的配置,全程不用自己输入其他命令即可配置成功,用户主目录和命令解析程序都是系统自动指定。3. 查看用户属性ca…...
2024/5/8 21:18:47 - 问题:CentOS7 nginx Failed to read PID from file /run/nginx.pid: Invalid argument?
1.在centos7上,配置nginx代理服务后, systemctl status nginx.service提示错误 :Failed to read PID from file /run/nginx.pid: Invalid argument2.看到好多说删掉改nginx.pid 文件的,试了后,无效。3.后来找到了一个方法: mkdir -p /etc/systemd/system/nginx.service.d…...
2024/4/24 12:08:50 - Python字符串最全使用方法!!
Python字符串最全使用方法!! 在编写程序中的所有字符串都是str类下的对象,可以通过 help(str) 查询方法清单。 对于字符串的基本操作: 所有标准序列操作(索引,切片,乘法,资格检查,长度,最大小值)都适用于字符串。 s = abcdefgABC123 #索引 通过下标找到指定元素 pri…...
2024/4/24 12:08:49 - Linux 查看进程运行的完整路径方法
本文转自 西门大官人-Linux 查看进程运行的完整路径方法通过ps及top命令查看进程信息时,只能查到相对路径,查不到的进程的详细信息,如绝对路径等。这时,我们需要通过以下的方法来查看进程的详细信息:Linux在启动一个进程时,系统会在/proc下创建一个以PID命名的文件夹,在…...
2024/4/15 4:44:09 - Rinex biography and Introduction for version 2
只放英文,原汁原味!RINEX: The Receiver Independent Exchange Format Version 2一、 THE PHILOSOPHY OF RINEXRinex格式起源于一个项目,需要将不同接收机的数据汇总归类,以方便处理:一般Rinex格式数据包括:载波相位、伪距、信号观测时间。The first proposal for the &q…...
2024/4/17 0:24:49 - 合并pandas的两列
tongji2[‘enodebid-ci’]=tongji2.apply(lambda x:x[‘enodebid’]+’-’+x[‘cellLocalId’],axis=1)...
2024/4/15 4:44:07 - mybatis 报错信息
Result Maps collection already contains value :1.resultType和resultMap搞混了2.xml代码有问题Mapped Statements collection already contains value:1.mapper中存在id重复的值2.mapper中的parameterType或resultType为空。No bean named customerOrderService is define…...
2024/4/17 11:18:31 - 操作系统---(34)请求分页存储管理之基本原理
1. 基本原理要点内存分大小想的帧(Frame); 进程按照帧的大小被分为若干页(Page); 进程仅装入部分页面,即开始执行 在执行过程中访问的页若已在内存,进行动态的地址重定位,执行指令(同基本分页存储管理)2. 请求分页式存储管理在执行过程中访问的页未装入内存时,产生…...
2024/5/8 20:07:33 - Shadow解决Activity等组件生命周期的方法解析
明确问题每个Android插件框架要解决的首要问题都是Activity的生命周期问题。Activity代表了Service等其他需要注册的组件。不同是插件框架解决这个问题的前提也不完全一样。而我们的业务要求比较苛刻,再加上Android 9.0的非公开API限制,所以由这些前提要求:插件代码也要能正…...
2024/4/24 12:08:50 - 英语单元课后习题+翻译详解
c 低估 d 指示,命令A 缓慢费力的走,拖延 D出现出版结果是选Bdesperate (因绝望而)不惜冒险的,不顾一切的,拼命的;绝望的;孤注一掷的;铤而走险的;非常需要;极想;渴望选crucial至关重要的;关键性的选Alaunched开始从事,发起,发动(尤指有组织的活动);(首次)上市,发行;使(船…...
2024/4/24 12:08:47 - 序列化:简单通用的数据交换格式JSON、MessagePack 和 ProtoBuffer
序列化,就是把内存里“活的对象”转换成静止的字节序列,便于存储和网络传输;而反序列化则是反向操作,从静止的字节序列重新构建出内存里可用的对象。 数据交换格式:JSON、MessagePack 和 ProtoBuffer。 对数据做序列化和反序列化: JSON: JSON 是一种轻量级的数据交换格式…...
2024/4/28 9:51:55 - 执行原理
2.4 执行原理 每个Spring Boot项目都有一个主程序启动类,在主程序启动类中有一个启动项目的main()方法,在该方法中通过执行SpringApplication.run()即可启动整个Spring Boot程序。 问题:那么SpringApplication.run()方法到底是如何做到启动Spring Boot项目的呢? 下面我们…...
2024/4/24 12:08:44 - 《神经质人格》摘录(导言,第一章)
文章目录导言第一章、神经质人格:人类心灵痛苦的密码1、对危险、损失和错误具有优先反应的人2、心理健康者和神经质人格者的表现3、什么是人格4、什么是神经质人格5、如何理解神经质人格6、人格可以决定痛苦,也可以决定幸福7、神经质人格的小例子8、神经质人格应该怎么做 导言…...
2024/4/24 12:08:45 - Android ViewDragHelper类
ViewDragHelper是个工具类,用来辅助ViewGroup内控件的操作和拖拽。 1. ViewDragHelper创建 ViewDragHelper通过静态方法create()创建, public static ViewDragHelper create(ViewGroup forParent, Callback cb) public static ViewDragHelper create(ViewGroup forParent, fl…...
2024/5/2 6:06:09
最新文章
- 【Linux】项目自动化构建工具make/makefile的简单使用
使用步骤 1) 编写 创建 makefile 文件 vim makefile用 vim 打开名为 makefile 的文件,存在该文件则打开编辑,不存在则创建并打开.在 makefile 文件中编写需要编译的文件 test:test.cppg -o test test.cpp第一行: 冒号左侧为编译后的可执行文件名,可以随便取. 冒号右侧为依赖…...
2024/5/9 4:00:12 - 梯度消失和梯度爆炸的一些处理方法
在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言,在此感激不尽。 权重和梯度的更新公式如下: w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...
2024/5/7 10:36:02 - dp小兰走迷宫
昨天学习了bfs的基本概念,今天来做一道经典习题练练手吧! bfs常用的两类题型 1.从A出发是否存在到达B的路径(dfs也可) 2.从A出发到B的最短路径(数小:<20才能用dfs) 遗留的那个问题的答案- 题目:走迷宫 #incl…...
2024/5/8 15:11:55 - Oracle 正则表达式
一、Oracle 正则表达式相关函数 (1) regexp_like :同 like 功能相似(模糊 匹配) (2) regexp_instr :同 instr 功能相似(返回字符所在 下标) (3) regexp_substr : 同 substr 功能相似&…...
2024/5/8 20:00:12 - 【外汇早评】美通胀数据走低,美元调整
原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...
2024/5/8 6:01:22 - 【原油贵金属周评】原油多头拥挤,价格调整
原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...
2024/5/7 9:45:25 - 【外汇周评】靓丽非农不及疲软通胀影响
原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...
2024/5/4 23:54:56 - 【原油贵金属早评】库存继续增加,油价收跌
原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...
2024/5/7 14:25:14 - 【外汇早评】日本央行会议纪要不改日元强势
原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...
2024/5/4 23:54:56 - 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响
原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...
2024/5/4 23:55:05 - 【外汇早评】美欲与伊朗重谈协议
原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...
2024/5/4 23:54:56 - 【原油贵金属早评】波动率飙升,市场情绪动荡
原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...
2024/5/7 11:36:39 - 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试
原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...
2024/5/4 23:54:56 - 【原油贵金属早评】市场情绪继续恶化,黄金上破
原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...
2024/5/6 1:40:42 - 【外汇早评】美伊僵持,风险情绪继续升温
原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...
2024/5/4 23:54:56 - 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势
原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...
2024/5/8 20:48:49 - 氧生福地 玩美北湖(上)——为时光守候两千年
原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...
2024/5/7 9:26:26 - 氧生福地 玩美北湖(中)——永春梯田里的美与鲜
原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...
2024/5/4 23:54:56 - 氧生福地 玩美北湖(下)——奔跑吧骚年!
原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...
2024/5/8 19:33:07 - 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!
原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...
2024/5/5 8:13:33 - 「发现」铁皮石斛仙草之神奇功效用于医用面膜
原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...
2024/5/8 20:38:49 - 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者
原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...
2024/5/4 23:54:58 - 广州械字号面膜生产厂家OEM/ODM4项须知!
原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...
2024/5/6 21:42:42 - 械字号医用眼膜缓解用眼过度到底有无作用?
原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...
2024/5/4 23:54:56 - 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...
解析如下:1、长按电脑电源键直至关机,然后再按一次电源健重启电脑,按F8健进入安全模式2、安全模式下进入Windows系统桌面后,按住“winR”打开运行窗口,输入“services.msc”打开服务设置3、在服务界面,选中…...
2022/11/19 21:17:18 - 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。
%读入6幅图像(每一幅图像的大小是564*564) f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...
2022/11/19 21:17:16 - 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...
win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面,在等待界面中我们需要等待操作结束才能关机,虽然这比较麻烦,但是对系统进行配置和升级…...
2022/11/19 21:17:15 - 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...
有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows,请勿关闭计算机”的提示,要过很久才能进入系统,有的用户甚至几个小时也无法进入,下面就教大家这个问题的解决方法。第一种方法:我们首先在左下角的“开始…...
2022/11/19 21:17:14 - win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...
置信有很多用户都跟小编一样遇到过这样的问题,电脑时发现开机屏幕显现“正在配置Windows Update,请勿关机”(如下图所示),而且还需求等大约5分钟才干进入系统。这是怎样回事呢?一切都是正常操作的,为什么开时机呈现“正…...
2022/11/19 21:17:13 - 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...
Win7系统开机启动时总是出现“配置Windows请勿关机”的提示,没过几秒后电脑自动重启,每次开机都这样无法进入系统,此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一:开机按下F8,在出现的Windows高级启动选…...
2022/11/19 21:17:12 - 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...
有不少windows10系统用户反映说碰到这样一个情况,就是电脑提示正在准备windows请勿关闭计算机,碰到这样的问题该怎么解决呢,现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法:1、2、依次…...
2022/11/19 21:17:11 - 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...
今天和大家分享一下win7系统重装了Win7旗舰版系统后,每次关机的时候桌面上都会显示一个“配置Windows Update的界面,提示请勿关闭计算机”,每次停留好几分钟才能正常关机,导致什么情况引起的呢?出现配置Windows Update…...
2022/11/19 21:17:10 - 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...
只能是等着,别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚,只能是考虑备份数据后重装系统了。解决来方案一:管理员运行cmd:net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...
2022/11/19 21:17:09 - 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?
原标题:电脑提示“配置Windows Update请勿关闭计算机”怎么办?win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢?一般的方…...
2022/11/19 21:17:08 - 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...
关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!关机提示 windows7 正在配…...
2022/11/19 21:17:05 - 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...
钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...
2022/11/19 21:17:05 - 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...
前几天班里有位学生电脑(windows 7系统)出问题了,具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面,长时间没反应,无法进入系统。这个问题原来帮其他同学也解决过,网上搜了不少资料&#x…...
2022/11/19 21:17:04 - 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...
本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法,并在最后教给你1种保护系统安全的好方法,一起来看看!电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中,添加了1个新功能在“磁…...
2022/11/19 21:17:03 - 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...
许多用户在长期不使用电脑的时候,开启电脑发现电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机。。.这要怎么办呢?下面小编就带着大家一起看看吧!如果能够正常进入系统,建议您暂时移…...
2022/11/19 21:17:02 - 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...
配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!配置windows update失败 还原更改 请勿关闭计算机&#x…...
2022/11/19 21:17:01 - 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...
不知道大家有没有遇到过这样的一个问题,就是我们的win7系统在关机的时候,总是喜欢显示“准备配置windows,请勿关机”这样的一个页面,没有什么大碍,但是如果一直等着的话就要两个小时甚至更久都关不了机,非常…...
2022/11/19 21:17:00 - 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...
当电脑出现正在准备配置windows请勿关闭计算机时,一般是您正对windows进行升级,但是这个要是长时间没有反应,我们不能再傻等下去了。可能是电脑出了别的问题了,来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...
2022/11/19 21:16:59 - 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...
我们使用电脑的过程中有时会遇到这种情况,当我们打开电脑之后,发现一直停留在一个界面:“配置Windows Update失败,还原更改请勿关闭计算机”,等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢࿰…...
2022/11/19 21:16:58 - 如何在iPhone上关闭“请勿打扰”
Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...
2022/11/19 21:16:57