在之前我们已经接触到如何使用setOf函数创建一个set,我们也可以用同样的方法创建list和map
这里的to是一个函数,之后的篇章会讲解
输出结果
可以看出kotlin没有用它自己的集合类而是采用了标准的Java集合类。没有自己专门的集合类使得kotlin更容易与java代码交互
kotlin对集合类有更多的操作
last函数获取一个列表的最后一个元素,maxOrNull得到一个数字列表的最大值
结果
java的集合都有一个默认的toString实现,但是它格式化的输出是固定的,而且往往不是我们想要的样子。比如我们现在要用分号来分隔每一个元素,然后用括号括起来,而不是采用默认实现的方括号。在java中要使用到第三方库,而在kotlin中标准库有专门的函数来处理这种情况。
我们先定义一个函数appendString,参数分别代表列表,分隔符,前缀,后缀。在Java中这种函数的缺点是参数隐晦。
而在kotlin中可以变成:
当调用一个kotlin定义的函数时,可以显式表明一些参数的名称。如果调用一个函数时指明了一个参数名称,为了避免混淆,它之后的所有参数都要标明名称
在kotlin中可以在生命函数的时候指定参数的默认值,这样就可以避免创建重载的函数,修改函数如下图。当使用常规的调用语法时,必须按照函数声明中定义的参数顺序来给定参数,可以省略的只有排在末尾的参数,如果使用命名参数,可以省略中间部分参数,也可以按任意顺序只给定我们自己需要的参数
结果
参数的默认值是被编码到被调用的函数中,而不是调用的地方。由于Java没有参数默认值的概念,如果想它能对Java的调用者更简便,可以用@JvmOverloads注解它,它会让编译生成Java重载函数,从最后一个开始省略每个参数,上图的函数将会变成四个重载函数。
在kotlin中可以把函数直接放到代码文件的顶层,不用从属于任何的类
新建一个myAppend.kt文件
在JVM中它会生成一个类,类的名字就是你的文件名。这个文件中的所有顶层函数编译为这个类的静态函数。因此当从Java中调用这个函数的时候和调用任何其他静态函数一样。
要修改包含kotlin顶层函数的生成的类的名称,需要为这个文件添加@JvmName注解
,将其放到文件开头,位于包名前面
顶层属性
属性也可以放到文件的顶层,在一个类的外面保存单独的数据,不常用。
这个值会被存储到一个静态的字段中,也可以在代码中用顶层属性来定义常量
把要扩展的类或接口名称放在即将添加的函数前面,这个类的名称叫做接收者类型,用来调用这个扩展函数的那个对象叫做接收者对象
演示
在这里String就是接收者类型,"yyds"就是接收者对象
而且接收者对象成员可以不用this访问
注意!扩展函数不允许你打破它的封装性,扩展函数不能访问私有的或者是受保护的成员。在调用的一方,扩展函数和成员函数没有区别。
我们自己定义的扩展函数不会自动地在整个项目范围生效,如果要使用它就要导入,这是为了避免偶然性的命名冲突。kotlin允许用和导入类一样的语法来导入单个函数
导入的三种形式
扩展函数实质是静态函数
,它把调用对象作为它第一个参数。调用扩展函数不会创建适配的对象或者任何运行时的额外消耗。在java中调用只需要调用这个(类名是文件名)
静态函数然后把接收者对象作为第一个参数传入即可
最终版本
扩展函数无非是静态函数的一个语法糖,可以使用更具体的类型作为接收者类型,而不是一个类。它的性质也决定了扩展函数不能被子类重写,扩展函数并不是类的一部分,而是声明在类之外的
当我们调用一个类型为View的变量的show函数时,对应的扩展函数会被调用,即使它现在是一个Button对象,所以并不存在重写。
如果一个类的成员函数和扩展函数有相同的签名,成员函数往往会被优先使用。当在扩展API类的时候:如果添加一个和扩展函数同名的成员函数,那么对应类定义的消费者将会重新编译代码,这将会改变它的意义并开始指向新的成员函数
用来扩展类的API,可以用来访问属性,用的是属性语法而不是函数的语法。尽管他们被成为属性,但它们可以没有任何状态,因为没有合适的地方存储它,不可能给现有的Java对象实例添加额外的字段。我们把前面的lastOne函数改成一个属性
和扩展函数一样,扩展属性也想接收者的一个普通的成员属性一样。这里必须定义getter函数,因为没有支持字段,所以没有默认getter的实现。同理也不能初始化因为没有位置存储初始值。如果在StringBuilder上定义一个相同的属性可以置为var,因为StringBuilder的内容是可变的
注意!当你需要在Java中访问扩展属性时应该显式调用它的getter函数。
在之前我们提到了函数last和max,分别获取列表的最后一个元素和最大值,它们的实现就是使用了扩展函数
当你在调用一个函数来创建列表的时候,可以传递任意个数的参数给它。比如我们之前使用过的listOf函数
在参数上使用关键字vararg
修饰符便可以实现可变参数。
当需要的参数已经包装在数组中时,调用该函数的语法,在Java中可以按原样传递数组,而在kotlin中则要求显式解包数组,以便每个数组元素在函数中能作为单独的参数来调用。从技术角度来说这个功能被称为展开运算符,使用的时候在对应的参数面前放一个*
:
这个to不是内置的结构,而是一种特殊的函数调用,被称为中缀调用。中缀调用可以与只有一个参数的函数一起使用,无论是普通函数还是扩展函数。要允许使用中缀符号调用函数,需要使用infix
修饰符来标记它
to函数会返回一个Pair类型的对象,它用来表示一对元素。它们都用到了泛型,为了简单我们省略了泛型。
kotlin字符串和java字符串完全相同
java中的split函数
不适用于点号".",因为它将一个正则表达式作为参数,并根据表达式将字符串分割成多个字符串,而点好是表示任何字符的正则表达式。 kotlin中提供了名为split的具有不同参数的重载的扩展函数。用来承载正则表达式的值需要一个Regex类型,而不是String,这样确保了当有一个字符串传递给这些函数的时候,不会被当作正则表达式。
当遇到要解析文件的完整路径名称到对应的组件:目录、文件名、扩展名。kotlin标准库中包含了一些可以用来获取在给定分隔符第一次(或最后一次)出现之前(或之后)的子字符串的函数。
结果
使用正则表达式完成同样的事情
在三重引号的字符串中不需要对任何字符进行转义
它的目的不仅在于避免转义字符,而且可以使它包含任何字符包括换行符,从而提供了一种更简单的方法把包含换行符的文本嵌入到程序中,可以调用trimMargin来删除每行中的前缀和前面的空格。 需要使用多行字符串的一个地方是测试。在测试中往往需要执行产生多行文本的操作并将结果与预期输出进行比较。它完美解决了将预期输出作为测试的一部分的问题,不需要笨拙的转义或从外部文件加载文本,只需放入一些引号并将预期的HTML或其他输出放在它们中间。
你可能不想要在类中一个面面俱到的方法中,去验证用户字段的每一种特殊情况,但如果把验证代码放到局部函数中,可以摆脱重复
这相当于集成了判断和输出到一个局部函数中,再调用该局部函数,这一整个操作是saveUser的操作
在局部函数中可以访问所在函数的所有参数和变量,所以可以去掉冗余的User参数
改进成扩展函数,把验证逻辑放到User类的扩展函数中
扩展函数也可以被声明为局部函数,但深度嵌套的局部函数往往令人费解,一般不建议用。