我的编程空间,编程开发者的网络收藏夹
学习永远不晚
位置:首页-资讯-运维

Kotlin学习教程(五)

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Kotlin学习教程(五)

泛型

class Data<T>(var t : T)
interface Data<T>
fun <T> logic(t : T){}

定义:

class TypedClass<T>(parameter: T) {
    val value: T = parameter
}

这个类现在可以使用任何的类型初始化,并且参数也会使用定义的类型,我们可以这么做:

val t1 = TypedClass<String>("Hello World!")
val t2 = TypedClass<Int>(25)

但是Kotlin很简单并且缩减了模版代码,所以如果编译器能够推断参数的类型,我们甚至也就不需要去指定它:

val t1 = TypedClass("Hello World!")
val t2 = TypedClass(25)
val t3 = TypedClass<String?>(null)
类型擦除
class Data<T>{}

Log.d("test", Data<Int>().javaClass.name)
Log.d("test", Data<String>().javaClass.name)

// 输出
com.study.jcking.weatherkotlin.exec.Data
com.study.jcking.weatherkotlin.exec.Data

声明了一个泛型类Data<T>,并实现了两种不同类型的实例。但是在获取类名是,却发现得到了同样的结果
com.study.jcking.weatherkotlin.exec.Data,这其实是在编译期擦除了泛型类型声明。

嵌套类

嵌套类顾名思义,就是嵌套在其他类中的类。而嵌套类外部的类一般被称为包装类或者外部类。

class Outter{
    class Nested{
        fun execute(){
            Log.d("test", "Nested -> execute")
        }
    }
}

// 调用
Outter.Nested().execute()

//输出
Nested -> execute

嵌套类可以直接创建实例,方式是包装类.嵌套类
val nested : Outter.Nested()

内部类

内部类和嵌套类有些类似,不同点是内部类用关键字inner修饰。

class Outter{
    val testVal = "test"
    inner class Inner{
        fun execute(){
            Log.d("test", "Inner -> execute : can read testVal=$testVal")
        }
    }
}

// 调用
val outter = Outter()
outter.Inner().execute()

// 输出
Inner -> execute : can read testVal=test

内部类不能直接创建实例,需要通过外部类调用

val outter = Outter()
outter.Inner().execute()

匿名内部类

// 通过对象表达式来 创建匿名内部类的对象,可以避免重写抽象类的子类和接口的实现类,这和Java中匿名内部类的是接口和抽象类的延伸一致。
text.setonClickListener(object : View.OnClickListener{
    override fun onClick(p0: View?) {
        Log.d("test", p0.string())
    }
})

或
mViewPager.addonPageChangeListener(object : ViewPager.OnPageChangeListener {
    override fun onPageScrollStateChanged(state: Int) {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }

    override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }

    override fun onPageSelected(position: Int) {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }

})

枚举

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

枚举可以带参数:

enum class Icon(val res: Int) {
    UP(R.drawable.ic_up),
    SEARCH(R.drawable.ic_search),
    CAST(R.drawable.ic_cast)
}

val searchIconRes = Icon.SEARCH.res

枚举可以通过String匹配名字来获取,我们也可以获取包含所有枚举的Array,所以我们可以遍历它。

val search: Icon = Icon.valueOf("SEARCH")
val iconList: Array<Icon> = Icon.values()

而且每一个枚举都有一些函数来获取它的名字、声明的位置:

val searchName: String = Icon.SEARCH.name()
val searchPosition: Int = Icon.SEARCH.ordinal()

密封类

密封类用来表示受限的类继承结构:当一个值为有限集中的
类型、而不能有任何其他类型时。在某种意义上,他们是枚举类的扩展:枚举类型的值集合
也是受限的,但每个枚举常量只存在一个实例,而密封类
的一个子类可以有可包含状态的多个实例。

要声明一个密封类,需要在类名前面添加sealed修饰符。虽然密封类也可以
有子类,但是所有子类都必须在与密封类自身相同的文件中声明。(在Kotlin 1.1之前,
该规则更加严格:子类必须嵌套在密封类声明的内部)。

sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()

fun eval(expr: Expr): Double = when (expr) {
    is Const -> expr.number
    is Sum -> eval(expr.e1) + eval(expr.e2)
    NotANumber -> Double.NaN
}

使用密封类的关键好处在于使用when表达式 的时候,如果能够
验证语句覆盖了所有情况,就不需要为该语句再添加一个else子句了。

fun eval(expr: Expr): Double = when(expr) {
    is Expr.Const -> expr.number
    is Expr.Sum -> eval(expr.e1) + eval(expr.e2)
    Expr.NotANumber -> Double.NaN
    // 不再需要 `else` 子句,因为我们已经覆盖了所有的情况
}

异常

Kotlin中,所有的Exception都是实现了Throwable,含有一个message且未经检查。这表示我们不会强迫我们在任何地方使用try/catch
这与Java中不太一样,比如在抛出IOException的方法,我们需要使用try-catch包围代码块。但是通过检查exception来处理显示并不是一个
好的方法。

抛出异常的方式与Java很类似:

throw MyException("Exception message")

try表达式也是相同的:

try{
    // 一些代码
}
catch (e: SomeException) {
    // 处理
}
finally {
    // 可选的finally块
}

Kotlin中,throwtry都是表达式,这意味着它们可以被赋值给一个变量。这个在处理一些边界问题的时候确实非常有用:

val s = when(x){
    is Int -> "Int instance"
    is String -> "String instance"
    else -> throw UnsupportedOperationException("Not valid type")
}

或者

val s = try { x as String } catch(e: ClassCastException) { null }

对象(Object)

声明对象就如同声明一个类,你只需要用保留字object替代class,其他都相同。只需要考虑到对象不能有构造函数,因为我们不调用任何构造函数来访问
它们。事实上,对象就是具有单一实现的数据类型。

object Resource {
    val name = "Name"
}

单例

object Resource {
    val name = "Name"
}

因为对象就是具有单一实现的数据类型,所以在kotlin中对象就是单例。
对象的实例在我们第一次使用时,被创建。所以这里有一个懒惰实例化:如果一个对象永远不会被使用,这个实例永远不会被创建。

对象表达式

对象也能用于创建匿名类实现。

recycler.adapter = object : RecyclerView.Adapter() {   
    override fun onBindViewHolder(holder: RecyclerView.ViewHolder?, position: Int) {   
    }

    override fun getItemCount(): Int {   
    }

    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): RecyclerView.ViewHolder {   
    }}

例如,每次想要创建一个接口的内联实现,或者扩展另一个类时,你将使用上面的符号。

伴生对象(Companion Object)

每个类都可以实现一个伴生对象,它是该类的所有实例共有的对象。它将类似于Java中的静态字段。

class App : Application() {   
    companion object {   
         lateinit var instance: App             private set     }

     override fun onCreate() {   
         super.onCreate()
         instance = this
    }}

在这例子中,创建一个由Application扩展的(派送)的类,并且在companion object中存储它的唯一实例。
lateinit表示这个属性开始是没有值得,但是,在使用前将被赋值(否则,就会抛出异常)。
private set用于说明外部类不能对其进行赋值。

委托(代理)

类委托

委托模式是最常用的设计模式的一种,在委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。
kotlin中的委托可以算是对委托模式的官方支持。
Kotlin直接支持委托模式,更加优雅,简洁。Kotlin通过关键字by实现委托。

interface base{
    fun print()
}

class baseImpl(val x : Int) : base{
    override fun print() {
        Log.d(JTAG, "baseImpl -> ${x.string()}")
    }
}

class Printer(b : base) : base by b

fun test(){
    val b = baseImpl(5)
    Printer(b).print()
}

// 输出
baseImpl -> 5

可以看到Printer类没有实现接口base的方法print(),而是通过关键字by将实现委托给了b,而输出也和预想的一样。

属性委托

语法是val/var <属性名>: <类型> by <表达式>。在by后面的表达式是该委托,因为属性对应的get()set()会被委托给它的getValue()
setValue()方法。 属性的委托不必实现任何的接口,但是需要提供一个getValue()函数(和setValue()——对于var属性)。

class Example {
    var property : String by DelegateProperty()
}

class DelegateProperty {
    var temp = "old"

    operator fun getValue(ref: Any?, p: KProperty<*>): String {
        return "DelegateProperty --> ${p.name} --> $temp"
    }

    operator fun setValue(ref: Any?, p: KProperty<*>, value: String) {
        temp = value
    }
}

fun test(){
    val e = Example()
    Log.d(JTAG, e.property)
    e.property = "new"
    Log.d(JTAG, e.property)
}

// 输出
DelegateProperty --> property --> old
DelegateProperty --> property --> new

像上面的DelegateProperty这样,被一个属性委托的类,我叫它被委托类,委托它的属性叫委托属性。其中:

  • 如果委托属性是只读属性即val,则被委托类需要实现getValue方法

  • 如果委托属性是可变属性即var,则被委托类需要实现getValue方法和setValue方法

  • getValue方法的返回类型必须是与委托属性相同或是其子类

  • getValue方法和setValue方法必须要用关键字operator标记

Kotlin通过属性委托的方式,为我们实现了一些常用的功能,包括:

  • 延迟属性lazy properties

  • 可观察属性observable properties

  • map映射

延迟属性

延迟属性我们应该不陌生,也就是通常说的懒汉,在定义的时候不进行初始化,把初始化的工作延迟到第一次调用的时候。kotlin中实现延迟属性很简单,
来看一下。

val lazyValue: String by lazy {
    Log.d(JTAG, "Just run when first being used")
    "value"
}

fun test(){
    Log.d(JTAG, lazyValue)
    Log.d(JTAG, lazyValue)
}

// 输出
Just run when first being used
value
value

可以看到,只有第一次调用了lazy里的日志输出,说明lazy方法块只有第一次执行了。按照个人理解,上面的lazy模块可以这么翻译

String lazyValue;
String getLazyValue(){
    if(lazyValue == null){
        Log.d(JTAG, "Just run when first being used");
        lazyValue = "value";
    }
    return lazyValue;
}

void test(){
    Log.d(JTAG, getLazyValue());
    Log.d(JTAG, getLazyValue());
}
可观察属性

可观察属性对应的是我们常用的观察者模式,机制类似于我们给View增加Listener。同样的kotlin给了我们很方便的实现:

class User {
    var name: Int by Delegates.observable(0) {
        prop, old, new -> Log.d(JTAG, "$old -> $new")
    }

    var gender: Int by Delegates.vetoable(0) {
        prop, old, new -> (old < new)
    }
}

fun test(){
    val user = User()
    user.name = 2    // 输出 0 -> 2        
    user.name = 1   // 输出 2 -> 1    

    user.gender = 2
    Log.d(JTAG, user.gender.string())   // 输出 2
    user.gender = 1
    Log.d(JTAG, user.gender.string())   // 输出 2
}

Delegates.observable()接受两个参数:初始值和修改时处理程序handler。 每当我们给属性赋值时会调用该处理程序(在赋值后执行)。
它有三个参数:被赋值的属性、旧值和新值。在上面的例子中,我们对user.name赋值,set变化触发了观察者,执行了Log.d代码段。

除了Delegates.observable()之外,我们还把gender委托给了Delegates.vetoable(),和observable不同的是,observable是执行了
set变化之后,才触发observable,而vetoable则是在set执行之前被触发,它返回一个Boolean,如果为true才会继续执行set
在上面的例子中,我们看到在第一次赋值user.gender = 2时,由于2>0,所以old<new判断成立,所以执行了set方法,gender为2,
第二次赋值user.gender = 1则没有通过判断,所以gender依然为2。

map映射

一个常见的用例是在一个映射map里存储属性的值。这经常出现在像解析JSON或者做其他“动态”事情的应用中。在这种情况下,你可以使用映射实例自身作
为委托来实现委托属性。

class User(val map: Map<String, Any?>) {
    val name: String by map
    val age: Int     by map
}
// 在这个例子中,构造函数接受一个映射参数
val user = User(mapOf(
    "name" to "John Doe",
    "age"  to 25
))
委托属性会从这个映射中取值(通过字符串键——属性的名称)
println(user.name) // Prints "John Doe"
println(user.age)  // Prints 25
这也适用于var属性,如果把只读的Map换成MutableMap的话
class MutableUser(val map: MutableMap<String, Any?>) {
    var name: String by map
    var age: Int     by map
}

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Kotlin学习教程(五)

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Kotlin学习教程(五)

泛型classDataT(vart:T)interfaceDataTfunTlogic(t:T){}定义:classTypedClassT(parameter:T){valvalue:T=parameter}这个类现在可
Kotlin学习教程(五)
2024-11-04

Kotlin学习教程(二)

上一篇文章介绍了Kotlin的基本语法,我感觉在继续学习更多知识之前有必要单独介绍以下编码规范。不管学什么东西,开始形成的习惯
Kotlin学习教程(二)
2024-11-04

Kotlin学习教程(四)

数据类:使用data class定义数据类是一种非常强大的类。在Kotlin学习教程(一)中最开始的用的简洁的示例代码就是一个数据类。这里
Kotlin学习教程(四)
2024-11-04

Kotlin学习教程(三)

前面介绍了基本语法和编码规范后,接下来学习下基本类型。在Kotlin中,所有东西都是对象,在这个意义上讲我们可以在任何变量上调
Kotlin学习教程(三)
2024-11-04

Kotlin学习教程(一)

在5月18日谷歌在I/O开发者大会上宣布,将Kotlin语言作为安卓开发的一级编程语言。并且会在Android Studio 3.0版本全面支持Kotlin
Kotlin学习教程(一)
2024-11-04

kotlin 官方学习教程之基础语法详解

kotlin 官方学习教程之基础语法详解Google 在今天的举行了 I/O 大会,大会主要主要展示内有容 Android O(Android 8.0)系统、Google Assistant 语音助手、Google 智能音箱、人工智能、机器
2023-05-31

学习Oracle的历程 (五)

今天,我们接着学习Oracle数据库,今天要学的是PL/SQL 中的单行函数 与 分析函数以及集合运算. 注意,今天的知识小喵个人觉得还是挺难的同事也挺重要的,要认真学习哦! 一 . 单行函数     首先我们来说一下单行函数分为哪些呢?        单行函
学习Oracle的历程 (五)
2021-01-27

socket.io学习教程之深入学习篇(三)

前言 socket.io提供了基于事件的实时双向通讯,本文深入的介绍了socket.io,下面来看看详细的内容吧。 静态文件socket.io默认情况下会通过socket.io-client包提供socket.io.min.js和socke
2022-06-04

Kotlin基础学习之位运算

什么是位运算?程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算说穿了,就是直接对整数在内存中的二进制位进行操作。比如,and运算本来是一个逻辑运算符,但整数与整数之间也可以进行and运算。举个例子,6的二进制是110,11的二进
2023-05-30

Python学习教程(Python学习路线):Python——SciPy精讲

Python学习教程(Python学习路线):Python——SciPy精讲SciPy 是 Python 里处理科学计算 (scientific computing) 的包,使用它遇到问题可访问它的官网 (https://www.scipy
2023-06-02

Python装饰器入门学习教程(九步学习)

装饰器(decorator)是一种高级Python语法。装饰器可以对一个函数、方法或者类进行加工。在Python中,我们有多种方法对函数和类进行加工,比如在Python闭包中,我们见到函数对象作为某一个函数的返回结果。相对于其它方式,装饰器
2022-06-04

Oracle入门学习五

学习视频: https://www.bilibili.com/video/BV1tJ411r7EC?p=35数据的完整性:保证插入表格的数据必须正确。包括实体完整性、区域完整性、引用完整性、自定义完整性。约束:约束的作用是为了保证数据的完整性,类型包括主键约束
Oracle入门学习五
2022-04-01

MySQL 学习笔记(五)

mysqldump 与 --set-gtid-purged 设置(1)  mysqldumpThe mysqldump client utility performs logical backups, producing a set of SQL statem
MySQL 学习笔记(五)
2022-01-27

研究学习Kotlin的方法有哪些

这篇文章将为大家详细讲解有关研究学习Kotlin的方法有哪些,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。Kotlin是一门让人感到很舒服的语言,相比Java来说,它更加简洁,省去了琐琐碎碎
2023-06-17

Python学习教程:面向对象学习实力讲解

类的实现class Cat:"""猫科动物类"""tag=我是家猫 def __init__ (self,name,age=0): #没有默认值必须要传,且写在前面self.name=nameself.__age=age #私有变量,外部不
2023-06-02

编程热搜

  • 人工智能你要知道的那些事
    编程学习网:早在1g时代我们只能接打电话。2g时代可以打电话发短信,玩早期的qq,但网络十分不稳定。3g时代带给我们很大的改变就是宽带上网,视频通话,看视频,听歌玩游戏。那时的人们认为4g无用,认为不会有什么改变,但当4g出来时我们才发现这是一次质的飞跃。
    人工智能你要知道的那些事
  • 人工智能无人机管制到底有多难?
    编程学习网:近日,一段“重庆网红列车遭无人机撞击逼停”的视频,在网络热传。
    人工智能无人机管制到底有多难?
  • 人工智能与人类
    欢迎各位阅读本篇,人工智能是计算机科学的一个分支,它企图了解智能的实质,并生产出一种新的能以人类智能相似的方式做出反应的智能机器,该领域的研究包括机器人、语言识别、图像识别、自然语言处理和专家系统等。本篇文章讲述了人工智能与人类,编程学习网教育平台提醒各位:本篇文章纯干货~因此大家一定要认真阅读本篇文章哦!
    人工智能与人类
  • 两小时 Elasticsearch 性能优化,直接把慢查询干团灭了……
    公共集群的机器负载分布不均衡的问题,业务的查询和流量不可控等各种各样的问题,要节省机器资源就一定会面对这种各种各样的问题,除非土豪式做法,每个业务都拥有自己的机器资源,这里面有很多很多颇具技术挑战的事情。
    两小时 Elasticsearch 性能优化,直接把慢查询干团灭了……
  • 关于OpenStack的架构详细讲解
    欢迎各位阅读本篇文章,OpenStack是一个开源的云计算管理平台项目,由几个主要的组件组合起来完成具体工作。本篇文章讲述了关于OpenStack的架构详细讲解,编程学习网教育平台提醒各位:本篇文章纯干货~因此大家一定要认真阅读本篇文章哦!
    关于OpenStack的架构详细讲解
  • AI &神经网络
    欢迎各位阅读本篇,本篇文章讲述了AI &神经网络,人工智能(Artificial Intelligence),英文缩写为AI。它是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门新的技术科学。神经网络内容丰富,反映了当前国内外该领域的最新研究成果和动向,编程学习网教育平台提醒各位:本篇文章纯干货~因此大家一定要认真阅读本篇文章哦!
    AI &神经网络
  • 人工智能对于网络安全的优缺点
    编程学习网:如今,产生的数据比以往任何时候都要多。由于数据分析工具的发展,各行各业的组织都更加重视大数据的收集和存储。
    人工智能对于网络安全的优缺点
  • Bash 初学者系列 7:bash 中的条件语句(if else)
    今天我们介绍一下如何在 bash 中使用条件语句。
    Bash 初学者系列 7:bash 中的条件语句(if else)
  • 人工智能机器学习的重要趋势是什么?
    编程学习网:在竞争日益激烈的技术市场中,从高科技初创公司到全球跨国公司都将人工智能视为关键竞争优势。但是,人工智能行业发展如此之快,以至于很难跟踪最新的研究突破和成就,甚至很难应用科学成果来实现业务成果。
    人工智能机器学习的重要趋势是什么?
  • 人工智能为什么会觉得Matplotlib用起来困难?
    编程学习网:Matplotlib是一个流行的Python库,可以很容易地用于创建数据可视化。
    人工智能为什么会觉得Matplotlib用起来困难?

目录