Kotlin 类 对象 函数

本文介绍Kotlin中的类定义、构造函数使用、函数定义及调用、中缀表示法、重载、可见性修饰符、继承、抽象和接口等概念。通过示例代码帮助读者理解Kotlin面向对象编程的基本原理。

定义类

使用class声明类

class Invoice { }

// 如果一个类没有类体,可以省略花括号。
class Empty

构造函数

在 Kotlin 中的一个类可以有一个主构造函数和一个或多个次构造函数。
主构造函数是类头的一部分:它跟在类名(和可选的类型参数)后。
直接调用构造函数来创建类的实例,Kotlin中没有new关键字

// 主构造函数写在类名后
class Class1 constructor(firstName: String)

// 如果主构造函数没有任何注解或者可见性修饰符
// 可以省略这个 constructor 关键字。
class Class2(firstName: String)

// 主构造函数不能包含任何的代码
// 初始化的代码可以放到以 init 关键字作为前缀的初始化块中
class Person(name: String) {
    var age = 20
    init {
        println("name = $name, age = $age")
    }
    fun hello() {
        println("hello")
    }
    // 声明次构造函数 需要直接或通过其他次构造函数间接调用主构造函数
    constructor(name: String, age: Int) : this(name) {
        this.age = age
    }
}

fun main(args: Array<String>) {
    val p = Person("fff")
    p.age = 21
    p.hello() //hello
}

函数

fun 函数名(参数列表): 返回值类型 { 函数体 }
fun 函数名(参数列表): 返回值类型 = 返回值表达式
- 无返回值时,返回值类型可省略
- 使用返回值表达式作为函数体且返回类型可有编译器推断时,返回值类型可省略
- 使用函数名(参数)对象名.函数名(参数)调用函数
- 参数可以有默认值
- 调用函数时,可以指定参数名

fun function(i: Int, a: Int = 1, b: Int = 10): Int{
    return i + a + b
}

fun main(args: Array<String>) {
    println(function(123)) // 134
    println(function(123, b = 20)) // 144
}

中缀表示法调用函数

如果一个函数
- 是成员函数或扩展函数
- 只有一个参数
- 用infix关键字标注

那么这个函数可以用对象A 函数名 对象B的方式调用,等同于对象A.函数名(对象B)

infix fun Int.addToString(i: Int): String{
    return this.toString() + i.toString()
}

fun main(args: Array<String>) {
    println(123 addToString 456) // 123456
    println(345.addToString(321)) // 345321
}

重载

Kotlin允许使用默认参数来减少重载数量

fun foo(i:Int, n = 123) {

}

fun foo(s:String) {

}

fun main(args: Array<String>) {
    foo(123)
    foo(123, 234)
    foo("str")
}

面向对象编程

可见性修饰符
- private 仅自己可见
- protected 在子类中可见
- internal 在同一模块中可见
- public 随处可见

Kotlin的默认可见性是public

继承

Kotlin中所有类的共同超类是Any
允许继承的类和成员要声明为open,未声明open的类和成员默认是final
使用class 类名() : 要继承的父类()继承
重写父类的成员时要声明为override
open子类重写的成员默认是open的,可以用final禁止继续继承

open class Parent{
    open var i:Int = 1
    open fun foo(){}
    fun bar(){}
}

open class Child : Parent() {
    override var i: Int = 2   
    override final fun foo(){}
    override fun bar(){} // error
}

抽象

abstract声明抽象类和函数
抽象类和函数默认是open
可以用抽象成员覆盖父类的非抽象开放成员

abstract class Animal {
    abstract fun play()
}

class Dog : Animal() {
    override fun play() {

    }
}

接口

使用interface定义接口
接口中的方法可以有默认实现

interface Inter{
    fun go()
    fun ok(){
        println("ok")
    }
}

class InterClass : Inter{
    override fun go(){

    }
}
<think>我们正在讨论Kotlin中如何将泛型对象作为函数参数传递。在Kotlin中,泛型在函数参数中的使用与在声明中似。我们可以定义一个函数,其参数型为泛型,同时指定该泛型型参数。 例如,假设我们有一个泛型`Box<T>`,我们想写一个函数,它接受`Box`对象作为参数,而不管`Box`中的型`T`是什么。但是,通常我们会指定`T`的具体型或者使用边界。 如果我们希望函数接受任意型`T`的`Box`,我们可以这样写: ```kotlin fun <T> handleBox(box: Box<T>) { // 处理box } ``` 这里,我们在函数上声明了型参数`T`,然后在参数中使用`Box<T>`。 另一种情况是,如果我们的函数本身不需要知道`T`的具体型,我们可以使用星投影(star projection)`Box<*>`,这表示我们接受`Box`的任何泛型实例。 ```kotlin fun handleAnyBox(box: Box<*>) { // 处理box,但是不能知道box中元素的具体型 } ``` 注意:在星投影的情况下,我们不能安全地写入数据(因为型未知),但可以读取为`Any?`。 另外,如果泛型有多个型参数,我们也可以使用似的方式。 下面是一个具体的例子: ```kotlin class Box<T>(val content: T) // 使用泛型函数 fun <T> printBoxContent(box: Box<T>) { println(box.content) } // 使用星投影 fun printAnyBoxContent(box: Box<*>) { println(box.content) // content的型是Any? } fun main() { val stringBox = Box("Hello") val intBox = Box(123) printBoxContent(stringBox) // 输出: Hello printBoxContent(intBox) // 输出: 123 printAnyBoxContent(stringBox) // 输出: Hello printAnyBoxContent(intBox) // 输出: 123 } ``` 如果我们需要对泛型型有所约束,可以使用上界(upper bound)。例如,如果我们希望函数只接受`Box<Number>`,可以这样写: ```kotlin fun handleNumberBox(box: Box<out Number>) { // 这里box的内容是Number或其子型 val num: Number = box.content println(num) } ``` 或者使用`Box<Number>`直接作为参数型,但这样会限制只能传递`Box<Number>`,而不能传递`Box<Int>`等。因此,我们通常使用协变(使用`out`)来允许传递子型。 另外,如果我们的函数需要传递多个相同型参数的Box,那么必须使用相同的型参数,这时就不能用星投影了,因为星投影代表未知型,多个星投影可能代表不同的型。 总结一下,传递泛型对象作为函数参数的方式有: 1. 在函数上声明型参数,如`fun <T> func(param: MyClass<T>)` 2. 使用星投影,如`fun func(param: MyClass<*>)` 3. 使用型投影(type projection)来指定变型(如`in`、`out`),如`fun func(param: MyClass<out Number>)` 根据具体需求选择合适的方式。 现在,生成相关问题: §§相关问题§§ 1. Kotlin中的星投影(star projection)在哪些场景下使用? 2. 在Kotlin中,如何处理泛型函数型参数约束? 3. Kotlin中的泛型变型(variance)如何影响函数参数传递? 4. 为什么在星投影的泛型参数中不能写入数据?</think>在 Kotlin 中将泛型对象作为函数参数传递时,需要注意型参数的处理方式。以下是几种常见场景的实现方法: ### 1. 基本泛型参数传递 ```kotlin class Box<T>(val content: T) // 接收泛型实例作为参数 fun <T> processBox(box: Box<T>) { println("Processing: ${box.content}") } fun main() { val stringBox = Box("Hello") val intBox = Box(42) processBox(stringBox) // 输出: Processing: Hello processBox(intBox) // 输出: Processing: 42 } ``` ### 2. 使用型投影(Type Projections) 当需要处理未知具体型的泛型时: ```kotlin fun printAnyBox(box: Box<*>) { val content = box.content // 型为 Any? println("Content: $content") } ``` ### 3. 带约束的泛型参数 ```kotlin class NumericBox<T: Number>(val value: T) fun <T: Number> sumBoxes(box1: NumericBox<T>, box2: NumericBox<T>): Double { return box1.value.toDouble() + box2.value.toDouble() } fun main() { val intBox = NumericBox(10) val floatBox = NumericBox(5.5f) println(sumBoxes(intBox, floatBox)) // 输出: 15.5 } ``` ### 4. 逆变参数传递(使用 `in` 关键字) ```kotlin interface Consumer<in T> { fun consume(item: T) } fun handleConsumer(consumer: Consumer<String>) { consumer.consume("Data") } fun main() { val anyConsumer = object : Consumer<Any> { override fun consume(item: Any) = println("Consumed: $item") } handleConsumer(anyConsumer) // 有效,因为 Consumer<Any> 是 Consumer<String> 的子型 } ``` ### 关键概念说明: 1. **型参数声明**:在函数名前声明 `<T>` 表示该函数使用泛型型 2. **星投影(`*`)**:表示未知泛型型,只能安全读取为 `Any?`[^1] 3. **型约束**:使用 `<T : 上界>` 限制可接受的型范围 4. **变型注解**: - `in` 表示逆变(消费型) - `out` 表示协变(生产型) ```mermaid graph TD A[传递泛型参数] --> B[基本泛型] A --> C[型投影] A --> D[带约束泛型] A --> E[逆变参数] B --> F[fun <T> func(param: Class<T>)] C --> G[fun func(param: Class<*>)] D --> H[fun <T: Bound> func(param: Class<T>)] E --> I[fun func(param: Consumer<in Type>)] ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值