前段时间学习了一下kotlin,主要是通过这本书来学习的,这个是中文版下载地址:
(https://github.com/wangjiegulu/kotlin-for-android-developers-zh)
这个网站是一个可以测试kotlin代码的,对学习kotlin也是挺有帮助的。
(https://try.kotlinlang.org)
也看了看这个,了解一下kotlin的常用语法等。
(http://kotlinlang.org/docs/reference)
然后也自己做了一个小项目感受了一下,感觉这门语言还是很ok的,写起来确实简单,目前没发现什么大问题,有一个小问题就是 android studio 有时候好像不识别ArrayList 和 MutableList的转换 经常会报错,只要删除在重新粘贴上去就可以了。
下面来记录一下自己使用的遇到的坑。。。
1.Extension(扩展函数)
这个东西大概是最早没接触的时候就一直在听大家在那里吹捧了,说是可以代替utils类啊什么的。
定义这个方法:Object.MethodName : returnType
Object是你要将这个扩展函数定义在哪个类下面,比如你要定义一个Activity的扩展函数就可以使用Activity.
MethodName就是方法名字啦
returnType是返回值类型,kotlin的默认返回类型不是void,而是一个Unit类型,对应java中的void类型。
使用这个的时候需要注意,需要把扩展函数定义为包级函数(就是新建一个kt文件里面不写class,直接写fun就行),如果写到类里面就只能在当前那个类里调用这个方法了。举个例子:
//一个Context扩展函数,获取屏幕高度。
fun Context.getScrennHeight(): Int {
//kotlin使用var的方式来声明变量,:后面跟的是变量的类型
//this@区分你要使用哪个类的this
//as关键字用来将对象转换到对应的类型
var wm: WindowManager = this@getScrennHeight.getSystemService(Context.WINDOW_SERVICE) as WindowManager
return wm.defaultDisplay.height
}
//我们在activity中就可以直接调用这个方法,就像这样:
class Activity1 : Activity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//调用我们的extension
getScreenWidth()
}
}
2.companion object(伴生对象)
kotlin中没有static方法,所以如果我们想要使用像java中的静态方法的调用,好像也没法实现静态代码块了,不过可以写在companion object块中也可以接受吧。在companion object块中定义的函数或者是对象可以直接通过类名.来调用。kotlin没有构造方法 但是 有一个init块,在实例化的时候会调用这个init块,有点仿照jvm的实现。
class Test1{
init {
//实例化时调用
}
companion object{
//这个块里面的可以直接通过类名.调用
}
}
3.inline fun(内联函数)
见名知意,就是一个函数接收一个函数作为参数,内联函数会在编译的时候被替换掉,而不是真正调用这个方法,可以减少我们的内存分配和运行时开销,举个例子吧:
inline fun inline1(code : () -> Unit){
//code是我们自己定义的方法名字 -> 后面是返回值
//这里可以写一些判断条件,比如我们android经常要判断sdk版本来执行不同的方法
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) code()
}
fun invokeInline1(){
inline1{
//调用内联函数inline1
//当条件满足才会执行code()
}
}
这样我们在编译的时候code()会被替换掉,而不是调用code(),就不需要为code()生成一个的对象。
4.with函数
这个函数在adapter中简直不要太好用,api中的定义是这样的
@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
接收一个T类型对象和一个T中的方法作为参数,并且这个T中的方法的返回值是R类型,最后返回结果也是R类型,这个就是相当于是返回第二个参数作为返回结果了嘛。所以,我们在adapter写出来就像这样:
//声明我们的data类,data类也是kotlin中特有的一个扩展,可以帮我们自动生成get set方法,不需要写实现,我们只需要定义类就可以了
data class PhotoData(var name:String)
...
//MutableList是kotlin增强版的Arraylist 支持各种过滤操作符
var mData : MutableList<PhotoData> = ArrayList()
...
//这个是在bindview中使用我们的with函数
with(mData[position]){
// holder为adapter中的ViewHolder对象
// ?.就相当于java中的 if null else
//image假设是我们的控件,text=setText
//我们就可以直接调用我们PhotoData中的变量,而不用在通过.的方式来调用了
holder?.itemview.image.text = name
}
5.匿名内部类的写法
这个是刚开始用的时候遇到的一个坑,kotlin本身是支持lambda的,比如我们写一个onClick事件的时候就可以直接这样写:
view.setOnClickListener {
//直接在这里写操作
}
但是如果我们要是想将它写全的话就得这么写:
view.setOnClickListener(object : View.OnClickListener{
override fun onClick(v: View?) {
TODO("not implemented")
}
})
使用object关键字:后面就是它的值类型
6.lateinit关键字
这个关键字可以用来延迟初始化、避免非空检查的,并且不能为基本数据类型添加此关键字,而且这个关键字只适用于var类型变量,因为有些时候我们声明变量的时候不会赋初始值并且不希望添加?来作非空判断,我们就可以使用这个关键字来声明:
比如声明一个String类型的变量:lateinit str:String;
还有一个是我们使用ButterKnife的时候,在java文件中我们需要这样去bindview:
@BindView(R2.id.title) TextView title;
而在kotlin中,我们需要这样去bind:
@BindView(R2.id.title)
lateinit var title: TextView
7.函数式接口在kotlin中的使用
函数式接口在1.8新添加,大致定义就是一个接口中只有一个抽象方法(并且这个方法不能是重写Object类的)那么就称这个接口为函数式接口,你可以显式的添加@FunctionalInterface来标注你的接口表示这是一个函数式接口,如果你不写@FunctionalInterface但是你的接口符合函数式接口的定义,编译器也同样的会认为你这个是一个函数式接口,kotlin中有一个SAM(simple abstract method)概念,对应函数式接口。下面来看一个函数式接口在java中定义的例子:
class TestFunctionInterface{
@FunctionalInterface
public interface MyInterface{
void callback(Result result);
}
public MyInterface myInterface;
public void setInterface(MyInterface myInterface){
this.myInterface = myInterface;
}
...
public void doWork(){
myInterface.callback(Result());
}
}
然而换成kotlin,是这样写的:
class TestFunctionInterface(private val callback : (Result) -> Unit){
fun doWork(){
callback(Result())
}
}
//调用的时候
TestFuncationInterface({
//方法的具体实现写在这里
}).doWork()
8.reified关键字的使用
使用这个关键字修饰泛型,可以理解为一种约束,如果支持反射的话,我们还可以直接得到这个Class,目前只支持在inline函数中使用。看一下怎么使用吧,以retrofit构造为例,我们都知道retrofit构造的时候需要传入一个Service Class。结合reified表达式就可以写成如下形式
//通过这个 可以获取到我们的Serivce对象
fun getRetrofit<reified T>() : T{
Retrofit.Builder()
....
.build()
.create(T::java.class)
}
然后使用的时候直接调用这个方法就会返回一个Service对象了,然后调用我们对应的请求即可。
其实还有好多小特性
比如if else 可以使用=号啊
使用const修饰val类型的变量在编译期间属性值就能被确定
anko库可以直接支持我们在activity中直接使用xml中的id来对控件直接进行操作而不用find了(fragment中还得find。。)
声明变量时如果不带?出现null的情况编译不会通过
方法体如果直接一行,返回值可以直接用等号表示
请求网络可以直接使用URL(url).readText()
asynctask使用直接可以用 doAsync{}快来表示
Class类型的表示方式:Test::class.java
java中Test.class的表示方式在kotlin是应该这样写Test().javaclass
使用lazy委托来实现延迟加载(kotlin中声明变量必须要赋值)
by map 使用map委托来声明map
创建list可以使用:listOf()
声明普通数组使用: Array<T>
interface中可以直接写方法实现,这个是java8才支持的
简单的EL表达式也是可以使用
is关键字:a is b 表示a是否属于b类型
out参数修饰符:协变(convariance),表示该参数只能作为返回值而不能作为传入参数
in参数修饰符:逆变(contravariance),表示该参数只能作为传入值,不能作为返回值
to关键字,,返回一个Pair对象
使用tailrec关键字修饰递归函数可以使编译器对函数进行优化
使用suspend修饰的函数称为挂起函数,只能在协程中调用,调用这个方法可能会造成协程挂起。
等等