学习Kotlin~类

news/2024/11/15 21:44:56/

类的field

  • 类定义的每一个属性,kotlin都会产生一个filed,一个setter(),一个getter()
  • field用来存储属性数据,不能直接定义,kotlin会封装,保护它里面数据,只暴露给getter和setter使用
  • 只有可变属性才有setter方法
  • 需要控制如何读取属性数据时,可以自定义它们
	class Player {//针对每定义一个属性,都会有一个field,get(),set()var name = "abc "get() = field.capitalize()set(v) {field = v.trim()}//计算属性是通过一个覆盖的get()和set()来计算var rolledValue = 0get() = (1..6).shuffled().first()set(v) {field = v + 11}}

类初始化

主构造函数

  • 主构造函数里,临时变量通常都会以下划线开头名字命名
	/*** 主构造函数里,临时变量,通常都会以下划线开头的名字命名*/class Player(_name: String,_age: Int,_isNormal: Boolean) {var name = _nameget() = field.capitalize()private set(value) {field = value.trim();}var age = _age;var isNormal = _isNormal;}
  • 主构造函数里定义属性,直接用一个变量和类型指定属性
	/*** 在主构造函数里直接定义属性*/class Player1(_name: String,var age: Int,val isNormal: Boolean) {var name = _nameget() = field.capitalize();private set(value) {field = value.trim();}}
  • 主构造函数里定义属性,可以给构造函数参数指定默认值
	/*** 在主构造函数里直接定义属性*/class Player1(_name: String,var age: Int,val isNormal: Boolean = true) {var name = _nameget() = field.capitalize();private set(value) {field = value.trim();}}

次构造函数

  • 可以定义多个次构造函数来配置不同的参数组合

  • 使用次构造函数,定义初始化代码逻辑

	/*** 次构造函数*/class Player2(_name: String,var age: Int,val isNormal: Boolean) {var name = _nameget() = field.capitalize();private set(value) {field = value.trim();}//次构造函数constructor(name: String) :this(name, age = 100, isNormal = false) {this.name = name.toUpperCase();}}

初始化块

  • 初始化块可以设置变量或值,以及有效性检查

  • 初始化块代码会在构造类实例时执行

	/*** 初始化块init,会在构造类实例时执行*/class Player3(_name: String,var age: Int = 20,private val isNormal: Boolean) {var name = _nameget() = field.capitalize();private set(value) {field = value.trim();}init {require(age > 0) { "age must be positive" }require(name.isNotBlank()) { "player must have a name" }//false会执行后面闭包}}
初始化顺序
主构造函数里声明的属性
类级别的属性赋值
init初始化块里的属性赋值和函数调用
次构造函数里属性赋值和函数调用

在这里插入图片描述

延迟初始化
  • 使用lateinit关键字相当于做了一个约定:再用它之前负责初始化
  • 只要无法确认lateinit变量是否完成初始化,可以执行isInitialized检查是否完成了初始化
  • 一般变量必须要初始化,但是使用lateinit以后可以先不用初始化,等到用的时候再去赋值
	/*** 延迟初始化 lateinit关键字相当于做了一个约定:再用它之前负责初始化* 只要无法确认lateinit变量是否完成初始化,可以执行isInitialized检查*/class Player5 {lateinit var equipment: Stringfun ready() {equipment = "sharp knife"}//::操作符 使用变量的引用fun battle() {if (::equipment.isInitialized) println(equipment)}}
惰性初始化
  • 暂时不初始化某个变量,直到首次使用它,这个叫惰性初始化
	/*** 惰性初始化,可以暂时不初始化某个变量,直到首次使用它才初始化*/class Player6(_name: String) {var name = _name//val config =  loadConfig() ;//这种方式config变量直接初始化了val config by lazy { loadConfig() }//这里使用by lazy就是惰性初始化private fun loadConfig(): String {println("loading...")return "xxx"}}fun main() {//创建对象的时候,就会给所有的对象初始化,//但是使用了by lazy以后的变量就可以不初始化,等到调用的时候自动初始化val p = Player6("jack")Thread.sleep(3000)println(p.config)}

继承

  • 类默认都是封闭的,要想让某个类开放继承,必须使用open关键字修饰它
	/*** 类默认是关闭的,要让某个类开放继承,必须使用open关键字修饰它*/open class Product(val name: String) {fun description() = "Product $name"open fun load() = "Nothing..."}

函数重载

  • 父类的函数也要以open关键字修饰,子类才能覆盖它
	//继承class LuxuryProduct(val _name: String) : Product(_name) {/*** 父类的函数也要以open关键字修饰,子类才能覆盖它*/override fun load() = "LuxuryProduct loading ..."fun sale(product: Product) {println(product.description())}}

类型检测

  • 每一个类都会继承一个共同的叫作Any的超类
is运算符
  • kotlin的is运算符是个不错的工具,可以用来检查某个对象的类型
	val p = LuxuryProduct("jack")/*** is运算符可以用来检查某个元素类型*/println(p is LuxuryProduct)println(p is Product)
as运算符
  • as操作符声明,这是一个类型转换

  • 只要能确定any is父类条件检查属实,它就会将any当做子类类型对待,可以不经过as转换

	val p = LuxuryProduct("jack")/*** as操作符,类型转换*/p.sale(p as LuxuryProduct)/*** 智能类型转换*/p.sale(p)//这里不用转,默认是子类

单例对象

object关键字

  • 使用object关键字,可以定义一个只能产生一个实例的类-单例
  • 使用object关键字有三种方式
对象声明
  • 对象声明有利于组织代码和管理状态,尤其是管理整个应用运行生命周期内某些一致性状态
	/*** object关键字 对象声明 这是一个单例对象*/object ApplicationConfig {//第一次创建时候执行init {println("loading config...")}fun setSomething() {println("setSomething")}}fun main() {ApplicationConfig.setSomething()println(ApplicationConfig)println(ApplicationConfig)}
对象表达式
  • 可以使用object声明某个类的子类实例对象,不用在重新写一个新类;创建对象时候,对象的类名也省略了
	open class Player {open fun load() = "loading nothing."}fun main() {/*** object关键字,声明一个匿名实例对象,也是单例* 这个对象,是 Player的子类对象,子类无需定义一个名字*/val p = object : Player() {override fun load() = "anonymous class load..."}println(p.load())}
伴生对象
  • 一个类里只能有一个伴生对象
  • 想将某个对象的初始化和一个类实例捆绑在一起,可以考虑伴生对象,使用companion修饰符
	import java.io.File/*** object 关键字 伴生对象* 使用companion修饰,一个类只能有一个伴生对象*/open class ConfigMap {companion object {/*** 只有初始化ConfigMap类或调用load函数时,伴生对象的内容才会载入。* 而且无论实例化ConfigMap类多少次,这个伴生对象始终只有一个实例存在。*/private const val PATH = "xxx"fun load() = File(PATH).readBytes()}}fun main() {println(ConfigMap.load())}

运算符重载

  • 要将内置运算符应用在自定义类身上,必须重写运算符函数,告诉编译器如何操作自定义类
操作符函数名作用
+plus把一个对象添加到另一个对象里
+=plusAssign把一个对象添加到另一个对象里,然后将结果赋值给第一个对象
==equals两个对象相等则返回true,否则false
>compareTo左边对象大于右边对象返回true,否则返回false
[]get返回集合中指定位置的元素
rangeTo创建一个range对象
incontains如果对象包含在集合里,则返回true
	class Coordinate(var x: Int, var y: Int) {//    operator fun plus(c: Coordinate): Coordinate {//        return Coordinate(this.x + c.x, this.y + c.y);//    }operator fun plusAssign(c: Coordinate) {this.x = this.x + c.x;this.y = this.y + c.y;}}fun main() {var a = Coordinate(10, 20);var c = Coordinate(1, 2);//    println(c + a)a += c}

嵌套类

  • 如果一个类对另一个类有用,那么将其嵌入到该类中,并保持在一起是合乎逻辑的
	/*** 嵌套类*/class Player3() {class Equipment(val name: String) {fun show() = println("equipment $name");}fun battle() {Equipment("AK7").show();}}fun main() {Player3().battle();}

数据类

数据类的对象

  • 数据类是专门用来设计存储数据的类
  • 数据类提供了toString的个性化实现
  • ==符号默认情况下,比较对象就是比较它们的引用值,数据类提供了equals和hashCode个性化实现
	/*** 数据类, 专门用来存储数据的类* 数据类提供了toString的个性化实现* ==符号默认情况下,比较对象就是比较它们的引用,* 数据类提供了equals和hashCode的个性化实现*/data class Coordinate(var x: Int, var y: Int) {//坐标值是否是正值val isInBounds = x >= 0 && y >= 0}fun main() {//重写了toString方法println(Coordinate(1, 5))//Coordinate(x=1, y=5)//本身重写了equals和hashCode所以两个对象相等println(Coordinate(1, 5) == Coordinate(1, 5))//true}

数据类的copy方法

  • 使用数据类的copy方法默认的是主构造函数,复制一个对象
	/*** 数据类提供了一个copy函数可以用来方便的复制对象*/data class Student(var name: String, var age: Int) {var score = 10private val hobby = "music"val subject: Stringinit {println("initializing student")subject = "math"}constructor(_name: String) : this(_name, 10) {score = 20}override fun toString(): String {return "Student(name='$name', age=$age, score=$score, hobby='$hobby', subject='$subject')"}}fun main() {val s = Student("Jack")println(s)val copy = s.copy("Rose")//这里复制对象默认使用的主构造函数println(copy)}

数据类的解构声明

  • 结构声明的后台实现就是声明component1、component2等若干个组件函数,让每个函数负责管理你想返回的一个属性数据,类似这样
	public final int component1() {return this.x;}public final int component2() {return this.y;}
	/*** 结构声明的后台实现就是声明component1,component2等若干个组件函数,让每个函数* 负责管理你想返回的一个属性数据*/class PlayerScore(val experience: Int, val level: Int) {operator fun component1() = experienceoperator fun component2() = level;}fun main() {val (x, y) = PlayerScore(10, 5)println(x)println(y)}
  • 如果定义一个数据类,它会自动为定义在主构造函数的属性添加对应的组件函数
	data class Coordinate(var x: Int, var y: Int) {//坐标值是否是正值val isInBounds = x >= 0 && y >= 0}fun main() {/*** 数据类天生支持解构语法,数据类默认会生成组件函数component1*/val (x, y) = Coordinate(10, 5)println(x)//10println(y)//5}

数据类的使用条件

  • 经常需要比较、复制、打印自身内容的类,数据类适合它们;

  • 数据类使用有以下三个条件

    • 数据类必须有至少带一个参数的主构造函数
    • 数据类主构造函数的参数必须是val或var
    • 数据类不能使用abstract、open、sealed和inner修饰符

枚举类

  • 用来定义常量集合的一种特殊类
	enum class Direction {EAST,WEST,SOUTH,NORTH}
  • 枚举类也可以定义函数
	/*** 枚举类也可以定义函数*/enum class Direction2(private val coordinate: Coordinate) {//枚举类构造函数传入对象,那么每个枚举类的对象也要传入构造对象EAST(Coordinate(5, -1)),WEST(Coordinate(1, 0)),SOUTH(Coordinate(0, 1)),NORTH(Coordinate(-1, 0));fun updateCoordinate(p: Coordinate) = Coordinate(p.x + coordinate.x, p.y + coordinate.y)}class Coordinate(val x: Int, val y: Int) {override fun toString(): String {return "Coordinate(x=$x, y=$y)"}}fun main() {println(Direction.EAST)//调用函数时,使用的是枚举常量,所以这样调用println(Direction2.EAST.updateCoordinate(Coordinate(1, 2)))}

下面使用枚举类实现一个驾照类和司机类;

	/*** 代数数据类型*/enum class LicenseStatus {UNQUALIFIED,//没资格LEARNING,//正在学QUALIFIED;//有驾照//驾驶证的id,这里的话只有有驾照才有其他的都不可能有// var licenseId: String? = null;}class Driver(var status: LicenseStatus) {fun checkLicense(): String {return when (status) {LicenseStatus.UNQUALIFIED -> "没资格"LicenseStatus.LEARNING -> "在学"LicenseStatus.QUALIFIED -> "有资格"}}}

密封类

  • 枚举类和密封类都是代数数据类型(ADT);

在上面的枚举类中不可能正常带有驾照的id,为了实现这种需求,我们使用了密封类

  • 密封类可以有若干个子类,若要继承密封类,这些子类必须和它定义在同一个文件里;
	/*** 密封类* 可以有若干个子类,要继承密封类,这些子类必须和它定义在同一个文件里*/sealed class LicenseStatus {//这种情况使用object单例,因为没有属性状态object UnQualified : LicenseStatus2()object Learning : LicenseStatus2()//有属性状态,所以使用类class Qualified(val licenseId: String) : LicenseStatus2()}class Driver(var status: LicenseStatus2) {fun checkLicense(): String {//编译器会自动检测是否有遗漏return when (status) {LicenseStatus2.UnQualified -> "没资格"LicenseStatus2.Learning -> "在学"is LicenseStatus2.Qualified ->"有资格,驾驶证编号:" + "${(this.status as LicenseStatus2.Qualified).licenseId}"}}}

使用了密封类,就可以正常展示驾照的Id了;


http://www.ppmy.cn/news/667142.html

相关文章

Web 应用程序综合监控

综合监控是什么意思 模拟用户通过 Web 应用程序的旅程并对其进行监控以检测任何增加延迟的元素的过程被广泛称为综合监控或综合测试。 为什么需要综合监测 为了确保最终用户的无缝体验,综合性能监控势在必行。监视综合事务以帮助您了解用户如何与 Web 应用程序交…

暴风集团实际控制人冯鑫因涉嫌犯罪被公安机关采取强制措施

【TechWeb】7月28日,暴风集团今日晚间发布公告,称于近日获悉公司实际控制人冯鑫因涉嫌犯罪被公安机关采取强制措施,相关事项尚待公安机关进一步调查。 截至目前,公司经营情况正常。公司管理层将加强管理,确保公司的稳…

追剧补番达人必备五款APP排行

随着WIFI的普及,越来越多的人喜欢在移动端观看视频、追剧补番。但是因为版权的关系,小编常常要辗转N个视频软件才能找到自己想看的剧集,所以也就有了美剧看搜狐、综艺爱奇艺、动漫转B站、弹幕看爆点这样好记的段子。那么,综合比较…

局域网限制网速软件_智能电视本地视频播放软件有哪些?【沙发管家】为你推荐!...

下载高清、超清的视频,然后通过本地播放的方式观影,也已然成为很多对清晰度要求较高的人的一些选择。因为在线看高清视频网速不达标的时候,会经常卡顿,观看体验太差,还不如下载下来好。智能电视的本地视频播放软件有哪…

计算机视觉及智能影像行业深度研究报告

点击上方“小白学视觉”,选择加"星标"或“置顶” 重磅干货,第一时间送达 (温馨提示:文末有下载方式) 1. 计算机视觉领跑 AI 产业,应用场景广阔 1.1 什么是计算机视觉?让机器“看懂”影像的 AI技…

怎么建立局域网_智能电视局域网共享怎么操作?多数品牌都适用的方法!

刚刚入手的智能电视怎么玩出新操作,总不能像有线电视一样墨守成规吧?那不是大材小用么?今天就让小智来带你解锁智能电视实现局域网共享的新操作! 这几个方法理论上适用于绝大多数电视品牌,再也不用担心电视找资源太麻烦…

Android编译调用FFmpeg API,自己写方法,编译so库

作者:谭东 时间:2017年9月19日 环境:Windows 8.1专业版 NDK版本:android-ndk-r14b FFmpeg版本:FFmpeg 3.0.2 “Einstein” 我这里使用的Android Studio为2.3.3版本。 大名鼎鼎的FFmpeg可以说全球出名,…

我们去参加了暴风的风迷节,它和乐视的乐迷节有什么区别?

9月6日,暴风在北京奥雅会展中心举办了第二届“风迷节”开幕仪式,揭开一场将持续到10月6日的品牌活动。近来北京正直入秋,天气爽朗,奥雅外红花与蓝天相映成趣,很有节日氛围。 暴风从2016年开始举办第一届“风迷节”&…