一周入门Kotlin(五)

本章内容主要介绍泛型,常见泛型函数,SharedPreferences,枚举,等知识点的使用

1.泛型

在java中,泛型主要是用来在代码中约束数据的类型的。常见的泛型一般在类的定义和函数的定义中.
举个例子,下面的代码中有个叫doSomething的函数,他需要传入某一类型的数据K,返回类型T的数据,那么泛型的定义如下:

class TypeClass {

    fun <K,T>doSomething(k: K): T? {
        return null
    }

}

当然,在java中,我们的类型支持上限(extends)和下限(super),而在kotlin中简化了这一操作。比如,我觉得上面的函数可以传入任意类型,那么也可以这么写:

class TypeClass {

    fun <K:Any,T:Any>doSomething(k: K): T? {
        return null
    }

}

这样,当我们传入的数据为任意类型的子类都可以通过,比如我传递一个String类型的参数,返回一个Int类型的数据,也是可以通过的编译的。

以上介绍的是泛型定义在函数中,如果出现在类型呢?上面的代码可以变为:

class TypeClass <K:Any,T:Any>{

    fun doSomething(k: K): T? {
        return null
    }

}

上面的代码说明了该类有2个类型需要限定,这是Java的做法,但有时候我们要清楚哪个类型的传入参数的类型,哪个参数是返回的参数的类型,这里参考了C#语言,可以改变为:

class TypeClass <in K:Any,out T:Any>{

    fun doSomething(k: K): T? {
        return null
    }

}

上面的代码中in 代表的是传入参数的类型,out代表使用该类型作为某个函数的返回值类型,不同的函数可以再类型多次声明只需要逗号隔开即可。接下来看看下面的代码如何操作:

//1.创建一个type1对象
var type1 = TypeClass<Any, String>()
//2.表达式右边函数的返回值根据上面的判断返回的类型应该是String(假如是"Hello Android !")
//3.那么表达式应该类似于  val doSomething:Any = "Hello Android !"
//  这样符合Java的逻辑 因为String是Any的子类
val doSomething: Any? = type1.doSomething(22.2)
Log.i("IT520", "doSomething $doSomething")

2.SharedPreferences的使用

对于一个APP而言,经常需要保存一些配置信息到手机内部,如果是简单的键值对我们可以使用SharedPreferences.假设现在想传递一个Long类型的数据到SharedPreferences中,我的目的是这样的,把该存储的数据操作设置给某个对象做为属性委托,当某个属性设值,则直接存储到sp中,当访问某个属性,则从sp中取出数据,代码如下:

class LongPreference(val ctx: Context, val name: String, val default: Long)
    : ReadWriteProperty<Any, Long> {

    val prefs: SharedPreferences by lazy {
        //创建SharedPreferences对应保存文件
        ctx.getSharedPreferences("defaultfile", Context.MODE_PRIVATE)
    }

    //当获取某个值的时候 调用该方法 内部将数据从SharedPreferences文件中取出
    override fun getValue(thisRef: Any, property: KProperty<*>): Long {
        return prefs.getLong(name, default)
    }

    //当设置某个值的时候 调用该方法 内部将数据存储到SharedPreferences文件中
    override fun setValue(thisRef: Any, property: KProperty<*>, value: Long) {
        prefs.edit().putLong(name,value).commit()
    }

}

接下来,我们可以使用object关键字声明一个对象,并通过函数返回上面的委托属性对象:

object DelegatesExt {

    fun longPreference(ctx: Context, name: String, default: Long)
            = LongPreference2(ctx, name, default)

}

如果使用该委托呢?下面在MainActivity中我们该出使用代码:

class MainActivity : AppCompatActivity(){

    var testValue: Long by DelegatesExt.longPreference(this, "zipCode", 0L)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        //设置该值 就会间接调用该委托将数据存储进SP
        testValue = 22L
        Log.i("IT520", "$testValue")
}

3.结合泛型和sp让代码更加通用

前面我们提到了如何让属性委托实现数据的存储,但是该存储只能存储Long类型的数据。接下来我们将通过泛型改装代码:

class PreferenceDelegate<T>(val ctx: Context, val name: String, val default: T)
    : ReadWriteProperty<Any, T> {

    val prefs: SharedPreferences by lazy {
        ctx.getSharedPreferences("defaultfile", Context.MODE_PRIVATE)
    }

    override fun getValue(thisRef: Any, property: KProperty<*>): T {
        return findSharedPreference(name, default)
    }
    //根据传递数据类型决定取出数据
    fun findSharedPreference(name: String, default: T): T {
        with(prefs) {
            val result: Any = when (default) {
                is Long -> getLong(name, default)
                is Int -> getInt(name, default)
                is Float -> getFloat(name, default)
                is String -> getString(name, default)
                is Boolean -> getBoolean(name, default)
                else -> throw IllegalArgumentException()
            }
            return result as T
        }
    }

    override fun setValue(thisRef: Any, property: KProperty<*>, value: T) {
        putSharedPreference(name, value)
    }

    //根据传递数据类型决定存放数据
    fun putSharedPreference(name: String, value: T) {
        with(prefs.edit()) {
            when (value) {
                is Long -> putLong(name, value).apply()
                is Int -> putInt(name, value).apply()
                is Float -> putFloat(name, value).apply()
                is String -> putString(name, value).apply()
                is Boolean -> putBoolean(name, value).apply()
                else -> throw IllegalArgumentException()
            }
        }
    }

}

object DelegatesExt {
    fun <T : Any> preference(ctx: Context, name: String, default: T)
            = PreferenceDelegate(ctx, name, default)
}

4.枚举

枚举这方面的技术与java的差不多,只是做了一点点的改进,如何创建枚举类呢?

enum class Day {
    SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
    THURSDAY, FIRDAY, SATURDAY
}

枚举改进的地方是,除了可以提供一个枚举值,还可以将枚举的附属信息添加到枚举值中,下面的Int就是我模拟的附属信息,可以一个也可以多个:

enum class Direction(val value:Int){
    EAST(1),
    WEST(2),
    SOUTH(3),
    NORTH(4)
}

接下来我们就可以使用枚举变量了:

//定义一个枚举变量
var today = Day.FIRDAY
//获取枚举变量的名称
Log.i("IT520", "FIRDAY name ${today.name}")
//获取枚举变量的索引
Log.i("IT520", "FIRDAY ordinal ${today.ordinal}")
//获取该枚举概念中所有数据的个数
Log.i("IT520", "Day size ${Day.values().size}")

5.Anko提供一个启动新界面的新方式

之前我们在上几篇提到,anko是kotlin在安卓中的轻量级额外开发包。他的commons包提供了一个方便使用Intent的框架类Intents.kt。

5.1 添加anko依赖包

在项目的build.gradle文件中,添加如下代码:

buildscript {
    ...
    ext.ANKO_VERSION = '0.10.0'
}

在app的build.gradle文件中,添加如下代码:

dependencies {
    ...
    compile "org.jetbrains.anko:anko-commons:$ANKO_VERSION"
}

5.2 Intent启动新的界面

假设我们有一个界面叫MainActivity,想启动一个新的SecondActivity的,并携带2个参数,那么MainActivity的启动代码应该是这样的:

startActivity<SecondActivity>(
                SecondActivity.USERNAME_KEY to "xiaoming",
                SecondActivity.PWD_KET to "123")

5.3 源码分析

inline fun <reified T: Activity> Context.startActivity(vararg params: Pair<String, Any>) {
    AnkoInternals.internalStartActivity(this, T::class.java, params)
}

object AnkoInternals {
    fun internalStartActivity(
           ctx: Context,
           activity: Class<out Activity>,
           params: Array<out Pair<String, Any>>) {
        ctx.startActivity(createIntent(ctx, activity, params))
    }
}

当然如果你查看源码 你会发现这真的是个很棒的类,除了可以启动新的界面,还可以启动服务,还能发送SMS,启动浏览器,发邮件,分享等等…

猜你喜欢

转载自blog.csdn.net/qq285016127/article/details/72861071