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
Related
I don't understand the use of this and how the object is created or constructor is called and what's happening in the below code.
class Person {
var children: MutableList<Person> = mutableListOf<Person>();
constructor(parent: Person) {
parent.children.add(this)
}
}
class Person(val name: String) {
var children: MutableList<Person> = mutableListOf<Person>();
constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}
source : https://kotlinlang.org/docs/reference/classes.html
: this(...) call means call to another constructor, so each object was constructed with the primary constructor too.
class Person /* a */ (val name: String) {
var children: MutableList<Person> = mutableListOf<Person>();
/* b */ constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}
In this example, there is primary constructor after a, and secondary one after b, so there are two ways to instantiate this class.
Call the primary constructor: Person("abc").
Call the secondary constructor: Person("abc", Person("dfg")), and it is guaranteed that both primary and secondary constructor will be called.
I get following error for PhoneAppItem:
Parcelable should have primary constructor
How can I solve this? I have a base class with constructors that don't share a basic constructor, so how can I extend such a class and make it parcelable in kotlin?
Ideas
add the primary constructor => then I get the exception, that my secondary constructors in PhoneAppItem do not call the primary constructor, IDE says following:
primary constructor call expected
Code
Here's the basic barebone code (I have more than 2 simple constructors!!) that creates this error:
abstract class AbstractPhoneAppItem : Parcelable {
constructor() {
}
constructor(packageName: String?) {
this.packageName = packageName
}
}
#Parcelize
class PhoneAppItem : AbstractPhoneAppItem {
constructor() : super()
constructor(packageName: String?) : super(packageName)
}
Which version of Kotlin are you using? Have you tried:
abstract class AbstractPhoneAppItem(): Parcelable {
private var packageName: String? = null
constructor(packageName: String?): this() {
this.packageName = packageName
}
}
#Parcelize
class PhoneAppItem : AbstractPhoneAppItem {
constructor() : super()
constructor(packageName: String?) : super(packageName)
}
You could create a primary constructor with an optional parameter:
abstract class AbstractPhoneAppItem (var packageName: String? = null) : Parcelable {
}
This way, you can use it without any params (so your packageName is null, or the default value you want) or use it with a parameter. Your item class then can call the superclass's primary constructor.
You'll have to use a custom parceler:
#Parcelize
class PhoneAppItem : AbstractPhoneAppItem {
constructor() : super()
constructor(packageName: String?) : super(packageName)
companion object : Parceler<PhoneAppItem> {
// modify logic to taste
override fun PhoneAppItem.writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(packageName)
}
override fun create(parcel: Parcel): PhoneAppItem {
return PhoneAppItem(parcel.readString())
}
}
}
Otherwise, #Parcelize really needs a primary constructor to know what to do.
I want to annotate with #JsonCreator using a primary constructor, something like this:
// error
#JsonCreator class User(
#JsonProperty("username") var username: String,
#JsonProperty("password") var password: String
) {
// ...
}
But the #JsonCreator annotation gives an error "This annotation is not applicable to target 'class'".
Using a secondary constructor works, but is it the only (or best) way?:
// works, but is there a better way?
class User #JsonCreator constructor(
#JsonProperty("username") var username: String,
#JsonProperty("password") var password: String
) {
// ...
}
What you describe here:
class User #JsonCreator constructor(
#JsonProperty("username") var username: String,
#JsonProperty("password") var password: String
) {
// ...
}
is actually explicitly specifying the primary constructor. You can differentiate the primary from the secondary by looking at the class declaration:
class User constructor(/** **/) { // <-- primary
constructor(/** ... **/) { // <-- secondary
}
}
if the constructor is part of the class header it is a primary constructor, if it is part of the class declaration (it is after the {) it is a secondary one.
I'm new to Kotlin, so I have this interface.
interface User {
var nickName : String
}
Now I want to create a class PrivateUser that implements this interface. I have also to implement the abstract member nickName.
Via constructor it's very simple
class PrivateUser(override var nickName: String) : User
However when I try to implement member inside the class Idea generates me this code
class Button: User {
override var nickName: String
get() = TODO("not implemented")
set(value) {}
}
It's confusing to me how to implement it further.
Properties must be initialized in Kotlin. When you declare the property in the constructor, it gets initialized with whatever you pass in. If you declare it in the body, you need to define it yourself, either with a default value, or parsed from other properties.
Some examples:
class Button : User {
override var nickname = "Fred"
}
class Button(val firstName: String, val lastName: String) : User {
override var nickname = "${firstname[0]}$lastname"
}
The code generated by IDEA is useful if you want a non-default getter and/or setter, or if you want a property without a backing field (it's getter and setter calculate on the fly when accessed).
More examples:
class Button : User {
override var nickname = "Fred"
get() = if (field.isEmpty()) "N/A" else field
set(value) {
// No Tommy
field = if (value == "Tommy") "" else value
}
}
class Button(val number: Int) : User {
var id = "$number"
private set
override var nickname: String
get() {
val parts = id.split('-')
return if (parts.size > 1) parts[0] else ""
}
set(value) {
field = if (value.isEmpty()) "$number" else "$value-$number"
}
}
I am new to Kotlin. I want to write a class which holds data. I want two constructor. What i want is something like this
class InstituteSearchDetails (var centerId: String) {
lateinit var centerId: String;
lateinit var instituteName: String;
lateinit var city: String;
init {
this.centerId=centerId
}
constructor( instituteName: String, city: String)
{
this.instituteName=instituteName;
this.city=city;
}
}
But on Secondary constructor line it says primary constructor call is required. I know some delegation is required which call primary constructor form there. I cant call primary constructor from here. I am sorry if i am doing some silly mistake. I am new to this thing
From the doc:
If the class has a primary constructor, each secondary constructor
needs to delegate to the primary constructor, either directly or
indirectly through another secondary constructor(s). Delegation to
another constructor of the same class is done using the this keyword:
Example:
class Person(val name: String) {
constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}
Your code:
constructor( instituteName: String, city: String) : this("centerId"){
this.instituteName=instituteName;
this.city=city;
}
But it doesn't look like you have the centerId value in the secondary constructor.
You can have two secondary constructors:
class InstituteSearchDetails {
lateinit var centerId: String;
lateinit var instituteName: String;
lateinit var city: String;
constructor(centerId: String) {
this.centerId = centerId
}
constructor( instituteName: String, city: String)
{
this.instituteName=instituteName;
this.city=city;
}
}
But be aware that, for instance, centerId wouldn't have been initialized if you use the second constructor and you will get an exception (UninitializedPropertyAccessException) if you try to access the centerId in that case.
Edit:
This is not possible in data class because data class requires a primary constructor with at least one val or var. If you have the primary constructor, then your secondary constructor should delegate to the primary constructor as well. Perhaps you can have all properties in a single primary constructor of a data class but with nullable properties. Or see Sealed class.
sealed class InstituteSearchDetails {
data class InstituteWithCenterId(val centerId: String): InstituteSearchDetails()
data class InstituteWithNameAndCity(val name: String, val city: String): InstituteSearchDetails()
}
fun handleInstitute(instituteSearchDetails: InstituteSearchDetails) {
when (instituteSearchDetails) {
is InstituteSearchDetails.InstituteWithCenterId -> println(instituteSearchDetails.centerId)
is InstituteSearchDetails.InstituteWithNameAndCity -> println(instituteSearchDetails.name)
}
}