网易首页 > 网易号 > 正文 申请入驻

开心档-软件开发入门之Kotlin 类和对象

0
分享至

前言

目录

Kotlin 类和对象

类定义

类的属性

属性定义

getter 和 setter

实例

主构造器

实例

次构造函数

实例

抽象类

嵌套类

内部类

匿名内部类

类的修饰符

Kotlin 类和对象

类定义

Kotlin 类可以包含:构造函数和初始化代码块、函数、属性、内部类、对象声明。

Kotlin 中使用关键字 class 声明类,后面紧跟类名:

class Runoob { // 类名为 Runoob // 大括号内是类体构成}

我们也可以定义一个空类:

class Empty

可以在类中定义成员函数:

class Runoob() { fun foo() { print("Foo") } // 成员函数}

类的属性

属性定义

类的属性可以用关键字 var 声明为可变的,否则使用只读关键字 val 声明为不可变。

class Runoob { var name: String = …… var url: String = …… var city: String = ……}

我们可以像使用普通函数那样使用构造函数创建类实例:

val site = Runoob() // Kotlin 中没有 new 关键字

要使用一个属性,只要用名称引用它即可

site.name // 使用 . 号来引用site.url

Kotlin 中的类可以有一个 主构造器,以及一个或多个次构造器,主构造器是类头部的一部分,位于类名称之后:

class Person constructor(firstName: String) {}

如果主构造器没有任何注解,也没有任何可见度修饰符,那么constructor关键字可以省略。

class Person(firstName: String) {}

getter 和 setter

属性声明的完整语法:

var

getter 和 setter 都是可选

如果属性类型可以从初始化语句或者类的成员函数中推断出来,那就可以省去类型,val不允许设置setter函数,因为它是只读的。

var allByDefault: Int? // 错误: 需要一个初始化语句, 默认实现了 getter 和 setter 方法var initialized = 1 // 类型为 Int, 默认实现了 getter 和 setterval simple: Int? // 类型为 Int ,默认实现 getter ,但必须在构造函数中初始化val inferredType = 1 // 类型为 Int 类型,默认实现 getter

实例

以下实例定义了一个 Person 类,包含两个可变变量 lastName 和 no,lastName 修改了 getter 方法,no 修改了 setter 方法。

class Person { var lastName: String = "zhang" get() = field.toUpperCase() // 将变量赋值后转换为大写 set var no: Int = 100 get() = field // 后端变量 set(value) { if (value < 10) { // 如果传入的值小于 10 返回该值 field = value } else { field = -1 // 如果传入的值大于等于 10 返回 -1 } } var heiht: Float = 145.4f private set}// 测试fun main(args: Array

) { var person: Person = Person() person.lastName = "wang" println("lastName:${person.lastName}") person.no = 9 println("no:${person.no}") person.no = 20 println("no:${person.no}")}

输出结果为:

lastName:WANGno:9no:-1

Kotlin 中类不能有字段。提供了 Backing Fields(后端变量) 机制,备用字段使用field关键字声明,field 关键词只能用于属性的访问器,如以上实例:

var no: Int = 100 get() = field // 后端变量 set(value) { if (value < 10) { // 如果传入的值小于 10 返回该值 field = value } else { field = -1 // 如果传入的值大于等于 10 返回 -1 } }

非空属性必须在定义的时候初始化,kotlin提供了一种可以延迟初始化的方案,使用 lateinit 关键字描述属性:

public class MyTest { lateinit var subject: TestSubject @SetUp fun setup() { subject = TestSubject() } @Test fun test() { subject.method() // dereference directly }}

主构造器

主构造器中不能包含任何代码,初始化代码可以放在初始化代码段中,初始化代码段使用 init 关键字作为前缀。

class Person constructor(firstName: String) { init { println("FirstName is $firstName") }}

注意:主构造器的参数可以在初始化代码段中使用,也可以在类主体n定义的属性初始化代码中使用。 一种简洁语法,可以通过主构造器来定义属性并初始化属性值(可以是var或val):

class People(val firstName: String, val lastName: String) { //...}

如果构造器有注解,或者有可见度修饰符,这时constructor关键字是必须的,注解和修饰符要放在它之前。

实例

创建一个 Runoob类,并通过构造函数传入网站名:

class Runoob constructor(name: String) { // 类名为 Runoob // 大括号内是类体构成 var url: String = "http://www.kxdang.com/topic/" var country: String = "CN" var siteName = name init { println("初始化网站名: ${name}") } fun printTest() { println("我是类的函数") }}fun main(args: Array

) { val kxdang = Runoob("菜鸟教程") println(kxdang.siteName) println(kxdang.url) println(kxdang.country) kxdang.printTest()}

输出结果为:

初始化网站名: 菜鸟教程菜鸟教程http://www.kxdang.com/topic/CN我是类的函数

次构造函数

类也可以有二级构造函数,需要加前缀 constructor:

class Person { constructor(parent: Person) { parent.children.add(this) }}

如果类有主构造函数,每个次构造函数都要,或直接或间接通过另一个次构造函数代理主构造函数。在同一个类中代理另一个构造函数使用 this 关键字:

class Person(val name: String) { constructor (name: String, age:Int) : this(name) { // 初始化... }}

如果一个非抽象类没有声明构造函数(主构造函数或次构造函数),它会产生一个没有参数的构造函数。构造函数是 public 。如果你不想你的类有公共的构造函数,你就得声明一个空的主构造函数:

class DontCreateMe private constructor () {}


注意:在 JVM 虚拟机中,如果主构造函数的所有参数都有默认值,编译器会生成一个附加的无参的构造函数,这个构造函数会直接使用默认值。这使得 Kotlin 可以更简单的使用像 Jackson 或者 JPA 这样使用无参构造函数来创建类实例的库。

class Customer(val customerName: String = "")

实例

class Runoob constructor(name: String) { // 类名为 Runoob // 大括号内是类体构成 var url: String = "http://www.kxdang.com/topic/" var country: String = "CN" var siteName = name init { println("初始化网站名: ${name}") } // 次构造函数 constructor (name: String, alexa: Int) : this(name) { println("Alexa 排名 $alexa") } fun printTest() { println("我是类的函数") }}fun main(args: Array

) { val kxdang = Runoob("菜鸟教程", 10000) println(kxdang.siteName) println(kxdang.url) println(kxdang.country) kxdang.printTest()}

输出结果为:

初始化网站名: 菜鸟教程Alexa 排名 10000菜鸟教程http://www.kxdang.com/topic/CN我是类的函数

抽象类

抽象是面向对象编程的特征之一,类本身,或类中的部分成员,都可以声明为abstract的。抽象成员在类中不存在具体的实现。

注意:无需对抽象类或抽象成员标注open注解。

open class Base { open fun f() {}}abstract class Derived : Base() { override abstract fun f()}

嵌套类

我们可以把类嵌套在其他类中,看以下实例:

class Outer { // 外部类 private val bar: Int = 1 class Nested { // 嵌套类 fun foo() = 2 }}fun main(args: Array

) { val demo = Outer.Nested().foo() // 调用格式:外部类.嵌套类.嵌套类方法/属性 println(demo) // == 2}

内部类

内部类使用 inner 关键字来表示。

内部类会带有一个对外部类的对象的引用,所以内部类可以访问外部类成员属性和成员函数。

class Outer { private val bar: Int = 1 var v = "成员属性" /**嵌套内部类**/ inner class Inner { fun foo() = bar // 访问外部类成员 fun innerTest() { var o = this@Outer //获取外部类的成员变量 println("内部类可以引用外部类的成员,例如:" + o.v) } }}fun main(args: Array

) { val demo = Outer().Inner().foo() println(demo) // 1 val demo2 = Outer().Inner().innerTest() println(demo2) // 内部类可以引用外部类的成员,例如:成员属性}

为了消除歧义,要访问来自外部作用域的 this,我们使用this@label,其中 @label 是一个 代指 this 来源的标签。

匿名内部类

使用对象表达式来创建匿名内部类:

class Test { var v = "成员属性" fun setInterFace(test: TestInterFace) { test.test() }}/** * 定义接口 */interface TestInterFace { fun test()}fun main(args: Array

) { var test = Test() /** * 采用对象表达式来创建接口对象,即匿名内部类的实例。 */ test.setInterFace(object : TestInterFace { override fun test() { println("对象表达式创建匿名内部类的实例") } })}

类的修饰符

类的修饰符包括 classModifier 和_accessModifier_:


  • classModifier: 类属性修饰符,标示类本身特性。

  • abstract // 抽象类 final // 类不可继承,默认属性enum // 枚举类open // 类可继承,类默认是final的annotation // 注解类

  • accessModifier: 访问权限修饰符

  • private // 仅在同一个文件中可见protected // 同一个文件中或子类可见public // 所有调用的地方都可见internal // 同一个模块中可见

实例

// 文件名:example.ktpackage fooprivate fun foo() {} // 在 example.kt 内可见public var bar: Int = 5 // 该属性随处可见internal val baz = 6 // 相同模块内可见

Kotlin 基本数据类型

下一篇

Kotlin 使用命令行编译

Kotlin 文件以 .kt 为后缀。

包声明

代码文件的开头一般为包的声明:

package com.kxdang.mainimport java.util.*fun test() {}class Runoob {}

kotlin源文件不需要相匹配的目录和包,源文件可以放在任何文件目录。

以上例中 test() 的全名是 com.kxdang.main.test、Runoob 的全名是 com.kxdang.main.Runoob。

如果没有指定包,默认为 default 包。

默认导入

有多个包会默认导入到每个 Kotlin 文件中:

  • kotlin.*
  • kotlin.annotation.*
  • kotlin.collections.*
  • kotlin.comparisons.*
  • kotlin.io.*
  • kotlin.ranges.*
  • kotlin.sequences.*
  • kotlin.text.*

函数定义

函数定义使用关键字 fun,参数格式为:参数 : 类型

fun sum(a: Int, b: Int): Int { // Int 参数,返回值 Intreturn a + b}

表达式作为函数体,返回类型自动推断:

fun sum(a: Int, b: Int) = a + bpublic fun sum(a: Int, b: Int): Int = a + b // public 方法则必须明确写出返回类型

无返回值的函数(类似Java中的void):

fun printSum(a: Int, b: Int): Unit { print(a + b)}// 如果是返回 Unit类型,则可以省略(对于public方法也是这样):public fun printSum(a: Int, b: Int) { print(a + b)}

可变长参数函数

函数的变长参数可以用 vararg 关键字进行标识:

fun vars(vararg v:Int){ for(vt in v){ print(vt) }}// 测试fun main(args: Array

) { vars(1,2,3,4,5) // 输出12345}

lambda(匿名函数)

lambda表达式使用实例:

// 测试fun main(args: Array

) { val sumLambda: (Int, Int) -> Int = {x,y -> x+y} println(sumLambda(1,2)) // 输出 3}

定义常量与变量

可变变量定义:var 关键字

var <标识符> : <类型> = <初始化值>

不可变变量定义:val 关键字,只能赋值一次的变量(类似Java中final修饰的变量)

val <标识符> : <类型> = <初始化值>

常量与变量都可以没有初始化值,但是在引用前必须初始化

编译器支持自动类型判断,即声明时可以不指定类型,由编译器判断。

val a: Int = 1val b = 1 // 系统自动推断变量类型为Intval c: Int // 如果不在声明时初始化则必须提供变量类型c = 1 // 明确赋值var x = 5 // 系统自动推断变量类型为Intx += 1 // 变量可修改

注释

Kotlin 支持单行和多行注释,实例如下:

// 这是一个单行注释/* 这是一个多行的 块注释。 */

与 Java 不同, Kotlin 中的块注释允许嵌套。

字符串模板

$ 表示一个变量名或者变量值

$varName 表示变量值

${varName.fun()} 表示变量的方法返回值:

var a = 1// 模板中的简单名称:val s1 = "a is $a" a = 2// 模板中的任意表达式:val s2 = "${s1.replace("is", "was")}, but now is $a"

NULL检查机制

Kotlin的空安全设计对于声明可为空的参数,在使用时要进行空判断处理,有两种处理方式,字段后加!!像Java一样抛出空异常,另一种字段后加?可不做处理返回值为null或配合 ?: 做空判断处理

//类型后面加?表示可为空var age: String? = "23" //抛出空指针异常val ages = age!!.toInt()//不做处理返回 nullval ages1 = age?.toInt()//age为空返回-1val ages2 = age?.toInt() ?: -1

当一个引用可能为 null 值时, 对应的类型声明必须明确地标记为可为 null。

当 str 中的字符串内容不是一个整数时, 返回 null:

fun parseInt(str: String): Int? { // ...}

以下实例演示如何使用一个返回值可为 null 的函数:

fun parseInt(str: String): Int? { return str.toIntOrNull()}fun printProduct(arg1: String, arg2: String) { val x = parseInt(arg1) val y = parseInt(arg2) // 直接使用 `x * y` 会导致错误, 因为它们可能为 null if (x != null && y != null) { // 在进行过 null 值检查之后, x 和 y 的类型会被自动转换为非 null 变量 println(x * y) } else { println("'$arg1' or '$arg2' is not a number") } }fun main() { printProduct("6", "7") printProduct("a", "7") printProduct("a", "b")}

或者:

fun parseInt(str: String): Int? { return str.toIntOrNull()}fun printProduct(arg1: String, arg2: String) { val x = parseInt(arg1) val y = parseInt(arg2) // ... if (x == null) { println("Wrong number format in arg1: '$arg1'") return } if (y == null) { println("Wrong number format in arg2: '$arg2'") return } // 在进行过 null 值检查之后, x 和 y 的类型会被自动转换为非 null 变量 println(x * y)}fun main() { printProduct("6", "7") printProduct("a", "7") printProduct("99", "b")}

类型检测及自动类型转换

我们可以使用 is 运算符检测一个表达式是否某类型的一个实例(类似于Java中的instanceof关键字)。

fun getStringLength(obj: Any): Int? { if (obj is String) { // 做过类型判断以后,obj会被系统自动转换为String类型 return obj.length } //在这里还有一种方法,与Java中instanceof不同,使用!is // if (obj !is String){ // // XXX // } // 这里的obj仍然是Any类型的引用 return null}

或者

fun getStringLength(obj: Any): Int? { if (obj !is String) return null // 在这个分支中, `obj` 的类型会被自动转换为 `String` return obj.length}

甚至还可以

fun getStringLength(obj: Any): Int? { // 在 `&&` 运算符的右侧, `obj` 的类型会被自动转换为 `String` if (obj is String && obj.length > 0) return obj.length return null}

区间

区间表达式由具有操作符形式 .. 的 rangeTo 函数辅以 in 和 !in 形成。

区间是为任何可比较类型定义的,但对于整型原生类型,它有一个优化的实现。以下是使用区间的一些示例:

for (i in 1..4) print(i) // 输出“1234”for (i in 4..1) print(i) // 什么都不输出if (i in 1..10) { // 等同于 1 <= i && i <= 10 println(i)}// 使用 step 指定步长for (i in 1..4 step 2) print(i) // 输出“13”for (i in 4 downTo 1 step 2) print(i) // 输出“42”// 使用 until 函数排除结束元素for (i in 1 until 10) { // i in [1, 10) 排除了 10 println(i)}

实例测试

fun main(args: Array

) { print("循环输出:") for (i in 1..4) print(i) // 输出“1234” println("\n----------------") print("设置步长:") for (i in 1..4 step 2) print(i) // 输出“13” println("\n----------------") print("使用 downTo:") for (i in 4 downTo 1 step 2) print(i) // 输出“42” println("\n----------------") print("使用 until:") // 使用 until 函数排除结束元素 for (i in 1 until 4) { // i in [1, 4) 排除了 4 print(i) } println("\n----------------")}

输出结果:

循环输出:1234----------------设置步长:13----------------使用 downTo:42----------------使用 until:123----------------

下一篇

Kotlin 基本数据类型

在插入文档时,MongoDB 首先检查固定集合的 size 字段,然后检查 max 字段。

实例

在 test 数据库中创建 kxdang 集合:

> use testswitched to db test> db.createCollection("kxdang"){ "ok" : 1 }>

如果要查看已有集合,可以使用 show collections 或 show tables 命令:

> show collectionskxdangsystem.indexes

下面是带有几个关键参数的 createCollection() 的用法:

创建固定集合 mycol,整个集合空间大小 6142800 B, 文档最大个数为 10000 个。

> db.createCollection("mycol", { capped : true, autoIndexId : true, size : 6142800, max : 10000 } ){ "ok" : 1 }>

在 MongoDB 中,你不需要创建集合。当你插入一些文档时,MongoDB 会自动创建集合。

> db.mycol2.insert({"name" : "菜鸟教程"})> show collectionsmycol2...

MongoDB 覆盖索引查询

下一篇

MongoDB 创建数据库

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
朱丹真实在,胖成这样还不P图,穿得像大姐但在周一围面前没气场

朱丹真实在,胖成这样还不P图,穿得像大姐但在周一围面前没气场

酒盅故事汇
2024-06-01 16:36:46
两场比赛进9球!曼城官方回顾2017年连续大胜利物浦、费耶诺德

两场比赛进9球!曼城官方回顾2017年连续大胜利物浦、费耶诺德

直播吧
2024-06-20 20:25:45
高中生模仿宋徽宗瘦金体,被老师打0分,评语:不要挑战考试底线

高中生模仿宋徽宗瘦金体,被老师打0分,评语:不要挑战考试底线

熙熙说教
2024-06-16 12:08:10
王一博《维和防暴队》香港票房仅3.9万,刘青云《谈判专家》928万

王一博《维和防暴队》香港票房仅3.9万,刘青云《谈判专家》928万

千信齐飞
2024-06-20 17:39:05
女单首位大满贯得主!邓亚萍探班国乒集训,指导女队参赛队员

女单首位大满贯得主!邓亚萍探班国乒集训,指导女队参赛队员

直播吧
2024-06-20 18:16:10
社会正在批量生产文盲和精神病!

社会正在批量生产文盲和精神病!

读史
2024-06-18 07:00:46
明日夏至,有钱没钱:一要躲,二不去,吃三样,忌三事,平安度夏

明日夏至,有钱没钱:一要躲,二不去,吃三样,忌三事,平安度夏

农夫也疯狂
2024-06-20 11:25:11
涉嫌严重违纪违法!江门市蓬江区一局长被查

涉嫌严重违纪违法!江门市蓬江区一局长被查

南方都市报
2024-06-20 20:26:08
失去中国国籍代价有多高?身价千亿依然是待宰的羔羊

失去中国国籍代价有多高?身价千亿依然是待宰的羔羊

枫冷慕诗
2024-06-18 19:09:58
菲军荷枪实弹,为何不敢放一枪?菲律宾记者透露仁爱礁大战细节

菲军荷枪实弹,为何不敢放一枪?菲律宾记者透露仁爱礁大战细节

布衣的呼喊
2024-06-20 12:00:07
不等梅西!迈阿密射手王或易主,23岁射手进28球,距纪录只差1球

不等梅西!迈阿密射手王或易主,23岁射手进28球,距纪录只差1球

球场的看客
2024-06-20 12:37:06
突发王炸消息,两亿股民将见证历史,明天A股就是这样走

突发王炸消息,两亿股民将见证历史,明天A股就是这样走

奔走的股市
2024-06-20 12:25:27
84年粟裕病逝,一位大军区司令员指示:不要对粟裕的遗孀过于热情

84年粟裕病逝,一位大军区司令员指示:不要对粟裕的遗孀过于热情

史源历史专栏
2024-06-20 15:05:02
为什么认为江浙沪一带的女生普遍长得很好看?网友:瞎扯,不同意

为什么认为江浙沪一带的女生普遍长得很好看?网友:瞎扯,不同意

奇特短尾矮袋鼠
2024-06-19 18:18:58
伟大总统泽连斯基——选下权利

伟大总统泽连斯基——选下权利

民间胡扯老哥
2024-04-04 08:58:42
克莱汤普森与勇士和美国男篮彻底决裂,加入他国为其出战世界杯

克莱汤普森与勇士和美国男篮彻底决裂,加入他国为其出战世界杯

阿雄侃篮球
2024-06-19 22:00:36
1983年,跟随薄一波5年的秘书要走,薄老将他推荐到重点部门工作

1983年,跟随薄一波5年的秘书要走,薄老将他推荐到重点部门工作

娱乐的宅急便
2024-06-20 10:37:20
北大硕士赵斌:姜萍连题目都看不懂,点名王润秋,说错愿承担后果

北大硕士赵斌:姜萍连题目都看不懂,点名王润秋,说错愿承担后果

东东趣谈
2024-06-18 17:25:07
6月20日下午,突然曝出一个重大消息!又要来大动作吗?

6月20日下午,突然曝出一个重大消息!又要来大动作吗?

股市皆大事
2024-06-20 11:11:59
豆腐和羊肉是肿瘤的“发物”?错!不想癌细胞恶化,少吃4种食物

豆腐和羊肉是肿瘤的“发物”?错!不想癌细胞恶化,少吃4种食物

39健康网
2024-06-19 20:20:02
2024-06-20 21:00:49
雪奈呐
雪奈呐
有自制力,做事有始有终;学习能力强,愿意不断地接触学习新知识。
151文章数 31关注度
往期回顾 全部

科技要闻

小米SU7流量泼天,富贵却被蔚来接住了

头条要闻

被保险人"呼吸心跳骤停"死亡 保险公司以猝死为由拒赔

头条要闻

被保险人"呼吸心跳骤停"死亡 保险公司以猝死为由拒赔

体育要闻

绿军的真老大,开始备战下赛季了

娱乐要闻

叶舒华参加柯震东生日聚会,五毒俱全

财经要闻

深圳一网红学位房14万/平跌到4万/平

汽车要闻

售价11.79-14.39万元 新一代哈弗H6正式上市

态度原创

旅游
数码
健康
艺术
家居

旅游要闻

铁路儿童票新规 已有超4900万小旅客免费出行

数码要闻

双塔双风扇8热管!超频三泰拉巨风RZ820风冷散热器图赏

晚餐不吃or吃七分饱,哪种更减肥?

艺术要闻

穿越时空的艺术:《马可·波罗》AI沉浸影片探索人类文明

家居要闻

自然开放 实现灵动可变空间

无障碍浏览 进入关怀版