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

Kotlin学习教程(四)

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

北京

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

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

看不清楚,换张图片

免费获取短信验证码

Kotlin学习教程(四)

数据类:使用data class定义

数据类是一种非常强大的类。在Kotlin学习教程(一)中最开始的用的简洁的示例代码就是一个数据类。这里我们再拿过来:

public class Artist {
       private long id;    private String name;    private String url;    private String mbid;    public long getId() {
           return id;    }    public void setId(long id) {
           this.id = id;    }    public String getName() {
           return name;    }    public void setName(String name) {
           this.name = name;    }    public String getUrl() {
           return url;    }    public void setUrl(String url) {
           this.url = url;    }    public String getMbid() {
           return mbid;    }    public void setMbid(String mbid) {
           this.mbid = mbid;    }    @Override public String toString() {
           return "Artist{" +          "id=" + id +          ", name='" + name + '\'' +          ", url='" + url + '\'' +          ", mbid='" + mbid + '\'' +          '}';    }}

使用Kotlin:

data class Artist(
    var id: Long,
    var name: String,
    var url: String,
    var mbid: String)

通过数据类,会自动提供以下函数:

  • 所有属性的get() set()方法

  • equals()

  • hashCode()

  • copy()

  • toString()

  • 一系列可以映射对象到变量中的函数(后面再说)。

如果我们使用不可修改的对象,就像我们之前讲过的,假如我们需要修改这个对象状态,必须要创建一个新的一个或者多个属性被修改的实例。
这个任务是非常重复且不简洁的。

举个例子,如果要修改Person类中charonage:

data class Person(val name: String,
                  val age: Int)
val charon = Person("charon", 18)
val charon2 = charon.copy(age = 19)

如上,我们拷贝了charon对象然后只修改了age的属性而没有修改这个对象的其它状态。

多声明

多声明,也可以理解为变量映射,这就是编译器自动生成的componentN()方法。

var personD = PersonData("PersonData", 20, "male")
var (name, age) = personD


Log.d("test", "name = $name, age = $age")

//输出
name = PersonData, age = 20

上面的多声明,大概可以翻译成这样:

var name = f1.component1()
var age = f1.component2()

继承

Kotlin中所有类都有一个共同的超类Any,这对于没有超类型声明的类是默认超类:

class Person // 从 Any 隐式继承

Any不是java.lang.Object。它除了equals()hashCode()toString()外没有任何成员。
Kotlin中所有的类默认都是不可继承的(final),为什么要这样设计呢?引用Effective Java书中的第17条:要么为继承而设计,并提供文档说明,
要么就禁止继承。所以我们只能继承那些明确声明open或者abstract的类:要声明一个显式的超类型,我们把类型放到类头的冒号之后:

open class Person(num: Int)
// 继承
class SuperPerson(num: Int) : Person(num)

如果该类有一个主构造函数,其基类必须用基类型的主构造函数参数就地初始化。
如果类没有主构造函数,那么每个次构造函数必须使用super关键字初始化其基类型,或委托给另一个构造函数做到这一点。
注意,在这种情况下,不同的次构造函数可以调用基类型的不同的构造函数:

class MyView : View {
    constructor(ctx: Context) : super(ctx)
    constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}

覆盖

方法覆盖

只能重写显示标注可覆盖的方法:

open class Person(num: Int) {
    open fun changeName(name: String) {

    }

    fun changeAge(age: Int) {

    }
}

class SuperPerson(num: Int) : Person(num) {
    override fun changeName(name: String) {
        // 通过super关键字调用超类实现
        super.changeName(name)
    }
}

SuperPerson.changeName()方法前面必须加上override标注,不然编译器将会报错。如果像上面Person.changeAge()方法没有标注open,
则子类中不能定义相同的方法:

class SuperPerson(num: Int) : Person(num) {
    override fun changeName(name: String) {
        super.changeName(name)
    }

    // 编译器报错
    fun changeAge(age: Int) {

    }
    // 重载是可以的
    fun changeAge(name: String) {

    }
    // 重载是可以的
    fun changeAge(age: Int, name: String) {

    }
}

标记为override的成员本身是开放的,也就是说,它可以在子类中覆盖。如果你想禁止再次覆盖,可以使用final关键字:

open class SuperPerson(num: Int) : Person(num) {
    final override fun changeName(name: String) {
        super.changeName(name)
    }
}
属性覆盖

属性覆盖与方法覆盖类似,只能覆盖显示标明open的属性,并且要用override开头:

open class Person(num: Int) {
    open val name: String = ""

    open fun changeName(name: String) {

    }

    fun changeAge(age: Int) {

    }
}

open class SuperPerson(num: Int) : Person(num) {
    override val name: String
        get() = super.name

    final override fun changeName(name: String) {
        super.changeName(name)
    }

}

每个声明的属性可以由具有初始化器的属性或者具有get方法的属性覆盖,你也可以用一个var属性覆盖一个val属性,但反之则不行。

抽象类

类和其中的某些成员可以声明为abstract。抽象成员在本类中可以不用实现。 需要注意的是,我们并不需要用open标注一个抽象类或者函数——因为这不
言而喻。

我们可以用一个抽象成员覆盖一个非抽象的开放成员:

open class base {
    open fun f() {}
}

abstract class Derived : base() {
    override abstract fun f()
}

修饰符

Kotlin中修饰符是与Java中的有些不同。在kotlin中默认的修饰符是public,这节约了很多的时间和字符。

  • private
    private修饰符是最限制的修饰符,和Javaprivate一样。它表示它只能被自己所在的文件可见。所以如果我们给一个类声明为private
    我们就不能在定义这个类之外的文件中使用它。
    另一方面,如果我们在一个类里面使用了private修饰符,那访问权限就被限制在这个类里面了。甚至是继承这个类的子类也不能使用它。

  • protected.
    Java一样,它可以被成员自己和继承它的成员可见。

  • internal
    如果是一个定义为internal的包成员的话,对所在的整个module可见。如果它是一个其它领域的成员,它就需要依赖那个领域的可见性了。
    比如如果写了一个private类,那么它的internal修饰的函数的可见性就会限制与它所在的这个类的可见性。

  • public.
    你应该可以才想到,这是最没有限制的修饰符。这是默认的修饰符,成员在任何地方被修饰为public,很明显它只限制于它的领域。

数组

数组用类Array实现,并且还有一个size属性及getset方法,由于使用[]重载了getset方法,所以我们可以通过下标很方便的获取或者
设置数组对应位置的值。
Kotlin标准库提供了arrayOf()创建数组和xxArrayOf创建特定类型数组

val array = arrayOf(1, 2, 3)
val countries = arrayOf("UK", "Germany", "Italy")
val numbers = intArrayOf(10, 20, 30)
val array1 = Array(10, { k -> k * k })
val longArray = emptyArray<Long>()
val studentArray = Array<Student>(2)
studentArray[0] = Student("james")

Java不一样的是Kotlin的数组是容器类,提供了ByteArray,CharArray,ShortArray,IntArray,LongArray,BooleanArray,
FloatArrayDoubleArray

集合

KotlinList<out T>类型是一个提供只读操作如sizeget等的接口。和Java类似,它继承自Collection<T>进而继承自Iterable<T>
改变list的方法是由MutableList<T>加入的。这一模式同样适用于Set<out T>/MutableSet<T>Map<K, out V>/MutableMap<K, V>

Kotlin没有专门的语法结构创建listset。要用标准库的方法如listOf()mutableListOf()setOf()mutableSetOf()
创建map可以用mapOf(a to b, c to d)

fun main(args : Array<String>) {
    var lists = listOf("a", "b", "c")
    for(list in lists) {
        println(list)
    }
}
fun main(args : Array<String>) {
    var map = TreeMap<String, String>()
    map["0"] = "0 haha"
    map["1"] = "1 haha"
    map["2"] = "2 haha"

    println(map["1"])
}
val numbers: MutableList<Int> = mutableListOf(1, 2, 3)
val readOnlyView: List<Int> = numbers
println(numbers)        // 输出 "[1, 2, 3]"
numbers.add(4)
println(readOnlyView)   // 输出 "[1, 2, 3, 4]"
readOnlyView.clear()    // -> 不能编译

val strings = hashSetOf("a", "b", "c", "c")
assert(strings.size == 3)

null类型

因为在Kotlin中一切都是对象,一切都是可null的。当某个变量的值可以为null的时候,必须在声明处的类型后添加?来标识该引用可为空。
Kotlin通过?将是否允许为空分割开来,比如str:String为不能空,加上?后的str:String?为允许空,通过这种方式,将本是不能确定的变
量人为的加入了限制条件。而不符合条件的输入,则会在IDE上显示编译错误而无法执行。

var value1: String
value1 = null        // 编译错误 Null can not be a value of a non-null type String

var value2 : String? 
value2 = null       // 编译通过

在对变量进行操作时,如果变量是可能为空的,那么将不能直接调用,因为编译器不知道你的变量是否为空,所以编译器就要求你一定要对变量进行判断

var str : String? = null
// 编译错误 Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?
str.length    
// 编译能通过,这表示如果str不为空的时候执行length方法
str?.length

那么问题来了,我们知道在javaString.length返回的是int,上面的str?.length既然编译通过了,那么它返回了什么?我们可以这么写:

var result = str?.length

这么写编译器是能通过的,那么result的类型是什么呢?在Kotlin中,编译器会自动根据结果判断变量的类型,翻译成普通代码如下:

if(str == null)
    result = null;            // 这里result为一个引用类型
else
    result = str.length;    // 这里result为Int

那么如果我们需要的就是一个Int的结果(事实上大部分情况都是如此),那又该怎么办呢?在kotlin中除了?表示可为空以外,还有一个新的符号:
感叹号!!,表示一定不能为空。所以上面的例子,如果要对result进行操作,可以这么写:

var str : String? = null
var result : Int = str!!.length

这样的话,就能保证result的数据类型,但是这样还有一个问题,那就是str的定义是可为空的,上面的代码中,str就是空,这时候下面的操作虽然
不会报编译异常,但是运行时就会见到我们熟悉的空指针异常NullPointerExectpion,这显然不是我们希望见到的,也不是kotlin愿意见到的。
java中的三元操作符大家应该都很熟悉了,kotlin中也有类似的,它很好的解决了刚刚说到的问题。在kotlin中,三元操作符是?:,写起来也
java要方便一些。

var str : String? = null
var result = str?.length ?: -1
//等价于
var result : Int = if(str != null) str.length else -1

if null缩写

val data = ……
val email = data["email"] ?: throw IllegalStateException("Email is missing!")

如果?:左侧表达式非空,elvis操作符就返回其左侧表达式,否则返回右侧表达式。
请注意,当且仅当左侧为空时,才会对右侧表达式求值。

!!操作符

我们可以写b!!,这会返回一个非空的b
(例如:在我们例子中的String)或者如果b为空,就会抛出一个空指针异常:

val l = b!!.length

因此,如果你想要一个 NPE,你可以得到它,但是你必须显式要求它,否则它不会不期而至。

安全的类型转换

如果对象不是目标类型,那么常规类型转换可能会导致ClassCastException
另一个选择是使用安全的类型转换,如果尝试转换不成功则返回null{: .keyword }:

val aInt: Int? = a as? Int

可空类型的集合

如果你有一个可空类型元素的集合,并且想要过滤非空元素,你可以使用filterNotNull来实现。

val nullableList: List<Int?> = listOf(1, 2, null, 4)
val intList: List<Int> = nullableList.filterNotNull()

表达式

if表达式

Kotlin中,if是一个表达式,即它会返回一个值。因此就不需要三元运算符条件 ? 然后 : 否则,因为普通的if就能胜任这个角色。
if的分支可以是代码块,最后的表达式作为该块的值:

val max = if (a > b) {
    print("Choose a")
    a
} else {
    print("Choose b")
    b
}
when表达式

when表达式与Java中的switch/case类似,但是要强大得多。这个表达式会去试图匹配所有可能的分支直到找到满意的一项。然后它会运行右边的表达
式。
Javaswitch/case不同之处是参数可以是任何类型,并且分支也可以是一个条件。

对于默认的选项,我们可以增加一个else分支,它会在前面没有任何条件匹配时再执行。条件匹配成功后执行的代码也可以是代码块:

when (x){
    1 -> print("x == 1") 
    2 -> print("x == 2") 
    else -> {
        print("I'm a block")
        print("x is neither 1 nor 2")
    }
}

因为它是一个表达式,它也可以返回一个值。我们需要考虑什么时候作为一个表达式使用,它必须要覆盖所有分支的可能性或者实现else分支。否则它不会被
编译成功:

val result = when (x) {
    0, 1 -> "binary"
    else -> "error"
}

如你所见,条件可以是一系列被逗号分割的值。但是它可以更多的匹配方式。比如,我们可以检测参数类型并进行判断:

when(view) {
    is TextView -> view.setText("I'm a TextView")
    is EditText -> toast("EditText value: ${view.getText()}")
    is ViewGroup -> toast("Number of children: ${view.getChildCount()} ")
    else -> view.visibility = View.GONE
}
for循环
val items = listOf("apple", "banana", "kiwi")
for (item in items) {
    println(item)
}

for (i in array.indices)
    print(array[i])

使用类型检测及自动类型转换

is运算符检测一个表达式是否某类型的一个实例。 如果一个不可变的局部变量或属性已经判断出为某类型,那么检测后的分支中可以直接当作该类型使用,
无需显式转换:

fun getStringLength(obj: Any): Int? {
    if (obj !is String) return null

    // `obj` 在这一分支自动转换为 `String`
    return obj.length
}

返回和跳转

Kotlin有三种结构化跳转表达式:

  • return:默认从最直接包围它的函数或者匿名函数返回。

  • break:终止最直接包围它的循环。

  • continue:继续下一次最直接包围它的循环。

Kotlin中任何表达式都可以用标签label来标记。标签的格式为标识符后跟@符号,例如:abc@fooBar@都是有效的标签。

要为一个表达式加标签,我们只要在其前加标签即可。

loop@ for (i in 1..100) {
    for (j in 1..100) {
        if (……) break@loop
    }
}

Ranges

Range表达式使用一个..操作符。表示就是一个该范围内的数据的数组,包含头和尾

var nums = 1..100 
for(num in nums) {
    println(num)
    // 打印出1 2 3 ....100
}
if(i >= 0 && i <= 10) 
    println(i)

转换成

if (i in 0..10) 
    println(i)

Ranges默认会自增长,所以如果像以下的代码:

for (i in 10..0)
    println(i)

它就不会做任何事情。但是你可以使用downTo函数:

for(i in 10 downTo 0)
    println(i)

我们可以在Ranges中使用step来定义一个从1到一个值的不同的空隙:

for (i in 1..4 step 2) println(i)
for (i in 4 downTo 1 step 2) println(i)

Until

上面的Range是包含了头和尾,那如果只想包含头不包含尾呢? 就要用until

var nums = 1 until 100
for(num in nums) {
    println(num)
    // 这样打印出来是1 2 3 .....99
}

免责声明:

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

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

Kotlin学习教程(四)

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

下载Word文档

猜你喜欢

Kotlin学习教程(四)

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

Kotlin学习教程(二)

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

Kotlin学习教程(五)

泛型classDataT(vart:T)interfaceDataTfunTlogic(t:T){}定义:classTypedClassT(parameter:T){valvalue:T=parameter}这个类现在可
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中学习了DML操作,可以我们添加数据、修改数据、删除数据,当我说到这里难免有大部分人觉得有点别扭,感觉缺少了点什么? 没错,作为编程的根本四要素之一,我们怎么只能去修改、添加、删除、而不去查看数据呢? 今天小喵学习了我们的Orac
学习Oracle的历程 (四)
2019-06-08

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

Numpy库的学习(四)

我们今天继续学习一下Numpy库接着前面几次讲的,Numpy中还有一些标准运算a = np.arange(3)print(a)print(np.exp(a))print(np.sqrt(a))exp表示求e的幂次方,比如上面看到的,e的0次
2023-01-31

Oracle入门学习四

上一篇:Oracle入门学习三学习视频:https://www.bilibili.com/video/BV1tJ411r7EC?p=35Oracle表连接:内连接、外连接。外连接分左连接、右连接。多表查询时,如果表之间没有条件关联,则会把所有匹配的结果查找出来
Oracle入门学习四
2017-05-24

NumPy 学习笔记(四)

NumPy 算术函数:  1、numpy.reciprocal(arr) 返回参数逐个元素的倒数  2、numpy.power(one, two) 将第一个输入数组中的元素作为底数,计算它与第二个输入数组中相应元素的幂,即 one^two 
2023-01-31

python 学习第四天

目录 基本运算符 算术运算符 比较运算符 赋值运算符 逻辑运算符 身份运算符 python运算符优先级
2023-01-31

编程热搜

  • 人工智能你要知道的那些事
    编程学习网:早在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用起来困难?

目录