Kotlin学习(四)—— 类与对象(类的声明,属性初始化问题,构造器)

类的声明

类声明由类名、类头(指定其类型参数、主构造函数等)和由⼤括号包围的类体构成。类头和类体都是可选的
定义一个最简单的类

class A

这样就定义了最简单的一个类了,什么都没有的类,没有类头和类体。(没有类体,可以省略花括号)

接下来定义一个正常的类:

class Person public constructor(name: String,age: Int)/*主构造函数,和它参数*/{
    var name:String  //属性
    var age :Int
    init {  //初始化器,主构造函数的函数体,能使用主构造函数的参数
        this.name = name
        this.age = age
    }

    //成员函数
    fun say(){
        println("I am ${name} and I was ${age} years old")
    }
}

但是实际上,上面的类定义太复杂了,因为:
1. 除非构造函数有明确必要私有,一般情况,默认为公有的
2. 属性可以写在主构造函数中,除非有一些属性在创建时不要初始化,不要初始化的属相或者延迟初始化的属性写在可以写在类里
3. init 只是主构造函数的方法体,没有特殊要求也可以省略

接下来简化的类的写法如下:

class Person(var name: String ,var age: Int){

    //成员函数
    fun say(){
        println("I am ${name} and I was ${age} years old")
    }
}

类中属性初始化问题

注意类中的属性不像Java那样声明了就会有默认值,我们可以不初始化它
kotlin中,类的属性必须要初始化或者延迟初始化:
我们先不管延迟初始化,先看看我们初始化的方式有几种

1 . 定义时直接给它赋予初始值

class User{
    //属性定义方式1,直接给它初值
    var name:String = ""
    var age:Int =0
}

2 . 使用init块初始化:init相当于主构造函数的函数体,所以能用主构造函数的参数

class User(name: String){
    //属性初始化方式2,使用init
    var name:String
    var age:Int
    init {
        this.name = name
        this.age = 0
    }
}

3 . 直接调用主构造函数的参数

class Customer(name: String) {
    val customerKey = name.toUpperCase()
}

4 . 直接把属性定义在主构造函数中,这样我们创建对象的时候就直接把属性初始化了

class User(var name: String,var age: Int)

5 . 延迟初始化
延迟初始化可以把属性的初始化推迟到要访问这个属性之前,但是访问之前还没有初始化就会抛出 kotlin.UninitializedPropertyAccessException的异常,不过要注意的是延迟初始化的属性不能是原生类型(Byte,Short,Int,Long,Float,Double,char,Boolean)八种,像String类型就可以延迟初始化

class User(var name: String,var age: Int){
    lateinit var gender :String
}

fun main(args: Array<String>) {
    var user = User("huahua",11) 
    //如果在要访问这个属性之前还不初始化,那么就会抛出kotlin.UninitializedPropertyAccessException异常
    //所以访问之前一定要初始化延迟初始化属性
    println(user.gender)

}

关于属性特别注意的是:和Java不同,不要轻易的用private修饰属性,它会是属性的setter和getter也私有化,要使属性能够访问到,尽量不要加权限修饰符,这一点一定要从Java的封装性中走出来!!!!
另外:要注意属性声明的是var还是val,var是可变属性,具有getter和setter,val是只读属性,只有getter

构造函数

Kotlin 中的⼀个类可以有⼀个主构造函数和⼀个或多个次构造函数。主构造函数是类头的⼀部分:它跟在类名(和可选的类型参数)后
类也可以声明前缀有 constructor的次构造函数

class User(var name: String){
    lateinit var age :String
    constructor(name: String,age: String):this(name){  //直接委托主构造函数
    }
    constructor():this("huahua","11"){  //通过委托另一个构造函数,间接委托主构造函数
    }
}

注意:

  1. 如果主构造函数没有任何注解或者可⻅性修饰符,可以省略这个 constructor 关键字
  2. 主构造函数不能包含任何的代码。初始化的代码可以放到以 init 关键字作为前缀的初始化块(initializer blocks)中
  3. 主构造的参数可以在初始化块中使⽤。它们也可以在类体内声明的属性初始化器中使⽤(上面属性初始化已经说过)
  4. 如果构造函数有注解或可⻅性修饰符,这个 constructor 关键字是必需的,并且这些修饰符在它前⾯
  5. 如果类有⼀个主构造函数,每个次构造函数需要委托给主构造函数,可以直接委托或者通过别的次构造函数间接委托,委托到同⼀个类的另⼀个构造函。数⽤ this 关键字即可(上面的代码已经说明了委托)
  6. 如果⼀个⾮抽象类没有声明任何(主或次)构造函数,它会有⼀个⽣成的不带参数的主构造函数。构造函数的可⻅性是 public。如果你不希望你的类有⼀个公有构造函数,你需要声明⼀个带有⾮默认可⻅性的空的主构造函数
  7. Kotlin在 JVM 上,如果主构造函数的所有的参数都有默认值,编译器会⽣成 ⼀个额外的⽆参构造函数,它将使⽤默认值。这使得 Kotlin 更易于使⽤像 Jackson 或者 JPA 这样的通过⽆参构造函数创建类的实例的库,如下:
  8. 如果没有声明主构造函数 ,就不用声明委托主构造函数了,其实是默认委托给无参构造函数了
  9. 如果没有在类名后面声明主构造函数,如果有一个无参的构造函数,那么会被当作主构造函数。
  10. init块中的代码是在创建对象以定会执行的(这里有像Java的初始化块)
class LL1{
    var name:String
    init {
        println("_主")
        this.name = ""
    }
    constructor(name: String){
        this.name=name

    }
    constructor(){
        println("_次")
    }
    constructor(name: String,age: Int)
}

fun main(args: Array<String>) {
    var l = LL1() //因为有默认的主构造函数,所以能这样创建对象
}
class User {
    lateinit var name:String
    constructor(name: String){
        this.name =name
    }
    init {
        println("lala")//调用每个构造方法之前会调用init中的代码
    }
    constructor(){
        println("主")
    }


}

创建类的对象

kotlin中并没有new关键字,创建对象是直接调用构造方法

fun main(args: Array<String>) {
    var u = User() 
}

类的成员

  1. 构造函数和初始化块
  2. 函数
  3. 属性
  4. 嵌套类和内部类
  5. 对象声明

这里小编只说了构造函数和初始化块,之后会补上其他成员的文章

结语:
此篇文章,是小编自己测试之后才发表的,所以如果有什么遗漏或者错误,还请大家用于在评论区指正。

### Kotlin 构造方法初始化变量 在 Kotlin 中,可以通过主构造函数和次构造函数来实现变量的初始化。以下是关于这两种方式的具体说明以及示例。 #### 主构造函数初始化 主构造函数是头部的一部分,在声明的同时即可完成属性初始化。如果需要对传入的参数进一步处理,则可以在 `init` 块中进行逻辑操作[^2]。 ```kotlin class Person(val name: String, val age: Int) fun main() { val person = Person("Alice", 25) println("${person.name}, ${person.age}") } ``` 在此示例中,`Person` 通过主构造函数接收两个参数并将其直接作为属性。这种方式简洁明了,适用于不需要复杂初始化逻辑的情况[^3]。 #### 次构造函数初始化 当需要多个不同的初始化路径或者更复杂的初始化过程时,可以使用次构造函数。次构造函数必须显式调用主构造函数或其他次构造函数以完成对象创建[^2]。 ```kotlin class Employee private constructor( var id: Int, var department: String ) { constructor(id: Int) : this(id, "Default Department") {} init { require(department.isNotEmpty()) { "Department cannot be empty." } } fun displayInfo() { println("Employee ID: $id, Department: $department") } } fun main() { val employee1 = Employee(101) employee1.displayInfo() val employee2 = Employee(102, "HR") employee2.displayInfo() } ``` 在这个例子中展示了两种创建 `Employee` 对象的方式:一种只提供员工编号,默认分配到某个部门;另一种则允许指定具体所属部门。注意这里还加入了校验逻辑确保数据有效性[^2]。 #### 结合临时变量成员属性 有时为了提高代码可读性和灵活性,会先定义一些仅用于赋值操作而不对外暴露的临时变量[^3]。 ```kotlin class Student(_firstName: String, _lastName: String, _grade: Double) { val firstName = _firstName.capitalize() val lastName = _lastName.capitalize() val gradeFormatted = String.format("%.2f", _grade) override fun toString(): String { return "$firstName $lastName has a grade of $gradeFormatted" } } fun main() { val student = Student("john", "doe", 97.8) println(student) } ``` 此片段里 `_firstName`, `_lastName`, 和 `_grade` 都只是用来帮助构建最终公开使用的标准化版本的数据[^3]。 ### 总结 以上介绍了三种不同场景下的构造器应用模式——简单直接型、多重入口型还有加工转换型。每种都有其适用场合,请依据实际需求选取最合适的方案。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值