一周入门Kotlin(五)

来源:互联网 发布:java 递归 编辑:程序博客网 时间:2024/06/06 00:35

本章内容主要介绍泛型,常见泛型函数,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,启动浏览器,发邮件,分享等等…