Primary constructor call expected - kotlin

this is example of inheritance in Kotlin.
open class Person() {
var _name : String = ""
var _age : Int = 0
constructor(name: String, age : Int) : this(){
_name = name
_age = age
}
}
class Student() : Person() {
var _university : String = ""
constructor (name : String, age: Int, university: String) : super(name, age){
_university = university
}
}
fun main() {
var person = Person("a", 10)
var student = Student("b", 18, "MIT")
println("${person._name} : ${person._age}")
println("${student._university}")
}
How can I resolve error "Primary constructor call expected" in this case?

If a class has a primary constructor, you must delegate the secondary constructors to the primary. Which is the case for Student class which is not doing so.
First you should remove the boilerplate of Person class following Kotlin's idioms (using optional parameters rather than constructor overloading).
// This one's completely optional to do, skip if you don't want to follow idioms
open class Person(
var name : String = ""
var age : Int = 0
)
And then Student class should have two separate constructors, because if you take a primary constructor then other constructors must delegate to it:
class Student: Person {
var university: String = ""
constructor() : super()
constructor (name: String, age: Int, university: String) : super(name, age) {
this.university = university
}
}

Related

Convert non-nullable to nullable in Kotlin

I have an Entity class as below:
#Entity(name = "Person")
#Table(name = "person")
class Person(
_firstName: String? = null,
_lastName: String?,
_address: String? = null)
) {
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "native")
#GenericGenerator(name = "native", strategy = "native")
var personId: Long? = null
var firstName = _firstName
var lastName = _lastName
var address = _address
}
The repository is as :
#Repository
interface PersonRepository : JpaRepository<Person?, Long?> {
fun findByIdAndFirstName (personId: Long, firstName: String): Person?
}
In my service class I am doing a findById(personId) and getting an Optional<Person?> in return.
While writing the testcases I am running into an issue with the Optional.
#Test
fun `test my changes`() {
every { personRepository.findById(PERSON_ID) } answers { Optional.of(getPerson())}
}
private fun getPerson(): Person {
return Person(_firstName = "First Name", _lastName = "Last Name", _address = "Address")
}
Since my getPerson() directly return the Person entity via a constructor the response is of type Person which is non-nullable.
But personRepository.findById(PERSON_ID) expects a nullable type Optional<Person?>.
Getting this as compilation error in my test class:
Type mismatch.
Required:
Optional<Person?>!
Found:
Optional<Person>
How can I fix this issue ?
You can cast it to nullable:
#Test
fun `test my changes`() {
every { personRepository.findById(PERSON_ID) } answers {
Optional.of(getPerson()) as Optional<Person?>
}
}
However (as Sweeper said in the comment) the type Optional<Person?> doesn't make much sense, and it looks like your JpaRepository should be parameterized on non-nullables. If it was, then findById would return Optional<Person> and you wouldn't have this issue.

Cannot save data model that contains List<Model> with Room ORM Kotlin

I have a problem with Room ORM working on Kotlin. My task is having ability to save and get data models RouteTemplateModel, that contains list of addresses of type AddressModel and object of class RouteModel that contains title of the specific route. Here is my code:
AddressModel.kt
#Entity(foreignKeys = arrayOf(
ForeignKey(entity = RouteModel::class,
parentColumns = arrayOf("routeId"),
childColumns = arrayOf("parentId"))))
data class AddressModel(
#PrimaryKey(autoGenerate = true)
var addressId: Long,
var parentId: Long,
var street: String,
var house: String,
var entrance: String,
var title: String){
constructor(): this(0, 0, "", "", "", "")
}
RouteModel.kt
#Entity
data class RouteModel(
#PrimaryKey(autoGenerate = true)
var routeId: Long,
var title: String) {
constructor() : this(0, "")
}
Here is my simple models, I found in documentation of Room that for creating relations between models I need to use #ForeignKey and #Relation
So with code samples in doc and tutorials I create RouteTemplateModel that contains object of RouteModel and list of AddressModels. Here is the class
RouteTemplateModel
class RouteTemplateModel{
private var id: Long = 0
#Embedded
private var routeModel: RouteModel = RouteModel()
#Relation(parentColumn = "routeId", entityColumn = "parentId")
private var addressList: List<AddressModel> = listOf()
constructor()
constructor(id: Long, routeModel: RouteModel, title: String,
addressList: List<AddressModel>){
this.id = id
this.routeModel = routeModel
this.addressList = addressList
}
fun getId(): Long{
return id
}
fun getRouteModel(): RouteModel{
return routeModel
}
fun getAddressList(): List<AddressModel>{
return addressList
}
fun setId(id: Long){
this.id = id
}
fun setRouteModel(routeModel: RouteModel){
this.routeModel = routeModel
}
fun setAddressList(addressList: List<AddressModel>){
this.addressList = addressList
}
}
So what`s a problem? I am getting such errors:
Error:The columns returned by the query does not have the fields [id]
in com.innotech.webcab3kotlin.model.RouteTemplateModel even though
they are annotated as non-null or primitive. Columns returned by the
query: [routeId,title]
And
Error:Type of the parameter must be a class annotated with #Entity or
a collection/array of it.
It is a real problem, because if my trying to fix first error and annotate in RouteTemplateModel id variable to return this column too, I need annotate class as Entity (like in second error), but when I do it I am getting an error
Error:Entities cannot have relations.
Here is AppDatabase.kt
#Database(entities = arrayOf(RouteModel::class, AddressModel::class), version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun getRouteDao(): RouteDao
}
and RouteDao.kt
#Dao
interface RouteDao {
#Query("SELECT routeId, title FROM RouteModel")
fun getAll(): List<RouteTemplateModel>
#Insert
fun insertAll(vararg models: RouteTemplateModel)
#Delete
fun delete(model: RouteTemplateModel)
}
Thats really confusing. Please, help me)
Your "parentId" column is capable of holding long value only, make its type to "Text" then create a TypeConverter from "List" to String and vice a versa for reference please have a look at link .

Kotlin constructor delegation to inner data class?

We have an abstract Java class (which we can't modify) called AbstractClass that we want to implement in Kotlin. A requirement is the Kotlin implementation is serializable/deserializable to JSON using vanilla Jackson Databind. This has lead us to the following implementation:
class MyClass(private val data: MyClassData? = null) : AbstractClass<MyClassData>(MyClass::class.java, "1") {
data class MyClassData(var name: String = "", var age: Int = 0) : AbstractData
override fun getData(): MyClassData? {
return data
}
}
This class will always be used from Java and currently you can instantiate it like this (Java):
MyClass myClass = new MyClass(new MyClassData("John Doe", 25));
But we'd prefer to instantiate it like this instead:
MyClass myClass = new MyClass("John Doe", 25);
I can of course change the Kotlin code to something like this:
class MyClass(#JsonIgnore private var name: String = "", #JsonIgnore private var age: Int = 0) : AbstractClass<MyClassData>(MyClass::class.java, "1") {
data class MyClassData(var name: String = "", var age: Int = 0) : AbstractData
private var data : MyClassData? = null
init {
data = MyClassData(name, age)
}
override fun getData(): MyClassData? {
return data
}
}
but this is very verbose and kind of defeats the purpose of using Kotlin.
What I think I'd like to do is something like this (pseudo code):
class MyClass(private val data: MyClassData? = null by MyClassData) : AbstractClass<MyClassData>(MyClass::class.java, "1") {
data class MyClassData(var name: String = "", var age: Int = 0) : AbstractData
override fun getData(): MyClassData? {
return data
}
}
(note the by MyClassData in the MyClass constructor which obviously doesn't work)
I.e. I'd like to somehow destruct or delegate the constructor of MyClass to take the same arguments as MyClassData without duplicating them. Is this something you can do in Kotlin or is there another way to solve it without adding too much code?
I think your main concerns are: (a) concise external API, (b) clean internal state (for Jackson)
Secondary Constructor
This is pretty lean:
class MyClass internal constructor(private val data: MyClassData)
: AbstractClass<MyClass>(MyClass::class.java, "1") {
data class MyClassData(var name: String, var age: Int) : AbstractData
constructor(name: String, age: Int) : this(MyClassData(name, age))
override fun getData(): MyClassData? = data
}
Simple API without creating extra fields (though I think this syntax is misleading):
val myClass = MyClass("John Doe", 25)
Pass-Thru Params & Initializer:
This was my first idea: directly pull out the outer class' params (though I now think the secondary constructor is nicer since it doesn't pollute the outer class):
class MyClass(#JsonIgnore private val name: String, #JsonIgnore private val age: Int)
: AbstractClass<MyClass>(MyClass::class.java, "1") {
data class MyClassData(var name: String, var age: Int) : AbstractData
private val data = MyClassData(this#MyClass.name, this#MyClass.age)
override fun getData(): MyClassData? = data
}
...again, same API:
val myClass = MyClass("John Doe", 25)
Self-Factory
This approach has potentially more descriptive syntax:
class MyClass(private val data : MyClassData)
: AbstractClass<MyClass>(MyClass::class.java, "1") {
data class MyClassData(var name: String, var age: Int) : AbstractData
companion object Factory {
fun create(name: String, age: Int) = MyClass(MyClassData(name, age))
}
override fun getData(): MyClassData? = data
}
This can be called like this:
val myClass = MyClass.Factory.create("John Doe", 25)
Conceptual Syntax: Structuring (DOES NOT EXIST)
I kind of like the idea of a 'structuring' syntax for method arguments that would group inputs into an object (the opposite of destructuring); a bit like varargs (i.e. syntactic sugar):
class MyClass(
private val data: (name: String, age: Int) : MyClassData(name, age)
) { ... }
This could be called in either of two ways:
val myClass1 = MyClass(MyClassData("John Doe", 25))
val myClass2 = MyClass("John Doe", 25)
In practice it's a rare requirement, and easily manageable with explicit overloads for just a few extra chars, so I don't think it will ever happen.

What is the difference between properties and parameters in Kotlin?

Here is a simple example of a class with some code (properties) inside the bracket
class Person(firstName: String) {
....
}
Now here is an example of a function with some code (parameters) inside the bracket
fun double(x: Int) {
...
}
I know this is a fundamental question but I am quite confused as a beginner.
You pass parameters to functions and constructors, and classes have properties.
The constructor of the Person class in your example has a single parameter, and so does the double function. In this case, the firstName parameter is not a property!
To make it a property, you have to declare it so:
class Person(firstName: String) {
val firstName : String = firstName
}
Kotlin allows this to be shorter, which makes the firstName parameter serve as a property:
class Person(val firstName: String)
First, your firstName also is a parameter rather than a property in Person class.
// v-- a parameter declared on primary constructor
class Person(firstName: String)
you can access a parameter declared on primary constructor in init block or property/field declaration, for example:
class Person(firstName: String){
val first:String
init{ first=firstName }
}
class Person(firstName: String){
val first:String = firstName
}
class Person(firstName: String){
#JvmField val first:String = firstName
}
to make the firstName to a property you can using keyword val or var, for example:
// v--- immutable property
class Person(val firstName: String)
// v--- mutable property
class Person(var firstName: String)
a Kotlin property will generate getter/setter(?) and a backing field(?) to java byte code. Take an example of a mutable property to java byte code as below:
public final class Person{
private String firstName; // backing field
// v--- parameter
public Person(String firstName){ this.firstName = firstName; }
//getter
public final String getFirstName(){ return firstName; }
//setter
public final String setFirstName(String firstName){ this.firstName= firstName; }
}
a parameter only visible in function scope/constructor scope except parameter declared on primary constructor.
Note: a parameter is immutable like as java effective-final variables/parameters, so you can't reassign a parameter at all in Kotlin, for example:
fun foo(bar:String){
bar = "baz"
// ^--- compile error
}
properties and parameters are different thinks:
parameters : when we declare any function :
fun sum(a: Int, b: Int): Int {
return a + b
}
Function having two Int parameters with Int return type:
Properties and Fields:
Declaring Properties
Classes in Kotlin can have properties. These can be declared as mutable, using the var keyword or read-only using the val keyword.
class Address {
var name: String = ...
var street: String = ...
var city: String = ...
var state: String? = ...
var zip: String = ...
}
To use a property, we simply refer to it by name, as if it were a field in Java:
fun copyAddress(address: Address): Address {
val result = Address() // there's no 'new' keyword in Kotlin
result.name = address.name // accessors are called
result.street = address.street
// ...
return result
}
The full syntax for declaring a property is:
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]

Call super class constructor in Kotlin, Super is not an expression

I have two classes Entity and Account as
abstract class Entity(
var id: String? = null,
var created: Date? = Date()) {
constructor(entity: Entity?) : this() {
fromEntity(entity)
}
fun fromEntity(entity: Entity?): Entity {
id = entity?.id
created = entity?.created
return this;
}
}
and
data class Account(
var name: String? = null,
var accountFlags: Int? = null
) : Entity() {
constructor(entity: Entity) : this() {
super(entity)
}
}
Which gives me the error
Super is not an expression, it can be only used in the left-hand side of
a dot '.'
Why cannot I do that?
The following will pass the compilation error, but I am not sure if it is correct.
constructor(entity: Entity) : this() {
super.fromEntity(entity)
}
You have a couple of problems in your code.
First, this is the correct syntax, to call a super constructor from a secondary constructor:
constructor(entity: Entity) : super(entity)
Second, you can't call a super constructor from a secondary constructor if your class has a primary constructor (which your class does).
Solution 1
abstract class Entity(
var id: String,
var created: Date
)
class Account(
var name: String,
var accountFlags: Int,
id: String,
created: Date
) : Entity(id, created) {
constructor(account: Account) : this(account.name, account.accountFlags, account.id, account.created)
}
Here, the copy constructor is in the child class which just delegates to the primary constructor.
Solution 2
abstract class Entity(
var id: String,
var created: Date
) {
constructor(entity: Entity) : this(entity.id, entity.created)
}
class Account : Entity {
var name: String
var accountFlags: Int
constructor(name: String, accountFlags: Int, id: String, created: Date) : super(id, created) {
this.name = name
this.accountFlags = accountFlags
}
constructor(account: Account) : super(account) {
this.name = account.name
this.accountFlags = account.accountFlags
}
}
Here I'm only using secondary constructors in the child class which lets me delegate them to individual super constructors. Notice how the code is pretty long.
Solution 3 (most idiomatic)
abstract class Entity {
abstract var id: String
abstract var created: Date
}
data class Account(
var name: String,
var accountFlags: Int,
override var id: String,
override var created: Date
) : Entity()
Here I omitted the copy constructors and made the properties abstract so the child class has all the properties. I also made the child class a data class. If you need to clone the class, you can simply call account.copy().
You can also move your primary constructor down into the class like this:
data class Account: Entity {
constructor(): super()
constructor(var name: String? = null, var accountFlags: Int? = null): super()
constructor(entity: Entity) : super(entity)
}
Advantage of this is, compiler will not require your secondary constructor to call primary constructor.
Another option is to create companion object and provide factory method e.g.
class Account constructor(
var name: String? = null,
var accountFlags: Int? = null,
id: String?,
created: Date?
) : Entity(id, created) {
companion object {
fun fromEntity(entity: Entity): Account {
return Account(null, null, entity.id, entity.created)
}
}
}
Use this super<Entity>.fromEntity(entity) to call super class methods.
As Documentation says:
In Kotlin, implementation inheritance is regulated by the following rule: if a class inherits many implementations of the same member from its immediate superclasses, it must override this member and provide its own implementation (perhaps, using one of the inherited ones). To denote the supertype from which the inherited implementation is taken, we use super qualified by the supertype name in angle brackets, e.g. super.
constructor(entity: Entity) : this() {
super<Entity>.fromEntity(entity)
}
To know more read Overriding Rules