学习Kotlin~类

类的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.hqwc.cn/news/9310.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MQTT协议学习

前言 最近在学习mqtt协议,看的是官方英文版的,写这篇博客就是为了将一些关键内容提取出来,以便日后的查询和复习,有需要的可以参考。官方的文档在这: MQTT Essentials - All Core Concepts explained (hivemq.com) …

时间序列预测 | Matlab基于粒子群算法优化门控循环单元(PSO-GRU)的时间序列预测,PSO-GRU时间序列预测,单列数据集

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 时间序列预测 | Matlab基于粒子群算法优化门控循环单元(PSO-GRU)的时间序列预测,PSO-GRU时间序列预测,单列数据集。 优化参数为学习率,隐藏层节点个数,正则化参数,要求2020b及以上版本&#

Nacos架构与原理 - 通信通道

文章目录 Nacos 长链接⼀、现状背景二、场景分析1. 配置SDK 和 Server 之间Server 之间通信 2. 服务SDK 和 Server 之间Server 之间通信 三、长链接核心诉求1. 功能性诉求客户端服务端 2. 性能要求3. 负载均衡客户端随机服务端柔性调 4. 连接⽣命周期5. 安全性6. 低成本多语⾔实…

NSS [SWPUCTF 2021 新生赛]no_wakeup

NSS [SWPUCTF 2021 新生赛]no_wakeup 先看题目&#xff0c;反序列化&#xff0c;绕过weakup。 exp&#xff1a; <?php class HaHaHa{public $admin;public $passwd;public function __construct(){$this->admin "admin";$this->passwd "wllm";…

HttpRunner接口自动化测试框架详解

目录 简介 框架流程 核心特性 下载安装 入门使用 测试场景 用例设计 运行测试 查看测试报告 HttpRunnerManager 简介 核心特性 下载安装 环境配置 erlang Rabbitmq 总结&#xff1a; 简介 HttpRunner是一款面向 HTTP(S) 协议的通用测试框架&#xff0c;只需编…

JavaScript

目录 一、javaScript介绍 二、JavaScript使用 三、变量 四、关系比较运算 五、逻辑运算 五、数组 六、函数 1.函数的定义方式 2.函数的作用域 3.变量声明提前 4.this关键字 七、自定义对象 1.Object 对象 2.自定义对象 3.匿名函数 比较数据 八、js中的事件 1.事件定义 …

蘑菇车联用城市级落地讲述自动驾驶新故事

作者 | 魏启扬 来源 | 洞见新研社 “如果不能实现自动驾驶&#xff0c;特斯拉将一文不值”。 这是马斯克在接受媒体采访时的公开发言&#xff0c;这句话的语境是&#xff0c;特斯拉是自动驾驶坚实的拥护者&#xff0c;且一直在付诸行动。 可是特斯拉渐进式的单车智能路线&am…

CodeTop整理-数组篇

目录 53. 最大子序和 33. 搜索旋转排序数组 三数之和 121. 买卖股票的最佳时机 4. 寻找两个正序数组的中位数 695. 岛屿的最大面积 54. 螺旋矩阵 88. 合并两个有序数组 152. 乘积最大子数组 42. 接雨水 64. 最小路径和 1. 两数之和 123. 买卖股票的最佳时机 III …

Ubuntu 22.04.2 LTS LTS x86_64 安装 stable-diffusion-webui 【2】基本版本完结。

前篇 Ubuntu 20.04 LTS x86_64 安装 stable-diffusion-webui_hkNaruto的博客-CSDN博客 内容太多&#xff0c;分第二篇继续 中途重装了机器&#xff0c;20.04 &#xff0c;apt upgrade后自动升级到22.04.2 现状&#xff1a;起来了&#xff0c;又没完全起来 启动日志 (stab…

mysqldump + python 定时备份数据库

场景&#xff1a; 需要对mysql进行定时备份&#xff0c;受限于硬盘空间的大小&#xff0c;需要对备份的数据需要定时清理 python代码实现&#xff1a; # -*- coding:UTF-8 -*- """ProjectName : HotelGo2DelonixPmxFileName : fix_missing_ratesDescripti…

基本 SQL 命令 、重要的 SQL命令、SQL 约束 及 SQL语句 的 执行顺序

学习目标&#xff1a; 学习目标如下&#xff1a; SQL语句执行顺序 学习内容&#xff1a; 基本 SQL 命令&#xff1a; FROMONJOINWHEREGROUP BYAGG_FUNCWITHHAVINGSELECT 从数据库中提取数据UNIONDISTINCTORDER BY 排序LIMIT 重要的sql命令&#xff1a; 1、SELECT - 从数据…

Linux:etc/group

etc/group文件中保存着系统中所有组的名称&#xff0c;以及每个组中的成员列表。 文件中的一行为一个组的信息&#xff0c;具体如下&#xff1a; 如果组口令字段为x的话&#xff0c;就还有一个etc/gshadow文件用于存放组口令。 GID用于标识一个组&#xff0c;应保证其唯一性。…