kotlin部分常用特性总结
<h3>Kotlin中类和对象初始化</h3>
<ul>
<li>添加open关键字代表可以被继承</li>
<li>Any 是所有类的父类,类似Object,包含 equals() hashCode() toString()方法</li>
<li>constructor 关键字代表构造函数, constructor关键字可以去掉</li>
</ul>
<pre><code class="java">class User(性格: String, 长相: String, 声音: String) : Human(性格, 长相, 声音)
class User2(name: String) : Human2(name)
open class Human constructor(var 性格: String, var 长相: String, var 声音: String) : Any() {
//对象创建时调用,构造方法的方法体
init {
println("new 了一个${this.javaClass.simpleName}, ta性格:$性格, 长相:$长相, 声音:$声音")
}
override fun toString(): String {
return "${性格} 性格"
}
}
open class Human2 {
val name: String
val age: Int
constructor(name: String) : this(name, 0) {
// 调用主构造函数或另一个辅助构造函数
}
constructor(name: String, age: Int = 18) { // 辅助构造函数
this.name = name
this.age = age
val user: User = User("温柔", "甜美", "动人")
val value: String? = getName()
println(value!!.length)
}
}
</code></pre>
<h3>空类型和智能类型转换</h3>
<ul>
<li>?.在遇到null时会静默跳过执行(返回null)</li>
<li>!!在遇到null时会立即抛出异常 (当对可空变量使用!!时,编译器会假定该变量不为null)</li>
</ul>
<pre><code>fun getName(): String? {// ? 表示返回值可以为空
return "na"//null
}
fun main(args: Array<String>) {
val str: String? = null
// ?.在遇到null时会静默跳过执行(返回null)
// !!在遇到null时会立即抛出异常 (当对可空变量使用!!时,编译器会假定该变量不为null)
//val length = str!!.length
// 应尽量避免使用!!,推荐替代方案包括:
// 优先使用val声明不可变变量
// 对延迟初始化变量使用 lateinit. var用lateinit 延迟初始化,val用lazy
lateinit var q: String
// 使用Elvis操作符?:提供默认值或者return
//Elvis操作符?: 结构:表达式A ?: 表达式B
//当表达式A非null时返回A的值,否则返回表达式B的结果
// 例如:
val length2 = str?.length ?: 0
val name: String = getName() ?: return
println(name.length)
main2(args)
}
</code></pre>
<h3>区间 Range,表示范围</h3>
<pre><code>//val:用于运行时常量,相当于Java的final字段,支持任何类型且仅初始化一次
//const:用于编译时常量,相当于Java的public static final字段,可通过类名直接访问。
//编译时确定值:const修饰的常量值必须在编译时确定,不能通过运行时计算或动态赋值。
//类型限制:仅支持基本类型(如int、double、string)和字符串。
//存储位置:必须声明在顶层(如包级可见变量)或伴生对象(companion object)中
val range: IntRange = 0..1024 // [0, 1024]
val range_exclusive: IntRange = 0 until 1024 // [0, 1024) = [0, 1023]
val a = (50 in range) // (i in range)判断i是否在区间中
</code></pre>
<h3>Unit</h3>
在 Kotlin 中,Unit 是一个特殊的类型,类似于 Java 中的 void,但有以下关键区别:
<ul>
<li>Unit 是一个实际存在的类型(单例对象),而 void 只是关键字</li>
<li>当函数不返回有意义的值时,Kotlin 会隐式返回 Unit</li>
<li>Unit 的实例可以用 Unit 或 () 表示</li>
</ul>
<pre><code>fun printMessage(msg: String): Unit { // 这里的 : Unit 可以省略
println(msg)
// 隐式返回 Unit
}
fun printUsage() {
println("请传入两个整型参数,例如 1 2") // (Any?) -> Unit
} // ()->Unit
val sum = { arg1: Int, arg2: Int ->
println("$arg1 + $arg2 = ${arg1 + arg2}")
arg1 + arg2
}
// (Int, Int) -> Int
val printlnHello = {
println(::printUsage is ()-> Unit)
println("Hello")
}
// ()-> Unit
val int2Long = fun(x: Int): Long {
return x.toLong()
}
</code></pre>
<h3>继承与实现</h3>
<ul>
<li>父类需要 open 才可以被继承</li>
<li>父类方法、属性需要 open 才可以被覆写</li>
<li>接口、接口方法、抽象类默认为 open</li>
<li>覆写父类(接口)成员需要 override 关键字</li>
<li>class D: A(),B,C</li>
<li>注意继承类时实际上调用了父类构造方法</li>
<li>类只能单继承,接口可以多实现</li>
</ul>
<h3>继承之接口代理(基于 by 关键字)</h3>
通过 by 将接口实现委托给其他对象,无需手动编写代理类
<pre><code>class SeniorManager(val driver: Driver, val writer: Writer): Driver by driver, Writer by writer
class CarDriver: Driver {
override fun drive() {
println("开车呢")
}
}
class PPTWriter: Writer {
override fun write() {
println("做PPT呢")
}
}
interface Driver{
fun drive()
}
interface Writer{
fun write()
}
fun main3(args: Array<String>) {
val driver = CarDriver()
val writer = PPTWriter()
val seniorManager = SeniorManager(driver, writer)
seniorManager.drive()
seniorManager.write()
}
</code></pre>
<h3>接口方法冲突</h3>
<ul>
<li>接口方法可以有默认实现</li>
<li>签名一致且返回值相同的冲突</li>
<li>子类(实现类)必须覆写冲突方法</li>
<li>super<[父类(接口)名]>.<a href="[参数列表]">方法名</a></li>
</ul>
<pre><code>abstract class A{
open fun x(): Int = 5
}
interface B{
fun x(): Int = 1
}
interface C{
fun x(): Int = 0
}
class D(var y: Int = 0): A(), B, C{
override fun x(): Int {
println("call x(): Int in D")
if(y > 0){
return y
}else if(y < -200){
return super<C>.x()
}else if(y < -100){
return super<B>.x()
}else{
return super<A>.x()
}
}
}
//3 5 1 0
fun main(args: Array<String>) {
println(D(3).x())
println(D(-10).x())
println(D(-110).x())
println(D(-10000).x())
}
</code></pre>
<h3>可见性对比</h3>
模块通常指一组共同编译的 Kotlin 文件(如 Gradle 模块或 IntelliJ 模块)
<table>
<thead>
<tr>
<th align="center">kotlin</th>
<th align="center">java</th>
</tr>
</thead>
<tbody>
<tr>
<td align="center">private</td>
<td align="center">private</td>
</tr>
<tr>
<td align="center">protected</td>
<td align="center">protected</td>
</tr>
<tr>
<td align="center">-</td>
<td align="center">default(包内可见)</td>
</tr>
<tr>
<td align="center">internal(模块内可见,模块化开发的核心修饰符)</td>
<td align="center">-</td>
</tr>
<tr>
<td align="center">public</td>
<td align="center">public</td>
</tr>
</tbody>
</table>
<h3>Object 关键字</h3>
object 关键字用于实现单例模式、伴生对象和对象表达式(匿名对象),是 Kotlin 特有的简化设计模式的语法糖。以下是其三大核心用途及示例:
1. 单例模式(对象声明)
直接通过 object 声明线程安全的单例:
<pre><code>object DatabaseManager {
private val connection = "DB_Connection"
fun query(sql: String) = println("Executing: $sql via $connection")
}
// 使用(全局唯一实例)
fun main() {
DatabaseManager.query("SELECT * FROM users") // 输出: Executing: SELECT * FROM users via DB_Connection
}
</code></pre>
特点:
* 只有一个实例的类
* 首次访问时延迟初始化(线程安全)
* 不能自定义构造函数, 可继承父类 可实现接口
<ol>
<li>伴生对象
<ul>
<li>每个类可以对应一个伴生对象</li>
<li>伴生对象的成员全局独一份 , 相当于静态方法 静态属性</li>
</ul></li>
</ol>
<pre><code>class Latitude private constructor(val value: Double){
companion object{
@JvmStatic
fun ofDouble(double: Double): Latitude{
return Latitude(double)
}
fun ofLatitude(latitude: Latitude): Latitude{
return Latitude(latitude.value)
}
@JvmField
val TAG: String = "Latitude"
}
}
</code></pre>
<ol>
<li>匿名对象
<ul>
<li>每次调用都会创建新对象</li>
<li>可同时继承类和实现接口(如 object : ClassA(), InterfaceB</li>
<li>注意:object 与 class 不同,不能实例化(本身就是实例)</li>
</ul></li>
</ol>
<pre><code> interface ClickListener {
fun onClick()
}
fun setClickListener(listener: ClickListener) {
listener.onClick()
}
fun main() {
var counter = 0
setClickListener(object : ClickListener {
override fun onClick() {
println("Clicked ${++counter} times") // 捕获并修改外部变量
}
})
}
</code></pre>
<h3>扩展成员</h3>
<ul>
<li>为现有类添加方法、属性</li>
<li>fun X.y(): Z {... }</li>
<li>val x.m 注意扩展属性不能初始化,类似接口属性</li>
<li>Java 调用扩展成员类似调用静态方法</li>
</ul>
<pre><code> operator fun String.times(int: Int): String{
val stringBuilder = StringBuilder()
for(i in 0 until int){
stringBuilder.append(this)
}
return stringBuilder.toString()
}
val String.a: String
get() = "abc"
var String.b: Int
set(value) {
}
get() = 5
//abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc
//5
fun main(args: Array<String>) {
println("abc" * 16)
"abc".b = 5
println("abc".b)
}
</code></pre>
<h3>operator关键字</h3>
operator 关键字用于重载运算符或约定特定函数,让类实例支持类似基础类型的操作(如 +、[]、in 等)
<h4>运算符重载</h4>
通过重载预定义的运算符函数,使对象支持算术、比较等操作
<pre><code>data class Point(val x: Int, val y: Int) {
// 重载 "+" 运算符
operator fun plus(other: Point): Point {
return Point(x + other.x, y + other.y)
}
}
fun main() {
val p1 = Point(1, 2)
val p2 = Point(3, 4)
println(p1 + p2) // 输出: Point(x=4, y=6)
}
</code></pre>
<table>
<thead>
<tr>
<th>运算符</th>
<th>对应函数名</th>
<th>示例表达式</th>
</tr>
</thead>
<tbody>
<tr>
<td>+</td>
<td>plus</td>
<td>a + b</td>
</tr>
<tr>
<td>-</td>
<td>minus</td>
<td>a - b</td>
</tr>
<tr>
<td>[]</td>
<td>get</td>
<td>a[i]</td>
</tr>
<tr>
<td>in</td>
<td>contains</td>
<td>a in b</td>
</tr>
<tr>
<td>==</td>
<td>equals</td>
<td>a==b</td>
</tr>
</tbody>
</table>
<h4>约定函数</h4>
operator 还可用于标记特定名称的函数,实现与语言特性的交互:
<ul>
<li>解构声明(Destructuring)</li>
</ul>
<pre><code>class User(val name: String, val age: Int) {
operator fun component1() = name // 解构为 (name, age)
operator fun component2() = age
}
fun main() {
val user = User("Alice", 25)
val (name, age) = user // 解构赋值
println("$name, $age") // 输出: Alice, 25
}
</code></pre>
<ul>
<li>迭代器支持</li>
</ul>
<pre><code>class Counter(val range: IntRange) {
operator fun iterator(): Iterator<Int> = range.iterator()
}
fun main() {
for (i in Counter(1..5)) { // 支持 for-in 循环
print("$i ") // 输出: 1 2 3 4 5
}
}
</code></pre>
<h4>注意事项</h4>
<ul>
<li>不可随意创造运算符:只能重载 Kotlin 预定义的运算符47。</li>
<li>一致性要求:如 plus 不应修改原对象,应返回新实例35。</li>
<li>与扩展函数结合:可为现有类添加运算符支持</li>
</ul>
<pre><code>operator fun Int.times(str: String) = str.repeat(this)
fun main() {
println(3 * "Hi ") // 输出: Hi Hi Hi
}
</code></pre>
<h3>属性代理</h3>
<h4>懒加载 lazy</h4>
<ul>
<li>延迟到首次访问时执行</li>
<li>线程安全模式:默认 LazyThreadSafetyMode.SYNCHRONIZED,可选 PUBLICATION(允许多线程初始化)或 NONE(单线程)</li>
</ul>
<pre><code>class Delegates{
val hello by lazy {
"HelloWorld"
}
val hello0 by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
"HelloWorld"
}
val hello2 by X()
var hello3 by X()
}
</code></pre>
<h4>基本用法</h4>
<pre><code>// val 属性:只需实现 getValue
// var 属性:需同时实现 getValue 和 setValue
class X{
private var value: String? = null
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
println("getValue: $thisRef -> ${property.name}")
return value?: ""
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String){
println("setValue, $thisRef -> ${property.name} = $value")
this.value = value
}
}
fun main6(args: Array<String>) {
val delegates = Delegates()
println(delegates.hello)
println(delegates.hello2)
println(delegates.hello3)
delegates.hello3 = "value of hello3"
println(delegates.hello3)
println(delegates.hello)
}
//HelloWorld
//getValue: com.spro.globalsearch.Delegates@1593948d -> hello2
//
//getValue: com.spro.globalsearch.Delegates@1593948d -> hello3
//
//setValue, com.spro.globalsearch.Delegates@1593948d -> hello3 = value of hello3
//getValue: com.spro.globalsearch.Delegates@1593948d -> hello3
//value of hello3
//HelloWorld
</code></pre>
<h3>数据类DataClass</h3>
<ul>
<li>自动生成标准方法:
数据类会自动生成 equals()、hashCode()、toString()、copy() 等方法,普通类需手动实现</li>
<li>主构造函数要求:
必须至少有一个参数,且参数需标记为 val 或 va, 普通类无此限制</li>
<li>解构声明支持
数据类自动支持解构,可直接将属性拆分为变,普通类则需手动实现 componentN()</li>
<li>不可变性推荐:
属性通常声明为 val(不可变),但支持 var(可变)</li>
<li>继承限制
数据类不能是 abstract、open、sealed 或 inner 类</li>
<li>无无参构造函数
需显式定义默认值或辅助构造函数</li>
</ul>
<h4>应用</h4>
<ul>
<li>数据传输对象
如 JSON 解析后的数据实体</li>
<li>简化数据操作
利用 copy() 实现不可变对象的修改
val user2 = user1.copy(age = 26)</li>
</ul>
<pre><code>data class Country(val id: Int, val name: String)
data class Country2(var id: Int, var name: String)
//component 组件
class ComponentX{
operator fun component1(): String{
return "您好,我是"
}
operator fun component2(): Int{
return 1
}
operator fun component3(): Int{
return 1
}
operator fun component4(): Int{
return 0
}
}
fun main7(args: Array<String>) {
//componentN解构声明
val china = Country(0, "中国")
println(china)// Country(id=0, name=中国)
println("${china.component1()} ------ ${china.component2()} ")//0 ------ 中国
val (idx, name) = china //解构赋值
println("$idx ------ $name")//0 ----- 中国
val componentX = ComponentX()//解构赋值
val (a, b, c, d) = componentX
println("$a $b$c$d")//您好,我是 110
}
</code></pre>
<h3>内部类</h3>
静态内部类与非静态内部类的区别:到底是否持有外部类的状态 (非静态内部类持有外部类的状态,可以访问外部类的属性)
<pre><code>open class Outter{
val a: Int = 0
class InnerDefaultStatic{
}
//非静态内部类
inner class Inner{
val a: Int = 5
fun hello(){
println(this@Outter.a)
}
}
}
interface OnClickListener{
fun onClick()
}
class View{
var onClickListener: OnClickListener? = null
}
fun main(args: Array<String>) {
val innerDefaultStatic = Outter.InnerDefaultStatic()
val inner = Outter().Inner()
val view = View()
//匿名内部类看上去没有名字,但是编译生成字节码文件有自己的ID,类似Outter$1.class
view.onClickListener = object : Outter(), OnClickListener{
override fun onClick() {
}
}
}
</code></pre>
<h3>枚举类</h3>
<ul>
<li>实例可数的类,注意枚举也是类</li>
<li>可以修改构造,添加成员</li>
<li>可以提升代码的表现力,也有一定的性能开销</li>
<li>实例可数指枚举(enum)类型的成员数量固定且不可扩展。</li>
<li>枚举通过enum class声明,其所有成员(如VERBOSE、DEBUG等)均为该类的实例对象,且默认构造函数为私有(private constructor()),因此无法被继承或扩展</li>
</ul>
<pre><code>enum class LogLevel(val id: Int){
VERBOSE(0), DEBUG(1), INFO(2), WARN(3), ERROR(4), ASSERT(5);
fun getTag(): String{
return "$id, $name"
}
override fun toString(): String {
return "$name, $ordinal"
}
}
println(LogLevel.DEBUG.ordinal)
LogLevel.values().map(::println)
println(LogLevel.valueOf("ERROR"))
//VERBOSE, 0
//DEBUG, 1
//INFO, 2
//WARN, 3
//ERROR, 4
//ASSERT, 5
//ERROR, 4
</code></pre>
<h3>密封类</h3>
子类可数
<pre><code>sealed class PlayerCmd{
}
class Play(val url:String,val position:Long =0):PlayerCmd()
class Seek(val position: Long): PlayerCmd()
object Pause:PlayerCmd()
object Resume:PlayerCmd()
object Stop:PlayerCmd()
enum class PlayerState{
IDLE, PAUSE, PLAYING
}
</code></pre>