Kotlin, jackson: cannot annotate #JsonCreator in primary constructor - kotlin

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.

Related

what is default value type of kotlin class constructor parameter?

class Greeter(name: String) {
fun greet() {
println("Hello, $name")
}
}
fun main(args: Array<String>) {
Greeter(args[0]).greet()
}
for above program I got this error
Unresolved reference: name
but when I add var or val
class Greeter(var name: String) {
or
class Greeter(val name: String) {
then program works fine, so why I need to add var or val to name, what is default type for constructor parameter val or var and why program gives me error when I not mention var or val
To use your value in the constructor like class Greeter(name: String), you can use init{}
class Greeter(name: String) {
var string:name = ""
init{
this.name = name
}
fun greet() {
println("Hello, $name")
}
}
or If you use val or var in the constructor it is more like class level variable and can be accessed anywhere inside the class
class Greeter(var name:String){
fun greet() {
println("Hello, $name")
}
}
The variable name can be used directly in the class then.
We can also give default values for the variables in both cases.
Adding val or var makes the parameter a property and can be accessed in the whole class.
Without this, it is only accessible inside init{}
The question is not making any sense, But the problem you are facing does make sense. In your case, the approach you are using is,
Wrong-Way:
// here name is just a dependency/value which will be used by the Greeter
// but since it is not assigned to any class members,
// it will not be accessible for member methods
class Greeter(name: String) {
fun greet(){} // can not access the 'name' value
}
Right-Way:
// here name is passed as a parameter but it is also made a class member
// with the same name, this class member will immutable as it is declared as 'val'
class Greeter(val name: String) {
fun greet(){} // can access the 'name' value
}
You can also replace val with var to make the name a mutable class member.

Initialize val property on Kotlin Data Class via Secondary constructor

class Animal {
val name: String
constructor(name: String){
this.name = name // initialized via constructor
}
}
For the above class in Kotlin I am able to initialize a val property via secondary constructor but the same is not allowed for Data classes
data class User(val name: String, val postalCode: Int) {
val email: String
constructor( email: String): this("", 1){
this.email = email // error: value can not be reassigned
}
}
What I can't understand is, where is the email property is initialized already as I haven't declared any initializes?
If your class has a primary constructor, you have to initialize all of its properties "in the primary constructor" - either by directly initializing them at their declaration:
val email = "foo#bar.com"
Or in an initializer block:
val email: String
init {
email = "foo#bar.com"
}
The compiler forces you to forward all secondary constructor calls to the to the primary constructor, and since the primary constructor already has to initialize all properties inside the class (otherwise calling it would construct a partially initialized instance, like in your code example), it wouldn't make sense to also initialize them in the body of the secondary constructor, especially for a val which cannot be reassigned.

Data class constructor with two different constructor in Kotlin

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)
}
}

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