passing variable to parent class in Kotlin [duplicate] - kotlin

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.
Person Class
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")
}
}
Student Class
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")
}
}
Main
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()
}
Output
Mike Stand was born in 2018
Hello Mike, your Student ID is: 8899
Bill Hook was born in 1984
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...
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.
Thanks for your help,
PS apologies for the inaccurate terminology. Always glad of pointers to do better.

the documentation you are referring to, says explicitly "When designing a base class, you should therefore avoid using open members in the constructors, property initializers, and init blocks"
this is exactly what is happening in your example. In the base class Person you use the open member age in the constructor (the line where you calculate the dob).
This should be avoided because when that line calculating dob is executed, age did not yet receive a value from the derived class.
Ok, I did not answer at the "How to correct this?" but I hope it helps in clarifying what is going on

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:
You call the Student constructor, passing in a value for age.
The first thing that constructor does (before constructing the Student object) is call the superclass's constructor.
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).
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) {
...
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
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)
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
Another option is to add a backing field and getter/setter for age and update dob whenever age is updated.

Related

Is there a way to pass the name of a field to a setter function?

Here I have several functions that all just set a single field on a model record.
In a more dynamic language, I'd just have a single setter function and pass it the name of the field (as a string) and the value that I want to set on the model object.
Is there a way to pass the name of the field in Elm?
What's the Elm way of doing something like this?
type alias Patient =
{ id : String
, name : String
, dateOfBirth : String
, sex : String
... other fields
}
setPatientName : Patient -> String -> Patient
setPatientName patient value =
{ patient | name = value }
setPatientDateOfBirth : Patient -> String -> Patient
setPatientDateOfBirth patient value =
{ patient | dateOfBirth = value }
setPatientSex : Patient -> String -> Patient
setPatientSex patient value =
{ patient | sex = value }
... many others
-- idx is the index of the patient in the model (which is an array of patients)
-- UpdateCell is a variant of my Msg type, like this: UpdateCell Int (Patient -> String -> Patient) String
onInputHandler : Int -> (Patient -> String -> Patient) -> String -> Msg
onInputHandler idx setter inputText =
UpdateCell idx setter inputText
-- idx is the index of the patient in the model (which is an array of patients)
createTableRow : Int -> Patient -> Html Msg
createTableRow idx patient =
...
, input [ type_ "text", onInput (onInputHandler idx setPatientName), value patient.name ] []
, input [ type_ "text", onInput (onInputHandler idx setPatientDateOfBirth), value patient.dateOfBirth ] []
...
I'm currently using each of these functions as an event handler for input elements. So I need a function that I can use for handling the input event. Ideally, I'd define just a single function and use that single one for all the input elements and pass it the field I want to update on the patient record.
The short answer is "no". But this seems a bit like an XY problem. It's not clear what benefit you are trying to achieve since the full application of such a function would be longer than the equivalent record update expression:
setField "name" patient value
-- vs
{ patient | name = value }
and as a partially applied function is only slightly shorter than the equivalent anonymous function with shortened argument names:
setField "name"
-- vs
\r x -> { r | name = x }
Although the latter is significantly noisier with all the symbols.
There is also a short-hand function for getting a record field:
.name
-- vs
\r -> r.name
So there is some precedent for having a dedicated syntax for setter functions too, but unfortunately there is not. Likely because it would complicate the language, and the syntax in particular, for relatively little benefit. I'm therefore curious about what you're actually trying to accomplish.
Edit after question update:
Putting functions in the Msg is a very bad idea because it goes against the Elm Architecture. It makes the state transition opaque and won't work very well with the debugger. When something goes wrong you can still see the state before and after, but you'll have trouble understanding what happened, and why it happened, because that information is encoded in an opaque function which probably isn't the one it should be.
You'll also have trouble factoring your logic. If you need something to happen only when a certain field updates, you might have to put the logic in the view, or special-case that field by putting the logic for that in update while the rest is in view, for example. Either way, you're on the path to a messy code base.
You should generally use names for messages that describe what happened, not what to do, because that tends to lead to an imperative mindset. Instead of UpdateCell you could call it InputChanged, for example. Then instead of the function you should have an identifier for the field. Ideally a custom type, like InputChanged Name, but even a string will work, though it will be much easier to miss a typo.
So instead of setter functions for each field you'll just case match the message and set the field in the update function:
InputChanged Name value ->
{ patient | name = value }
-- vs
setPatientName : Patient -> String -> Patient
setPatientName patient value =
{ patient | name = value }
Then if you need to clear the sex when the name changes, for example (because reasons...), you can simply do:
InputChanged Name value ->
{ patient | name = value, sex = "" }
The Elm Architecture is good because it makes changes easy and safe, not because it's concise and free of boiler-plate. Good Elm code often has a lot of copy-and-paste, but that's not always bad.

How to make method in repository like GreaterThanOrEquals in Spring Data Rest..?

How to make method on entity like GreaterThanOrEquals in Spring Data Rest..?
Entity class looks like below.
Class Demo {
private Long id;
private Long number;
}
Repository class looks like below.
interface DemoRepository extends JPARepository<Demo, Long>{
Collection<Demo> findByIdAndNumberGreaterThanZero(Long id, Long number);
}
The above code will works.? If not so how could i achieve this with out #Query.?
The method name is mistyped it should be findByIdAndNumberGreaterThanEqual, not findByIdAndNumberGreaterThanZero.
Collection<Demo> findByIdAndNumberGreaterThanEqual(Long id, Long number);
Also, if the id property is the actual id of the entity this method will return zero or one result. (Either the entity with the given id, if the number of that entity is greater or equal or nothing at all)
So it should be
Optional<Demo> findByIdAndNumberGreaterThanEqual(Long id, Long number);
...or if you need all of the entities where the number property is greater than a given value:
Collection<Demo> findByNumberGreaterThanEqual(Long number);

Beginner question - Inheritance - why isn't my age constructor parameter used?

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.
Person Class
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")
}
}
Student Class
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")
}
}
Main
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()
}
Output
Mike Stand was born in 2018
Hello Mike, your Student ID is: 8899
Bill Hook was born in 1984
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...
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.
Thanks for your help,
PS apologies for the inaccurate terminology. Always glad of pointers to do better.
the documentation you are referring to, says explicitly "When designing a base class, you should therefore avoid using open members in the constructors, property initializers, and init blocks"
this is exactly what is happening in your example. In the base class Person you use the open member age in the constructor (the line where you calculate the dob).
This should be avoided because when that line calculating dob is executed, age did not yet receive a value from the derived class.
Ok, I did not answer at the "How to correct this?" but I hope it helps in clarifying what is going on
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:
You call the Student constructor, passing in a value for age.
The first thing that constructor does (before constructing the Student object) is call the superclass's constructor.
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).
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) {
...
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
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)
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
Another option is to add a backing field and getter/setter for age and update dob whenever age is updated.

Where clause with type check "is" operator does not use discriminator

I have mapped a class hierarchy in NHibernate. Simple as that:
class abstract Animal
class Dog : Animal
class Cat: Animal
class Cow: Animal
In mapping I have discriminator values set on ANIMAL_TYPE column:
Dog -> dog
Cat -> cat
Cow -> cow
All kind of queries work. Except this one, when I need to fetch objects of two particular types. I wrote is like this:
QueryOver.Of<Animal>().Where(animal => animal is Dog || theme is Cat)
And I receive no items in result. When I look into generated query, NHibernate generates:
(this_.ANIMAL_TYPE = #p0 or this_.ANIMAL_TYPE = #p1)
which is fine, but the values in #p0 and #p1 parameters contain full class name, eg.
Zoo.Model.Cat
instead of discriminator values. How can I solve it? Do I have to keep my discriminator values in sync with type names? I would like to avoid it if possible.
This question & answer should be working for you: How can QueryOver be used to filter for a specific class? (asked just a day before you)
var query = session.QueryOver<Animal>()
.Where(animal => animal.GetType() == typeof (Dog)
|| animal.GetType() == typeof (Cat)
);
var list = query.List<Animal>();

Linq to sql - get value from db function and not directly from the db field (while mapping properties)

When you map a table to an object, every property created corresponds to one db column.
I want to execute a db function on a column before it gets mapped to the property, so the property gets the value returned by the db function, and not the column
I was trying to achieve that by Expression property of ColumnAttribute (as in the example below), so instead of BirthDate the usrFn_UTCToLocalTime(BirthDate) is returned
but it does not seem to be working and still gets pure value of the column.
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage = "_BirthDate", DbType = "DateTime", UpdateCheck = UpdateCheck.Never, Expression = "dbo.usrFn_UTCToLocalTime(BirthDate)")]
public System.Nullable<System.DateTime> BirthDate
{
get
{
return this._BirthDate;
}
}
I have also modified the DBML XML as in:
other post on stackoverflow
but also without result.
Is that possible by using LINQ or do I have to overwrite a getter which costs roundtrip to the server?
According to the Remarks section on this MSDN page, the Expression property of the ColumnAttribute is used when calling CreateDatabase, so it won't work the way you intend unless you created your database with Linq to Sql.
You can create a Linq query that selects the various columns and calls the db function in one statement like this (based on MSDN example):
var qry = from person in db.Persons
select new {
FirstName = person.FirstName,
LastName = person.LastName,
BirthDate = person.BirthDate,
Dob = db.usrFn_UTCToLocalTime(person.BirthDate)
};
This projects into an anonymous type, but you could use a non-anonymous type as well. For the sake of the example, I've got a table named Person with FirstName, LastName, and BirthDate columns, and a user defined scalar function named usrFn_UTCToLocalTime. The sql statement sent to the server is:
SELECT [t0].[FirstName], [t0].[LastName], [t0].[BirthDate], CONVERT(DateTime,[dbo].[usrFn_UTCToLocalTime]([t0].[BirthDate])) AS [Dob]
FROM [dbo].[Person] AS [t0]
As I was suggesting in the question, for now I have overwritten the get method so I have:
get
{
using (var context = DB.Data.DataContextFactory.CreateContext())
{
return context.usrFn_UTCToLocalTime(_BirthDate);
}
//return this._BirthDate;
}
But with every access to the property, roundtrip is made - which is not my intention but gives a proper result.
I leave the question still open