学习Kotlin~类

news2024/11/25 2:58:46

类的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 = 0
	        get() = (1..6).shuffled().first()
	        set(v) {
	            field = v + 11
	        }
	}

类初始化

主构造函数

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

次构造函数

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

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

	/**
	 * 次构造函数
	 */
	class Player2(
	    _name: String,
	    var age: Int,
	    val isNormal: Boolean
	) {
	
	    var name = _name
	        get() = 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 = _name
	        get() = 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: String
	    fun 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 = 10
	    private val hobby = "music"
	    val subject: String
	
	    init {
	        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() = experience
	    operator 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)//10
	    println(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.coloradmin.cn/o/702876.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

UNITY3D弹幕游戏,万人同屏解决方案_类萌宠宠之战

先上效果 (类萌宠宠之战)弹幕游戏,万人同屏解决方案演示 UNITY默认的人物动画显示方案是 SkinnedMeshRenderer 该动画的计算是由CPU计算(计算骨骼位置所影响的顶点位置) 所以是CPU计算,物体大于2000个时…

Python3,掌握这几种并行处理,轻轻松松提升for循环速度。

并行处理几种方法 1、引言2、并行处理2.1 定义2.2 并行处理优缺点2.3 并行处理的常用库2.4 代码示例2.4.1 multiprocessing2.4.2 concurrent.futures2.4.3 joblib2.4.4 threading 3、总结 1、引言 小屌丝:鱼哥,你给我讲一讲并行处理呗。 小鱼&#xff1…

Android:datePicker对话框的使用

一、前言&#xff1a;这篇文章是关于DatePickerDialog&#xff0c;点击按钮出现一个日期选择器对话框&#xff0c;通过点击确认把选则的日期显示到文本控件上。 二、上代码 页面布局xml <?xml version"1.0" encoding"utf-8"?> <LinearLayout…

正则表达式回溯引发的生产惨案

文章目录 背景问题原因分析如何解决&#xff1f;chatgpt 3.5GP4的表现未完待续 背景 业务上的一个字段在解析时为了避免脏数据导致后续ETL的异常&#xff0c;决定从源头将该字段严格按照设计的规则去匹配。该字段的上传是设备端传上来的文件中的一个字段。 正向&#xff1f;反…

「C/C++」C++类与类的关系(UML类图+代码说明)

✨博客主页&#xff1a;何曾参静谧的博客 &#x1f4cc;文章专栏&#xff1a;「C/C」C/C学习 相关术语 &#x1f3af;依赖关系&#xff08;Dependency&#xff09;&#xff1a;表示一个类的实现需要另一个类的协助&#xff0c;虚线箭头&#xff0c;箭头指向被依赖的类。 &#…

基于当量因子法、InVEST、SolVES模型等多技术融合在生态系统服务功能社会价值评估中的应用及论文写作、拓展分析

生态系统服务是人类从自然界中获得的直接或间接惠益&#xff0c;可分为供给服务、文化服务、调节服务和支持服务4类&#xff0c;对提升人类福祉具有重大意义&#xff0c;且被视为连接社会与生态系统的桥梁。自从启动千年生态系统评估项目&#xff08;Millennium Ecosystem Asse…

2023软科中国大学智能建造专业排名结果出炉(共54所高校)

智能建造专业&#xff08;Intelligent Construction&#xff09;是一个新兴的跨学科领域&#xff0c;它涉及到建筑、土木工程、计算机科学、数据科学等多个学科的知识。智能建造专业专注于研究如何利用先进的计算机技术、人工智能&#xff08;AI&#xff09;、物联网&#xff0…

进销存软件市场成熟,为什么还要用低代码构建?

关键词&#xff1a;进销存软件、群晖NAS、低代码平台 编者按&#xff1a; 进销存管理软件产业已经逐步走向成熟&#xff0c;产品种类也越来越丰富&#xff0c;也正因如此&#xff0c;企业在选择购买进销存软件的时候&#xff0c;往往不知该如何选择。不少的企业会直接选择平台型…

[golang 微服务] 8.go-micro的负载均衡操作,go Web框(Gin,Beego)调用go-micro微服务

一.先创建go-micro服务端 启动consul 需要先启动consul, consol相关内容见 [golang 微服务] 5. 微服务服务发现介绍,安装以及consul的使用,Consul集群 [golang 微服务] 6. GRPC微服务集群Consul集群grpc-consul-resolver案例演示 启动consul命令,这里,使用dev模式: consul agen…

手机上怎么压缩视频?教你几招手机压缩视频小技巧

压缩视频是一种有益的技术&#xff0c;可以帮助人们在存储、传输和观看视频时更有效率和便捷。尤其是在现今数字化信息时代&#xff0c;视频已经成为人们日常生活中不可或缺的一部分&#xff0c;因此更需要使用视频压缩技术来更好地管理和使用这些视频文件。下面给大家分享几种…

Yolov8优化: 多分支卷积模块RFB,扩大感受野提升小目标检测精度

1.RFB-Net介绍 论文&#xff1a;https://arxiv.org/pdf/1711.07767.pdf 代码&#xff1a;GitHub - GOATmessi7/RFBNet: Receptive Field Block Net for Accurate and Fast Object Detection, ECCV 2018 受启发于人类视觉的Receptive Fields结构&#xff0c;本文提出RFB&#xf…

3.1 C++纯虚函数

C 纯虚函数 C的纯虚函数是一种特殊的虚函数&#xff0c;没有函数体&#xff0c;只有函数原型。 纯虚函数语法格式为&#xff1a; 等号后面的 0 表示该函数为纯虚函数。 纯虚函数在抽象类中定义&#xff0c;抽象类是指包含至少一个纯虚函数的类&#xff0c;不能被实例化。 …

bim在建筑工程中的应用有哪些?

BIM以其在协同设计、冲突检测、可视化沟通、项目控制和可持续性设计等方面的应用&#xff0c;提高了建筑工程的效率、质量和可持续性&#xff0c;为各利益相关者带来了诸多益处。它已逐渐成为现代建筑工程不可或缺的重要工具和方法。BIM在建筑工程的作用越来越明显。 ​  BIM…

HOT29-删除链表的倒数第 N 个结点

leetcode原题链接&#xff1a;删除链表的倒数第 N 个结点 题目描述 删除链表的倒数第 N 个结点。 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,…

伦敦金k线图基础知识有多重要?

正所谓万丈高楼平地起&#xff0c;不积跬步无以至千里&#xff0c;在投资市场上也才如此&#xff0c;从来没有投资者能够在伦敦金市场上一蹴而就地取得成功&#xff0c;很多成功的交易者都是一步一个脚印&#xff0c;从最基础知识开始学起&#xff0c;逐渐成为专业的投资者。 看…

新星计划2023【Java基础及数据库Mysql】学习方向报名入口!

新星计划2023【Java基础及数据库Mysql】学习方向报名入口&#xff01; 一、关于本学习方向导师二、关于本学习方向官方微信群三、关于活动时间&奖品&要求四、学习计划五、TOP5评选规则六、活动要求七、注意事项 本赛道是针对那些希望从事Java开发并且想要学习如何与数据…

NVIDIA-Linux-x86_64-535.54.03.run cuda_12.2.0_535.54.03_linux.run下载地址

Official Drivers | NVIDIA Linux x64 (AMD64/EM64T) Display Driver | 535.54.03 | Linux 64-bit | NVIDIA 下载连接 Download NVIDIA, GeForce, Quadro, and Tesla DriversDownload drivers for NVIDIA graphics cards, video cards, GPU accelerators, and for other GeFor…

CAD转换PDF怎么转换?教你几种简单转换方法

CAD&#xff08;计算机辅助设计&#xff09;是一种广泛应用于工程设计和制造领域的软件。虽然CAD可以生成高质量的设计图纸&#xff0c;但是在与其他人共享这些图纸时可能会出现问题。因此&#xff0c;将CAD文件转换成PDF可以在各种设备上打开和查看。此外PDF还可以在不改变文件…

Postman设置断言

目录 前言&#xff1a; 一、断言的定义 二、Postman断言的语法 三、Postman中chai.js断言常用语法 前言&#xff1a; 在进行API测试时&#xff0c;断言是一项重要的功能。它能帮助我们验证接口的响应是否符合预期结果&#xff0c;从而确保API的正确性和可靠性。在Postman中…

Hudi 文件布局(File Layouts)

文章目录 Hudi File Layouts1 核心概念1.1 Base File1.2 Base File1.3 File Slice1.4 File Group 2. File Layouts写过程2.1 COW表2.2 MOR表 Hudi File Layouts 1 核心概念 File Layouts&#xff08;文件布局&#xff09;是指Hudi的数据文件在存储介质上的分布&#xff0c;Hu…