在这里出现Noob问题,我正在通过Udemy初学者Kotlin课程学习,我无法弄清楚为什么在使用派生类时不使用我的age参数,但是在我的基础学习时可以使用类。
人员班
open class Person(open var firstname: String, open var surname: String,
open var age: Int) {
val thisyear: Int = Calendar.getInstance().get(Calendar.YEAR)
val dob = thisyear - age
fun printperson() {
println("$firstname $surname was born in $dob")
}
}
学生班
class Student(override var firstname: String, override var surname:
String, override var age: Int, val studentID: Int):
Person(firstname, surname, age) {
fun returnDetails() {
println("Hello $firstname, your Student ID is: $studentID")
}
}
主要
fun main(args: Array<String>) {
val studentMike = Student(firstname = "Mike",
surname = "Stand", age = 67, studentID = 8899)
studentMike.printperson()
studentMike.returnDetails()
val personBill = Person(firstname = "Bill", surname = "Hook", age = 34)
personBill.printperson()
}
输出
Mike Stand was born in 2018
Hello Mike, your Student ID is: 8899
Bill Hook was born in 1984
如您所见,Bill是Person类中方法的直接使用,而Mike是间接调用,年龄参数应该是通过Student类从Person类继承的。
看看Kotlin的官方文档,问题似乎与“ Derived class initialisation order”有关,但对于我一生来说,我不太想知道如何解决此问题。
感谢您的帮助,
对不正确的术语表示歉意。总是为能做得更好而感到高兴。
答案 0 :(得分:2)
您要引用的文档明确表示“在设计基类时,因此应避免在构造函数,属性初始化程序和init块中使用开放成员”
这正是您的示例中发生的情况。在基类Person中,您可以使用构造函数中的开放成员年龄(计算dob的行)。 应该避免这种情况,因为执行该行计算dob时,age尚未从派生类接收到值。
好的,我没有回答“如何更正此问题?”但我希望它有助于弄清发生了什么事
答案 1 :(得分:1)
Umberto Cozzi所说的是正确的,这与您在构造函数中引用开放值有关。如果逐步执行代码,您将看到事件的顺序为:
Student
构造函数,并传递age
的值。Student
对象之前)是超类的构造函数。Person
)时,您遇到了以下代码行:val dob = thisyear - age
。 age
是开放成员(即在子类Student
中被覆盖)。因此,应从Student
中检索该值。问题在于,此时Student
尚未被完全构造(记住其构造函数所做的第一件事就是调用超类的构造函数)。因此,Person
尚不能问Student
应该是什么值age
,因为尚未设置该值。如果您单步执行代码并在此代码行中检出age
的值,则该值为零(Int
的默认值)。那么问题是怎么办?我认为您应该问的是:为什么Student
会覆盖age
(实际上是firstname
和surname
)。 age
和firstname
之间surname
,Person
和Student
的行为是否有不同?答案可能是否定的。因此Student
不应覆盖这些属性:而是应将它们简单地声明为构造函数参数(没有val
或var
)并将这些值传递给基类。换句话说,Student
应该如下所示:
class Student(firstname: String, surname: String, age: Int, val studentID: Int) :
Person(firstname, surname, age) {
...
您可能还想知道,声明thisyear
的代码行实际上创建了Person
的属性,称为thisyear
,我想您实际上并没有想。直接在类中(而不是在函数中)声明的任何val
或var
成员都是属性的声明(这就是在构建{时立即计算所有这些属性的原因。 {1}}对象)。因此,您可能希望将其内联为:
Person
如果计算更为复杂并且需要更多代码行,则只需创建一个私有方法(例如val dob = Calendar.getInstance().get(Calendar.YEAR) - age
)并调用该方法,例如calculateDob
还有一点异常,val dob = calculateDob(age)
是age
(即可以更改),而var
是dob
(即不能更改)。因此,用户可以更改val
的值,但是age
不会被更新。解决此问题的一种可能方法是更改dob
,以使其不是构造过程中为其分配(只读)值的属性,而应使其成为具有getter的属性,该属性将在每次构造时均计算该值称为,例如
dob
另一种选择是为val dob
get() = Calendar.getInstance().get(Calendar.YEAR) - age
添加一个支持字段和getter / setter,并在age
更新时更新dob
。