初学者问题-继承-为什么不使用我的年龄构造函数参数? [英] Beginner question - Inheritance - why isn't my age constructor parameter used?

查看:57
本文介绍了初学者问题-继承-为什么不使用我的年龄构造函数参数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在这里出现Noob问题,我正在通过Udemy初学者进行操作 Kotlin 当然,我无法弄清楚为什么在使用派生类时未使用age参数,但是在使用基类时仍可以工作.

Noob question here, I am working my way through a Udemy beginner Kotlin course and I can't work out why my age parameter isn't used when I use my derived class, but will work when my base class is used.

人员班

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类继承了.

As you can see Bill was a direct use of the method in the Person Class, whereas Mike was an indirect call, the age parameter should have been inherited from the Person Class through the Student Class...

查看Kotlin官方文档,看来问题出在"派生类初始化命令",但对于我一生来说,我不太想知道如何纠正此错误.

Looking at the official Kotlin docs, it looks like the issue is to do with the "Derived class initialisation order" but for the life of me I can't quite grok how to correct this.

感谢您的帮助,

对于不正确的术语,PS表示歉意.总是为能做得更好而感到高兴.

PS apologies for the inaccurate terminology. Always glad of pointers to do better.

推荐答案

Umberto Cozzi所说的是对的,这与在构造函数中引用开放值有关.如果单步执行代码,您将看到事件的顺序为:

What Umberto Cozzi said is right, it is to do with the fact that you're referring to an open value in the constructor. If you step through the code you'll see that the sequence of events is:

  1. 您调用 Student 构造函数,并传入 age 的值.
  2. 构造函数要做的第一件事(在构造 Student 对象之前之前)是调用超类的构造函数.
  3. 在构建超类(<人> )的过程中,您按下了以下代码行: val dob = thisyear-age . age 是开放成员(即在子类 Student 中被覆盖).因此,应从 Student 中检索该值.问题在于,此时 Student 尚未完全构建(记住其构造函数所做的第一件事就是调用超类的构造函数).因此,人员尚无法问 student 应该是什么值 age ,因为该值尚未设置.如果您单步执行代码并在此代码行中检出 age 的值,则该值为零( Int 的默认值).
  1. You call the Student constructor, passing in a value for age.
  2. The first thing that constructor does (before constructing the Student object) is call the superclass's constructor.
  3. During the construction of the superclass (Person) you hit this line of code: val dob = thisyear - age. age is an open member (i.e. overridden in the subclass, Student). So the value should be retrieved from Student. The problem is that at this point the Student hasn't been fully constructed yet (remember the first thing its constructor did was call the superclass's constructor). So it's not possible yet for Person to ask Student what value age should be, as that value hasn't been set yet. If you step through the code and check out the value of age at this line of code, it's zero (the default value for an Int).

那么问题是怎么办?我认为您应该问的是:为什么 Student 会覆盖 age (实际上是 firstname surname ). Person Student age firstname surname 的行为是否有所不同>?答案可能是否定的.因此, Student 不应覆盖这些属性:而是应将它们简单地声明为构造函数参数(没有 val var )并将这些值传递给基类.换句话说,学生应如下所示:

So the question is what to do? And I think the thing you should ask is: why does Student override age (and indeed firstname and surname). Is there any different in behaviour of age, firstname and surname between Person and Student? The answer is probably no. So Student shouldn't override these properties: instead it should declare them simply as constructor parameters (without val or var) and pass those values to the base class. In other words, Student should look as follows:

class Student(firstname: String, surname: String, age: Int, val studentID: Int) :
        Person(firstname, surname, age) {
    ...

您可能还想知道一个事实,即声明 thisyear 的代码行实际上创建了 Person 的属性,称为 thisyear >,我想您是不需要的.直接在类中(而不是在函数中)声明的任何 val var 成员都是属性的声明(这就是为什么所有这些都立即计算出来的原因)在构建 Person 对象期间).因此,您可能希望将其内联为:

You might also want to be aware of the fact that your line of code that declares thisyear actually creates a property of Person, called thisyear, which I guess you don't want. Any val or var members that are declared directly in the class (rather than in a function) are a declaration of a property (and this is why this is all being calculated immediately during the construction of the Person object). So you might well want to inline this as:

val dob = Calendar.getInstance().get(Calendar.YEAR) - age

如果计算更加复杂并且需要更多行代码,只需创建一个私有方法(例如 calculateDob )并调用该方法,例如 val dob = calculateDob(age)

If the calculation is more complex and requires more lines of code, just create a private method (e.g. calculateDob) and call that, e.g. val dob = calculateDob(age)

age var (即可以更改),而 dob val (即无法更改).因此,用户可以更改 age 的值,但不会更新 dob .解决此问题的一种可能方法是更改​​ dob ,以使其不是构造过程中为其分配(只读)值的属性,而使其成为具有getter的属性,该属性将计算该值每次被调用时,例如

There's also the slight anomaly that age is a var (i.e. can change) whereas dob is a val (i.e. can't change). So a user could change the value of age, but dob won't be updated. One possible way to solve this is to change dob so that instead of it being a property which is assigned a (read-only) value during construction, make it a property with a getter which will calculate the value every time it's called, e.g.

val dob
    get() = Calendar.getInstance().get(Calendar.YEAR) - age

另一种选择是为 age 添加背景字段和getter/setter,并在 age 更新时更新 dob .

Another option is to add a backing field and getter/setter for age and update dob whenever age is updated.

这篇关于初学者问题-继承-为什么不使用我的年龄构造函数参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆