入行没几年的小码农,近期学习Kotlin,做一份笔记记录,此文依据《Kotlin实战》这本书的流程记录,部分示例内容均摘自《Kotlin实战》,记下自己的理解,本篇记录Kotlin中的方法细节。
Kotlin学习笔记系列
新手上路,Kotlin学习笔记(一)-- Kotlin入门介绍
新手上路,Kotlin学习笔记(二)---方法(函数)部分
新手上路,Kotlin学习笔记(三)---类、对象、接口
新手上路,Kotlin学习笔记(四)---Lambda表达式在Kotlin中的使用
新手上路,Kotlin学习笔记(五)---Kotlin中的类型系统
新手上路,Kotlin学习笔记(六)---运算符重载和其它约定
新手上路,Kotlin学习笔记(七)---Lambda作为形参和返回值的使用
新手上路,Kotlin学习笔记(八)---泛型的使用
新手上路,Kotlin学习笔记(九)---注解和反射
Kotlin中为集合的创建提供了新的方式,更加简便,比如
val arrayList = arrayListOf("1","a")
查看源码,发现此处返回的是Java中的ArrayList对象
/**
* Returns a new [ArrayList] with the given elements.
* @sample samples.collections.Collections.Lists.arrayList
*/
public fun arrayListOf(vararg elements: T): ArrayList
= if (elements.size == 0) ArrayList() else ArrayList(ArrayAsCollection(elements, isVarargs = true))
同样的,在Collections.kt文件下,有各种集合的创建方式,可以满足我们日常使用的各种需求,不再像Java一样复杂。
当我们在Java中进行方法重载的时候,需要写多个方法,重复的设置默认值调用同名重载方法,非常麻烦,并且在使用的时候,会提示一大堆方法出来,比如在Android自定义View的时候,会写多个View的构造方法,如果只写一个参数最多的方法,又需要在使用的时候传入很多默认值,各自都有不方便的地方。
public class MyView extends View {
public MyView(Context context) {
this(context,null);
}
public MyView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr,0);
}
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
}
而在Kotlin中,我们可以使用一个方法来解决上述问题,既可以减少无用代码的书写,又可以在调用的时候不用传入过多的默认值。
class MyKotlinView(context: Context? = null, attrs: AttributeSet? = null
, defStyleAttr: Int = 0, defStyleRes: Int = 0)
: View(context, attrs, defStyleAttr, defStyleRes)
只需要上面这么一点代码就能完成Java中的“长篇大论”。
Kotlin中使用冒号来继承父类或实现接口,这里不做详细解释,后面再介绍。
这里我们看到MyKotlinView的构造方法中还是有四个参数的,但是我们在方法的参数后面多写了 等号“=”,这个等号的作用就是给参数设置默认值,如果在使用的时候,没有给参数设置入参,那么就会使用默认值。
比如我们创建View的时候只需要Context,就可以这样写
val kotlinView = MyKotlinView(this)
此时后面的参数均会使用默认值,和前面Java中直接使用new MyView(this)一样。
更为强大的是,比如我们需要传入第一个参数和第三个参数,那么在Java中,我们在使用的时候就需要给第二个参数设置默认值或者重新写一份重载的方法,而在Kotlin中就不需要这么麻烦,可以直接这样使用
val kotlinView2 = MyKotlinView(context = this,defStyleRes = 0)
我们在书写入参的时候,指明这个值是给哪个参数使用的,这样就可以直接指定传入某个参数。
在开发中,有很多方法,我们在各个类中都会使用,通常我们会把这种方法统一放在一个类中,然后这个拥有大量静态方法的类被我们叫做静态工具类,可以说,没有静态工具类,我们的开发就很会很难受。
而在Kotlin中,我们有了静态工具类的替代者,那就是kotlin中的顶层函数
我们可以在一个类的外部书写一个方法,比如这样
package com.mtf.kotlin
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
fun string2Int(str : String) = Integer.parseInt(str)
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val kotlinView = MyKotlinView(this)
val kotlinView2 = MyKotlinView(context = this,defStyleRes = 0)
val index = string2Int("3")
}
}
在类MainActivity中写了一个方法string2Int,我们可以在类中直接调用该方法。
在另一个类中又要怎么用呢?让我们看下面的例子.
package com.mtf.other
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import com.mtf.kotlin.R
import com.mtf.kotlin.string2Int
class SecondActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_second)
val index = string2Int("333")
}
}
可以看出,SecondActivity是在另一个包下面的,那么我们调用string2Int方法的时候,就需要在上方import这个方法。
我们还可以对该方法重新命名,以避免有相同的方法名在该类中使用过了,只需要在import的时候添加as即可,如
import com.mtf.kotlin.string2Int as s2i
此时调用即为
val index = s2i("333")
另,如果是在同一个包下面,不需要import,可以直接使用,此处不再贴示例了。
我们的项目可能存在Java和Kotlin混合的情况,那么,顶层函数如何在Java中使用呢?
在我们的Java类中使用Kotlin的顶层函数时,也是和静态工具类的使用方式一样,只需要类名+方法名即可,类名就是顶层函数的文件名加上Kt,比如上面的string2Int就是
private void init()
{
int index = MainActivityKt.string2Int("123");
}
如果想要更改这个类名,只需要在包声明之前添加注解即可
@file:JvmName("StringUtil")
package com.mtf.kotlin
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
fun string2Int(str : String) = Integer.parseInt(str)
此时在调用的类名就变成了StringUtil
同样的,我们也可以在顶层声明属性,使用方法和顶层函数一致,不再进行过多解释。
在Kotlin中,我们可以给一个类额外的增加一些方法,这些类通常是API中的一些类,比如String等
添加方法的代码如下
fun String.lastChar() = this[length - 1]
添加之后,我们就可以直接在String对象上面使用这个方法了,比如
"123".lastChar() //结果为“3”
和顶层函数一样,当我们在不同的包下面使用时,需要Import,同时也可以更改方法的名称
tips:因为扩展方法是静态的,所以当父类和子类有相同的扩展方法时,会根据当前对象的声明类型来调用相应的方法,而不会使用子类的方法。
对于扩展属性,使用方式和扩展方法基本一致,示例如下
var String.lastchar : Char
get() = this[length - 1]
set(value : Char) = println("set lastChar is $value")
声明扩展属性的时候,必须要设置get方法,同时不能初始化。
Java中的可变长参数使用...表示,而在Kotlin中,使用关键字vararg声明即可,如本文开始提到的创建集合的方法,源码中就是用了vararg关键字声明可变长参数
public fun arrayListOf(vararg elements: T): ArrayList
Kotlin中,当一个函数只有一个参数的时候,还可以进行简化,如在Map创建的时候,可以这样做
val map = mapOf(1 to "one")
此处的to就是中缀调用的符号,to其实是一个方法,但是此处不需要使用间隔符,其效果等同于 1.to("one")
上面这种写法更加简便,省略了间隔符和括号等
在Kotlin中,当我们的方法只有一个参数时,只需要在方法前添加关键字infix,就可以使用这种方式了,如
infix fun String.getByIndex(index : Int) = this[index]
fun init()
{
val char = "123" getByIndex 1
}
上面方法就是可以中缀调用的方法声明,最前面为关键字infix 后面是 调用的对象 . 方法名 ( 参数 )
在Kotlin中,对正则表达式和字符串进行了区分,如果要使用正则表达式,需要使用字符串的toRegex方法,因为Kotlin中的正则表达式将是一个类型,这样可以避免字符串和正则表达式使用的不清楚。
Kotlin中表示字符串时,还可以用三个引号来表示字符串,这样使用的好处是,用三个引号包裹的字符串,不需要进行转义
txt.setText("1.1\"1")
txt.setText(""" 1.1"1""")
上述两行代码均为显示字符串 1.1"1 当使用了三个引号的字符串时,就不需要对其中的符号进行转义了,这个在正则表达式中尤为方便。同理,换行符在其中也不需要转义,可以直接换行书写,这种表示是可以跨行的。
当我们在某个方法中需要重复的进行某个步骤时,通常会将这段代码提取出来成为一个方法,然后去调用,但是很多时候,这个提取出来的方法又只某一个方法中使用,看起来还是不够方便美观。
Kotlin中就为我们提供了局部方法的写法,我们可以在一个方法中写一个局部方法,该局部方法只在这个方法中可以使用,并且可以获取到方法中的变量,也减少了Java中提取方法后,需要传入将要使用的变量的流程,示例如下
fun checkPerson(person: Person)
{
if (person.addr.isEmpty())
{
throw IllegalArgumentException("person.addr is Empty")
}
if (person.name.isEmpty())
{
throw IllegalArgumentException("person.name is Empty")
}
}
fun checkPersonSimple(person: Person)
{
fun checkempty(value: String, fieldName: String)
{
if (value.isEmpty())
{
println(person.name)//局部方法可以直接使用外面的变量,如此处的person
throw IllegalArgumentException("$fieldName is Empty")
}
}
checkempty(person.addr, "addr")
checkempty(person.name, "name")
}
上方的checkempty就是一个局部方法,通过对比可以看出简化了不少,也没有将需要重复使用的方法放置在类中,以免出现不必要的误使用。
下一章新手上路,Kotlin学习笔记(三)---类、对象、接口