Kotlin标准函数与静态方法基础知识详解

目录
  • 标准函数with与run和apply with函数
    • with函数
    • run函数
    • apply函数
  • 定义静态方法

标准函数with与run和apply with函数

with函数

接收两个参数:第一个参数可以是任意类型的对象,第二个参数是一个Lambda表达式。with函数会在Lambda表达式中提供第一个参数对象的上下文,并使用Lambda表达式中的最后一行代码作为返回值返回。

示例代码如下:

val result=with(obj){
//这里是obj的上下文
"value"//with函数的返回值
}

那么这个函数有什么作用呢?它可以在连续调用同一个对象的多个方法时让代码变得更加精简,下面我们来看一个具体的例子。

比如有一个水果列表,现在我们想吃完所有的水果,并将结果打印出来,可以这样写

    val list= listOf("Apple","Banana","Orange","Pear","Grape")
        val builder=StringBuilder()
        builder.append("Start eating fruits.\n")
        for(fruit in list){
            builder.append(fruit).append("\n")
        }
        builder.append("Ate all fruits.")
        val result=builder.toString()
        println(result)

这段代码的逻辑很简单,就是使用StringBuilder来构建吃水果的字符串,最后将结果打印出来

打印结果为:

观察上述代码,你会发现我们连续调用了很多次builder对象的方法。其实这个时候就可以考虑使用with函数来让代码变得更加精简,如下所示:

        val list= listOf("Apple","Banana","Orange","Pear","Grape")
        val result= with(StringBuilder()){
            append("Start eating fruits.\n")
            for(fruit in list){
                append(fruit).append("\n")
            }
            append("Ate all fruits.")
            toString()
        }
        println(result)

首先我们给with函数的第一个参数传入一个StringBuilder对象,那么接下来整个Lambda表达式的上下文就会是这个StringBuilder对象。于是我们在Lambda表达式中就不用再像刚才那样调用builder.append()和builder.toString()方法了,而是可以直接调用append()和toString()方法。Lambda表达式最后一行代码会作为with函数的返回值返回,最终我们将结果打印出来。

虽然两段代码结果一样,但是明显第二段代码的写法更加简洁一些,这就是with函数的作用。

run函数

run函数的用法和使用场景其实和with函数时非常类似的,只是稍微做了一些语法改动而已。首先run函数是不能直接调用的,而是一定要调用某个对象的run函数才行;其次run函数只接收一个Lambda参数,并且会在Lambda表达式中提供调用对象的上下文。其他方面和with函数一样,包括也会使用Lambda表达式最后一行作为返回值返回。示例代码如下:

val result=obj.run{
//这里是obj的上下文
"value"//run函数的返回值
}

那么现在我们就可以使用run函数来修改一下吃水果的这段代码,如下所示

     val list= listOf("Apple","Banana","Orange","Pear","Grape")
        val result=StringBuilder().run{
            append("Start eating fruits.\n")
            for(fruit in list){
                append(fruit).append("\n")
            }
            append("Ate all fruits.")
            toString()
        }
        println(result)

总体来说变化很小,只是将调用with函数并传入StringBuilder对象改成了调用StringBuilder对象的run方法,其他没什么区别,这两段代码最终的执行结果是完全相同的。

apply函数

apply函数和run函数也是极其相似的,都是在某个对象上调用,并且接收一个Lambda参数,也会在Lambda表达式中提供调用对象的上下文,但是apply函数无法指定返回值,而是会自动返回调用对象本身。示例如下:

val result=obj.apply{
//这里是obj的上下文
}
//result==obj

那么现在我们再使用apply函数来修改一下吃水果的这段代码

     val list= listOf("Apple","Banana","Orange","Pear","Grape")
        val result=StringBuilder().apply{
            append("Start eating fruits.\n")
            for(fruit in list){
                append(fruit).append("\n")
            }
            append("Ate all fruits.")
        }
        println(result.toString())

由于apply函数无法指定返回值,只能返回调用对象本身,因此result实际上是一个StringBuilder对象,所以我们在最后打印的时候还要调用它的toString()方法才行。这段代码的执行结果和前面两段仍然完全相同的。

其实with、run和apply这几个函数的用法和使用场景是非常类似的。在大多数情况下,可以相互转换。

例如我们启动Activity的时候

val intent=Intent(this,SecondActivity::class.java)
intent.putExtra("param1","data1")
intent.putExtra("param2","data2")
startActivity(intent)

这里每传递一个参数就会调用一次intent.putExtra()方法,如果要传递10个参数,那就得调用10次。对于这种情况,我们就可以使用标准函数来对代码进行精简,如下所示

val intent=Intent(this,SecondActivity::class.java).apply{
intent.putExtra("param1","data1")
intent.putExtra("param2","data2")
}
startActivity(intent)

可以看到,由于Lambda表达式中的上下文就是Intent对象,所以我们不需要调用intent.putExtra()方法,而是直接调用putExtra()方法就可以了。传递的参数越多,这种写法优势也就越明显。

定义静态方法

静态方法再某些编程语言里面又叫做类方法,指的就是那种不需要创建实例就能调用的方法,所有主流的编程语言都会支持静态方法这个特性。

在java中定义一个静态方法,只需要在方法上声明一个static关键字就可以了,如下所示:

public class Util{
public static void doAction(){
System.out.printlin("do action")
}
}

上述代码中的doAction()方法就是一个静态方法。调用静态方法并不需要创建类的实例,而是可以直接以Util.doAction()这种写法调用。因而静态方法非常适合用于编写一些工具类的功能,因为工具类通常没有创建实例的必要,基本上是全局通用的。

和大多数主流编程语言不同的是,kotlin却极度弱化了静态方法这个概念,想要在Kotlin中定义一个静态方法反倒不是那么容易。

因为Kotlin提供了比静态方法更好用的语法特性,那就是单例类

object Util{
fun doAction(){
println("do action")
}
}

虽然这里的doAction不是静态方法,但是我们仍然可以使用Util.doAction()方式来调用,这就是单例类所带来的便利。

不过,使用单例类的写法会将整个类中所有方法全部变成类似于静态方法的调用方式,而如果我们只是希望让类中的某一个方法变成静态方法的调用方式该怎么办呢?这个时候就可以使用companion object了,示例如下:

class Util{
fun doAction1(){
println("do action1")
}
companion object{
fun doAction2(){
println("do action2")
}
}
}

首先我们将Util从单例类改成一个普通类,然后在类中直接定义一个doAction1()方法,又在companion object中定义了一个doAction2()方法。现在两个方法就有了本质的区别,因为doAction1()方法是一定要先创建Util类的实例才能调用的,而doAction2()方法可以直接使用Util.doAction2()的方式调用。

不过,doAction2()方法其实也不是静态方法,companion object这个关键字实际上会在Util类的内部创建一个伴生类,而doAction2()方法就是定义在这个伴生类里面的实例方法。只是Kotlin会保证Util类始终只会存在一个伴生类对象,因此调用Util.doAction2()方法实际上就是调用了Util类中伴生对象的doAction2()方法。

编译成字节码结果如下:

public final class Util {
   public static final Util.Companion Companion = new Util.Companion((DefaultConstructorMarker)null);
   public final void doAction1() {
      String var1 = "do action1";
      boolean var2 = false;
      System.out.println(var1);
   }
   @Metadata(
      mv = {1, 1, 16},
      bv = {1, 0, 3},
      k = 1,
      d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0000\b\u0086\u0003\u0018\u00002\u00020\u0001B\u0007\b\u0002¢\u0006\u0002\u0010\u0002J\u0006\u0010\u0003\u001a\u00020\u0004¨\u0006\u0005"},
      d2 = {"Ltest/Util$Companion;", "", "()V", "doAction2", "", "kotlin01"}
   )
   public static final class Companion {
      public final void doAction2() {
         String var1 = "do action2";
         boolean var2 = false;
         System.out.println(var1);
      }
      private Companion() {
      }
      // $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}

由此看出,Kotlin中确实没有直接定义静态方法的关键字,但是提供了一些语法特性来支撑类似于静态方法调用的写法,这些语法特性基本上可以 满足我们平时的开发需求了。

如果需要真正的静态方法,Kotlin提供了两种实现方式:注解和顶层方法。

先来看注解,前面的单例类和companion object都只是在语法的形式上模仿了静态方法的调用方式,实际上他们都不是真正的静态方法。因此如果你在Java中以静态方法的形式去调用的话,你会发现这些方法并不存在。而如果我们给单例类或companion object中的方法加上@JvmStatic注解,那么Kotlin编译器就会将这些方法编译成真正的静态方法,如下所示:

class Util{
    fun doAction1(){
        println("do action1")
    }
    companion object{
        @JvmStatic
        fun doAction2(){
            println("do action2")
        }
    }
}

编译成字节码,此时doAction2()方法为静态方法

public final class Util {
   public static final Util.Companion Companion = new Util.Companion((DefaultConstructorMarker)null);
   public final void doAction1() {
      String var1 = "do action1";
      boolean var2 = false;
      System.out.println(var1);
   }
   @JvmStatic
   public static final void doAction2() {
      Companion.doAction2();
   }
   @Metadata(
      mv = {1, 1, 16},
      bv = {1, 0, 3},
      k = 1,
      d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0000\b\u0086\u0003\u0018\u00002\u00020\u0001B\u0007\b\u0002¢\u0006\u0002\u0010\u0002J\b\u0010\u0003\u001a\u00020\u0004H\u0007¨\u0006\u0005"},
      d2 = {"Ltest/Util$Companion;", "", "()V", "doAction2", "", "kotlin01"}
   )
   public static final class Companion {
      @JvmStatic
      public final void doAction2() {
         String var1 = "do action2";
         boolean var2 = false;
         System.out.println(var1);
      }
      private Companion() {
      }
      // $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}

注意@JvmStatic注解只能加在单例类或Companion object中的方法上,如果你尝试加在一个普通方法上,会直接提示语法错误。

由于doAction2()方法已经成为真正的静态方法,那么现在不管是在Kotlin中还是Java中,都可以使用Util.doAction2()的写法来调用。

再来看顶层方法,顶层方法指的是那些没有定义在任何类中的方法,比如我们编写的main()方法。Kotlin编译器会将所有的顶层方法全部编译成静态方法,因此只要你定义了一个顶层方法,那么它就一定是静态方法。

想要定义一个顶层方法,首先要创建一个Kotlin文件。对着任意包名右击→New→Kotlin File/Class,在弹出的对话框中输入文件名即可。注意创建类型要选择File,如图所示

点击“ok”完成创建,这样刚刚的包名路径下就会出现一个Helper.kt文件。现在我们再这个文件中定义的任何方法都会是顶层方法,比如这里我就定义一个doSomething()方法吧,如下所示:

fun  doSomething(){
    println("do something")
}

Kotlin会将所有的顶层方法全部编译成静态方法,那么我们要怎么调用这个doSomething()方法呢?

如果是在Kotlin中调用的话,所有的顶层方法都可以在任何位置被直接调用,不用管包名路径,也不用创建实例,直接输入doSomething()即可。

但如果在Java代码中调用,你会发现找不到doSomething()这个方法,因为Java中没有顶层方法这个概念,所有的方法必须定义在类中。那么这个doSomething()被藏在哪里呢?我们刚才创建的Kotlin文件名叫做Helper.kt,于是Kotlin编译器会自动创建一个叫作Helperkt的Java类,doSomething()方法就是以静态方法的形式定义在HelperKt类里面的,因此在Java中使用Helperkt.doSomething()的写法来调用就可以了。

到此这篇关于Kotlin标准函数与静态方法基础知识详解的文章就介绍到这了,更多相关Kotlin标准函数与静态方法内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • kotlin快速入门之标准函数与静态方法

    目录 标准函数 with run apply 静态方法 单例类实现静态方法 伴生类实现静态方法 注解实现静态方法 顶层方法实现静态方法 总结 标准函数 首先我们介绍标准函数 with.run.apply,如果你了解javascript,那理解kotlin的标准函数width.run.apply那简直不要太轻松.with.run.apply与javascript中的with的意义基本一模一样,只是含有一些细微差别. with with它接收两个参数,第一个参数可以是任意类型的对象,第二个参数是一个

  • Kotlin标准函数与静态方法应用详解

    目录 标准函数 with函数 run函数 apply函数 静态方法 单例类 companion object 注解 顶层方法 标准函数 标准函数就是在Standard.kt文件中定义的函数,任何Kotlin代码都可以自由地调用所有的标准函数 let函数就属于是一个标准函数,经常配合?.操作符来进行判空处理 with函数 with函数接收两个参数,第一参数可以是任何类型的对象,第二个参数是一个Lambda表达式 with函数会在Lambda表达式中提供第一个参数的上下文,并使用Lambda表达式的

  • Kotlin 标准函数和静态方法示例详解

    目录 标准函数 with run Apply 定义静态方法 注解 顶层方法 标准函数 with with 的作用是可以在连续调用同一对象的多个方法时让代码变得更加精简 val result = with(obj){ //这里是obj的上下文 "value" //with 函数的返回值 } 看个例子,例如有一个水果列表,现在我们想吃完所有水果,并将结果打印出来 val list = listOf("Apple","Banana","Ora

  • Kotlin标准函数与静态方法基础知识详解

    目录 标准函数with与run和apply with函数 with函数 run函数 apply函数 定义静态方法 标准函数with与run和apply with函数 with函数 接收两个参数:第一个参数可以是任意类型的对象,第二个参数是一个Lambda表达式.with函数会在Lambda表达式中提供第一个参数对象的上下文,并使用Lambda表达式中的最后一行代码作为返回值返回. 示例代码如下: val result=with(obj){ //这里是obj的上下文 "value"//w

  • Cisco路由技术基础知识详解之一

    Cisco路由技术基础知识详解 路由器 <一> 最简单的网络可以想象成单线的总线,各个计算机可以通过向总线发送分组以互相通信.但随着网络中的计算机数目增长,这就很不可行了,会产 生许多问题: 1.带宽资源耗尽.     2.每台计算机都浪费许多时间处理无关的广播数据.     3.网络变得无法管理,任何错误都可能导致整个网络瘫痪.     4.每台计算机都可以监听到其他计算机的通信. 把网络分段可以解决这些问题,但同时你必须提供一种机制使不同网段的计算机可以互相通信,这通常涉及到在一些ISO网

  • Cisco路由技术基础知识详解

    Cisco路由技术基础知识详解 路由器 <一> 最简单的网络可以想象成单线的总线,各个计算机可以通过向总线发送分组以互相通信.但随着网络中的计算机数目增长,这就很不可行了,会产 生许多问题: 1.带宽资源耗尽.     2.每台计算机都浪费许多时间处理无关的广播数据.     3.网络变得无法管理,任何错误都可能导致整个网络瘫痪.     4.每台计算机都可以监听到其他计算机的通信. 把网络分段可以解决这些问题,但同时你必须提供一种机制使不同网段的计算机可以互相通信,这通常涉及到在一些ISO网

  • C语言入门之基础知识详解

    一.思维导图 内容不限于此思维导图 二.环境搭建 对于老手,自动跳过这一趴吧,或者也可以看一下我有没有啥纰漏,毕竟小白需要这一趴. 编译器很多,大部分老师会在学生学习C语言的时候推荐使用VC,不带语言提示器的那种,说是可以提高学生的编码能力.我也不知道到底是不是这么一回事儿.我推荐使用VS,这样学的快,函数记不住的问题很严重吗?项目的车轮碾压过去,再记不住也得记住吧!!! 更何况这个系列到后面是会有需要用文本文件编程写项目的阶段. 下载VS2019社区版,不要标新立异选那些最新版的,出了问题到时

  • java二维数组基础知识详解

    目录 1. 查找 2. 顺序查找 3. 二分查找 4. 多维数组 4.1 二维数组 175 4.2 二维数组细节 5. 二维数组的使用方式 176 6. 二维数组的动态初始化 1.先声明:类型 数组名[][]; 再定义(开辟空间) 数组名 = new 类型[大小][大小] 2.动态初始化-列数不确定 178 7. 二维数组的静态初始化 179 8. 二维数组练习 180 8.1 int arr[][]={{4,6},{1,4,5,7},{-2}}; 遍历该二维数组,并得到和 1. 查找 1) 顺

  • Android RecyclerView 基础知识详解

    本周的谷歌I/O大会带来了很多关于Android的振奋人心的消息.可能我们需要较长的时间来消化Android L引入的新东西. 这些天我一直在研究RecyclerView,并想在此给各位分享一下到目前为止我的成果. RecyclerView是什么? RecyclerView是一种新的视图组,目标是为任何基于适配器的视图提供相似的渲染方式.它被作为ListView和GridView控件的继承者,在最新的support-V7版本中提供支持. 在开发RecyclerView时充分考虑了扩展性,因此用它

  • .Net Core 3.1 Web API基础知识详解(收藏)

    目录 一.前言 二.Swagger调试Web API 三.配置文件 四.文件上传 五.统一WebApi数据返回格式 六.模型验证 七.日志使用 八.依赖注入 九.缓存 十.异常处理 十一.应用安全与JWT认证 十二.跨域 一.前言 随着近几年前后端分离.微服务等模式的兴起,.Net Core也似有如火如荼之势 ,自16年发布第一个版本到19年底的3.1 LTS版本,以及将发布的.NET 5,.NET Core一路更迭,在部署和开发工具上也都支持了跨平台应用.一直对.Net Core有所关注,但未

  • javascript中json基础知识详解

    大致介绍 JSON(JavaScript Object Notation  JavaScript对象表示法),JSON是一种数据格式,不是一种编程语言.虽然它的名字中有JavaScript但是它却不属于JavaScript,就像Java和JavaScript的关系一样.而且,并不是只有JavaScript才使用它,毕竟 JSON 只是一种数据格式.很多编程语言都有针对 JSON 的解析器和序列化器. JSON是由Douglas Crockford在2001年提出,为了取代XML 语法 JSON的

  • ADO.NET基础知识详解

    ADO.NET是微软提供的一种数据库访问技术. ADO.NET为不同类型的数据源提供了不同的数据提供程序对象: 数据提供程序 说明 SQL Server 数据提供程序 提供对Microsoft SQL Server中数据的访问,使用System.Data.SqlClient命名空间. OLE 数据提供程序 提供对使用OLE DB公开的数据源(如Access.Excel等)中数据的访问,使用System.Data.oleDb命名空间. ODBC 数据提供程序 提供对使用ODBC公开的数据源中数据的

  • WebPack基础知识详解

    1.什么是Webpack WebPack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其打包为合适的格式以供浏览器使用. 2.为什要使用WebPack 今的很多网页其实可以看做是功能丰富的应用,它们拥有着复杂的JavaScript代码和一大堆依赖包.为了简化开发的复杂度,前端社区涌现出了很多好的实践方法 a:模块化,让我们可以把复杂的程序细化为小的文件; b:类似于TypeSc

随机推荐