kotlin的一些学习和使用时遇到的问题

前段时间学习了一下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修饰的函数称为挂起函数,只能在协程中调用,调用这个方法可能会造成协程挂起。

等等
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页