I am trying to implement an interface in Kotlin, and basically what I am trying to do is say: "If the value atb if null/not set, then set it to bugId".
Here is the Kotlin interface I am trying to implement
interface Incident {
#get:NotNull
#get:Past
val incidentDate: LocalDateTime
#get:NotNull
val source: String
#get:NotNull
#get:Positive
val bugId: Int
#get:NotNull
#get:Pattern(
regexp = "FOO|BAZ",
message = "'\${validatedValue}' not allowed. Must be one of : {regexp}"
)
val pillar: String
#get:Positive
val atb: Int
}
And here is how I am trying to implement it:
data class PSR (
#JsonDeserialize(using = DoubleToInt::class)
override val bugId: Int,
#JsonDeserialize(using = EpochToLocalDateTime::class)
override val incidentDate: LocalDateTime,
override val pillar: String,
#JsonDeserialize(using = DoubleToInt::class)
override val atb: Int = bugId,
) : Incident {
override val source: String = "PSR"
}
The problem is I am trying to deserialize this data from JSON and when I do, I get the following (understandable) error:
Instantiation of [simple type, class com.company.models.spreadsheets.psr.PSR] value failed for JSON property atb due to missing (therefore NULL) value for creator parameter atb which is a non-nullable type
at [Source: UNKNOWN; byte offset: #UNKNOWN] (through reference chain: com.company.models.spreadsheets.psr.PSR["atb"])
I've given the equivalent java code below that achieves what I want. How can I do this in Kotlin (and keep the benefits of a data class)?
class PERF implements Incident {
private final LocalDateTime incidentDate;
private final Integer bugId;
private final String pillar;
private final Integer atb;
public PERF(LocalDateTime incidentDate, Integer bugId, String pillar, Integer atb) {
this.bugId = bugId;
this.atb = atb == null ? bugId: atb;
this.incidentDate = incidentDate;
this.pillar = pillar;
}
#NotNull
#Override
public LocalDateTime getIncidentDate() {
return incidentDate;
}
#Override
public int getBugId() {
return bugId;
}
#NotNull
#Override
public String getPillar() {
return pillar;
}
#Override
public int getAtb() {
return atb;
}
#Override
public String getSource() {
return "PERF"
}
}
Assuming you are using Jackson, just create a secondary constructor and mark that as JsonCreator instead. Have the secondary constructor delegate to the primary one.
data class PSR (
override val bugId: Int,
override val incidentDate: LocalDateTime,
override val pillar: String,
override val atb: Int = bugId,
) : Incident {
#JsonCreator
constructor(
#JsonDeserialize(using = DoubleToInt::class)
#JsonProperty("bugId")
bugId: Int,
#JsonDeserialize(using = EpochToLocalDateTime::class)
#JsonProperty("incidentDate")
incidentDate: LocalDateTime,
#JsonProperty("pillar")
pillar: String,
#JsonDeserialize(using = DoubleToInt::class)
#JsonProperty("atb")
atb: Int?,
): this(bugId, incidentDate, pillar, atb ?: bugId)
override val source: String = "PSR"
}
Related
I'm facing this Error when I used the suspend keyword with the insert() method in Kotlin. I researched a lot but can't understand the best solution that how I resolve this. Kindly if you people have any best solution against this error. Please suggest/help me, how I can resolve this.
Error Text
error: Type of the parameter must be a class annotated with #Entity or a collection/array of it.
kotlin.coroutines.Continuation<? super java.lang.Long> continuation);
error: Not sure how to handle insert method's return type.
public abstract java.lang.Object insertConfCard(#org.jetbrains.annotations.NotNull()
Execution failed for task ':app:kaptDebugKotlin'.
> A failure occurred while executing org.jetbrains.kotlin.gradle.internal.KaptWithoutKotlincTask$KaptExecutionWorkAction
> java.lang.reflect.InvocationTargetException (no error message)
My DAO
#Dao
interface ConfCardDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertConfCard(confCard: ConfCard): Long
}
Generated DAO
import java.lang.System;
#androidx.room.Dao()
#kotlin.Metadata(mv = {1, 7, 1}, k = 1, d1 = {"\u0000\u0018\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0010\t\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0002\bg\u0018\u00002\u00020\u0001J\u0019\u0010\u0002\u001a\u00020\u00032\u0006\u0010\u0004\u001a\u00020\u0005H\u00a7#\u00f8\u0001\u0000\u00a2\u0006\u0002\u0010\u0006\u0082\u0002\u0004\n\u0002\b\u0019\u00a8\u0006\u0007"}, d2 = {"Lcom/example/stid_settings_kotlin/datamodel/data/local/ConfCardDao;", "", "insertConfCard", "", "confCard", "Lcom/example/stid_settings_kotlin/datamodel/data/local/ConfCard;", "(Lcom/example/stid_settings_kotlin/datamodel/data/local/ConfCard;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;", "app_debug"})
public abstract interface ConfCardDao {
#org.jetbrains.annotations.Nullable()
#androidx.room.Insert(onConflict = androidx.room.OnConflictStrategy.REPLACE)
public abstract java.lang.Object insertConfCard(#org.jetbrains.annotations.NotNull()
com.example.stid_settings_kotlin.datamodel.data.local.ConfCard confCard, #org.jetbrains.annotations.NotNull()
kotlin.coroutines.Continuation<? super java.lang.Long> continuation);
}
My build.gradle(:app)
dependencies {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.5.1'
implementation 'com.google.android.material:material:1.7.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
// Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
// implementation "com.squareup.okhttp3:okhttp:4.9.0"
// Coroutines
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1'
// Coroutine Lifecycle Scopes
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.5.1"
// Room
implementation "androidx.room:room-runtime:2.3.0"
kapt "androidx.room:room-compiler:2.3.0"
// optional - Kotlin Extensions and Coroutines support for Room -- use room with coroutine
implementation "androidx.room:room-ktx:2.3.0"
}
My build.gradle(:Project)
plugins {
id 'com.android.application' version '7.2.0' apply false
id 'com.android.library' version '7.2.0' apply false
id 'org.jetbrains.kotlin.android' version '1.7.10' apply false
}
task clean(type: Delete) {
delete rootProject.buildDir
}
My Entity Class
#Entity(tableName = "confcard", indices = [Index(value = ["confName"], unique = true)])
class ConfCard {
#PrimaryKey(autoGenerate = true)
var id: Int = 0
#NotNull
var confName: String
#NotNull
var name: String
#NotNull
var pid: String
#NotNull
var pver: String
#NotNull
var data: String
#NotNull
var kNew: String
#NotNull
var kCur: String
#NotNull
var uuid: String
#NotNull
var type: String // ScbvsOCB
#NotNull
var tkNeeded: String
#NotNull
var originatingServer: String
constructor() {
this.id = 0
this.confName = ""
this.name = ""
this.pid = ""
this.pver = ""
this.data = ""
this.kNew = ""
this.kCur = ""
this.uuid = ""
this.type = ""
this.originatingServer = ""
this.tkNeeded = ""
}
constructor(
ConfName: String,
Name: String,
PartId: String,
PartVer: String,
Data: String,
KNew: String,
KCur: String,
UUID: String,
CardType: String,
OriginatingServer: String,
TkNeeded: String,
) {
this.confName = ConfName
this.name = Name
this.pid = PartId
this.pver = PartVer
this.data = Data
this.kNew = KNew
this.kCur = KCur
this.uuid = UUID
this.type = CardType
this.originatingServer = OriginatingServer
this.tkNeeded = TkNeeded
}
#JvmName("getId1")
fun getId(): Int = id
#JvmName("setId1")
fun setId(id: Int) {
this.id = id
}
#JvmName("getConfName1")
fun getConfName(): String = confName
#JvmName("setConfName1")
fun setConfName(confName: String) {
this.confName = confName
}
#JvmName("getName1")
fun getName(): String = name
#JvmName("setName1")
fun setName(name: String) {
this.name = name
}
fun getPartId(): String = pid
fun setPartId(pid: String) {
this.pid = pid
}
fun getPartVer(): String = pver
fun getPartVer(pver: String) {
this.pver = pver
}
#JvmName("getData1")
fun getData(): String = data
#JvmName("setData1")
fun setData(data: String) {
this.data = data
}
#JvmName("getKNew1")
fun getKNew(): String = kNew
#JvmName("setKNew1")
fun setKNew(kNew: String) {
this.kNew = kNew
}
#JvmName("getKCur1")
fun getKCur(): String = kCur
#JvmName("setKCur1")
fun setKCur(kCur: String) {
this.kCur = kCur
}
fun getUUID(): String = uuid
fun setUUID(uuid: String) {
this.uuid = uuid
}
fun getCardType(): String = type
fun setCardType(type: String) {
this.type = type
}
#JvmName("getOriginatingServer1")
fun getOriginatingServer(): String = originatingServer
#JvmName("setOriginatingServer1")
fun setOriginatingServer(originatingServer: String) {
this.originatingServer = originatingServer
}
#JvmName("getTkNeeded1")
fun getTkNeeded(): String = tkNeeded
#JvmName("setTkNeeded1")
fun setTkNeeded(tkNeeded: String) {
this.tkNeeded = tkNeeded
}
}
how to add 2 or more constructors ?? i know the use of data class in kotlin, but i am not getting what exactly this keyword is in kotlin and why we have to put anything inside this?
public class Model {
public String mId, mTitle, mDesc;
public Model() {
}
public Model(String mId, String mTitle, String mDesc) {
this.mId = mId;
this.mTitle = mTitle;
this.mDesc = mDesc;
}
public String getmId() {
return mId;
}
public void setmId(String mId) {
this.mId = mId;
}
public String getmTitle() {
return mTitle;
}
public void setmTitle(String mTitle) {
this.mTitle = mTitle;
}
public String getmDesc() {
return mDesc;
}
public void setmDesc(String mDesc) {
this.mDesc = mDesc;
}
}
I know kotlin but not that much.
how i changed
data class model_for_single_row (val mId:String,val mTitle:String,val mDesc:String){
constructor():this()
}
it gives me error to put something inside this. why we use this here and why we should put, and what we should put?
Default value of String in java is null, which isn't the case in Kotlin.
You can make fields nullable and attach their defualt values to null:
data class model_for_single_row(
val mId: String? = null,
val mTitle: String? = null,
val mDesc: String? = null
)
You can call it like:
model_for_single_row()
model_for_single_row("id")
model_for_single_row("id", "title")
model_for_single_row("id", "title", "desc")
model_for_single_row(mTitle = "title")
Parameters not supplied will be null here.
Hope it could be useful, the this keyword is used concerning constructors inside a class :
1 - to delegate from a secondary constructor to the primary constructor, like this :
class Car(val id: String, val type: String) {
constructor(id: String): this(id, "unknown")
}
2 - to delegate from a secondary constructor to another secondary constructor where no primary constructor is present; so here in this example there is a child class with more than one secondary constructor derived from a parent class with more than one secondary constructor and no primary constructor as well:
fun main(args: Array<String>) {
val p1 = AuthLog("Bad Password")
}
open class Log {
var data: String = ""
var numberOfData = 0
constructor(_data: String) {
}
constructor(_data: String, _numberOfData: Int) {
data = _data
numberOfData = _numberOfData
println("$data: $numberOfData times")
}
}
class AuthLog: Log {
constructor(_data: String): this("From AuthLog -> + $_data", 10) {
}
constructor(_data: String, _numberOfData: Int): super(_data, _numberOfData) {
}
}
I am new to kotlin and building a quiz app. I don't understand How do I write this java code into Kotlin? Especially the getters and setters? Also how to create both default and parameterized constructor in Kotlin?
What I did is This:
class Question {
var question: String
var opt1: String
var opt2: String
var opt3: String
var answerno: Int
constructor(question: String, opt1: String, opt2: String, opt3: String, answerno: Int) {
this.question = question
this.answerno = answerno
this.opt1 = opt1
this.opt2 = opt2
this.opt3 = opt3
}
}
Java Code Here:
public class Question {
private String question;
private String option1;
private String option2;
private String option3;
private int answerNr;
public Question() {
}
public Question(String question, String option1, String option2, String option3, int answerNr) {
this.question = question;
this.option1 = option1;
this.option2 = option2;
this.option3 = option3;
this.answerNr = answerNr;
}
public String getQuestion() {
return question;
}
public void setQuestion(String question) {
this.question = question;
}
public String getOption1() {
return option1;
}
public void setOption1(String option1) {
this.option1 = option1;
}
public String getOption2() {
return option2;
}
public void setOption2(String option2) {
this.option2 = option2;
}
public String getOption3() {
return option3;
}
public void setOption3(String option3) {
this.option3 = option3;
}
public int getAnswerNr() {
return answerNr;
}
public void setAnswerNr(int answerNr) {
this.answerNr = answerNr;
}
}
In kotlin getters and setters are automatically generated by the compilers, you can write all the variables into the constructor. This will generate all the getter and setters for the fields here.
class Question (
var question: String
var opt1: String
var opt2: String
var opt3: String
var answerno: Int
)
If you want to provide a custom getter or setter, just create property inside the class:
class Question (
question: String
var opt1: String
var opt2: String
var opt3: String
var answerno: Int
) {
var question = question
get() {
// getter code (use field variable here to access this variable)
}
set(value) {
// assign value to field variable, like `field = value`
}
}
You don't need to assign getter setter as it is there by default. You can access them using question.option1.
You can use like this,
class Question(
var question: String = "default value",
var option1: String = "default value",
var option2: String = "default value",
var option3: String = "default value",
var answerNr: Int = 0
)
This way you can assign default values.
I would suggest you read up the kotlin documentation to create a model class. It has some good explanations here.
https://kotlinlang.org/docs/reference/properties.html
Have a look at following plain object class.
class Question {
var question:String
var option1:String
var option2:String
var option3:String
var answerNr:Int = 0
constructor() {}
constructor(question:String, option1:String, option2:String, option3:String, answerNr:Int) {
this.question = question
this.option1 = option1
this.option2 = option2
this.option3 = option3
this.answerNr = answerNr
}
}
If you are looking for the data class then try following way
data class Question (
var question:String,
var option1:String,
var option2:String,
var option3:String,
var answerNr:Int = 0
) {
}
get() = field // getter
set(value) { // setter
field = value
}
Eg:-
get() = question
set(value){
question = value
}
You can avoid getters/setters boilerplate with Kotlin Data classes.
You also can replace constructors with Fabric methods in Companion objects. More on this topic you could find in the book Effective Java "Item 1: Consider static factory methods instead of constructors " And here.
Another useful data classes feature is copy-methods. With them you can avoid creating mutable object. Immutable objects have a lot advantages over mutable. For example it's safe to use immutable objects in multithreading programming.
data class Question(
val question: String,
val opt1: String,
val opt2: String,
val opt3: String,
val answerno: Int
) {
companion object {
// Fabric methods in companion objects as replace to secondary constructors
fun fromQuestion(question: String) = Question(
question = question,
opt1 = "",
opt2 = "",
opt3 = "",
answerno = 0
)
}
}
// Using Companion object fabric method
val myQuestion = Question.fromQuestion("question")
// Avoid mutable objects with copy method
val newQuestion = myQuestion.copy(
question = "New question"
)
If you want absolutely the same structure as in Java class you presented here is the converted solution with all "nullabilities":
class Question {
var question: String? = null
var option1: String? = null
var option2: String? = null
var option3: String? = null
var answerNr = 0
constructor() {}
constructor(
question: String?,
option1: String?,
option2: String?,
option3: String?,
answerNr: Int
) {
this.question = question
this.option1 = option1
this.option2 = option2
this.option3 = option3
this.answerNr = answerNr
}
}
Thanks to #gidds, for pointing out that Kotlin by default generates getters and setters (for mutable properties) for each class property.
Properties are not private and declared as var because:
- (var) your java code had getters and setters for each property;
- (not private) your getters and setters simply return and set values without changing them.
If for example getQuestion and setQuestion used question value to perform some calculations and returned the result of calculations your converted class would look like this:
class Question {
private var question: String? = null
var option1: String? = null
var option2: String? = null
var option3: String? = null
var answerNr = 0
constructor() {}
constructor(
question: String?,
option1: String?,
option2: String?,
option3: String?,
answerNr: Int
) {
this.question = question
this.option1 = option1
this.option2 = option2
this.option3 = option3
this.answerNr = answerNr
}
fun getQuestion(): String {
return question + "value"
}
fun setQuestion(question: String) {
this.question = question + "value"
}
}
That is the most direct conversion.
I have Java POJO class like this:
class Topic {
#SerializedName("id")
long id;
#SerializedName("name")
String name;
}
and I have a Kotlin data class Like this
data class Topic(val id: Long, val name: String)
How to provide the json key to any variables of the kotlin data class like the #SerializedName annotation in java variables ?
Data class:
data class Topic(
#SerializedName("id") val id: Long,
#SerializedName("name") val name: String,
#SerializedName("image") val image: String,
#SerializedName("description") val description: String
)
to JSON:
val gson = Gson()
val json = gson.toJson(topic)
from JSON:
val json = getJson()
val topic = gson.fromJson(json, Topic::class.java)
Based on answer of Anton Golovin
Details
Gson version: 2.8.5
Android Studio 3.1.4
Kotlin version: 1.2.60
Solution
Create any class data and inherit JSONConvertable interface
interface JSONConvertable {
fun toJSON(): String = Gson().toJson(this)
}
inline fun <reified T: JSONConvertable> String.toObject(): T = Gson().fromJson(this, T::class.java)
Usage
Data class
data class User(
#SerializedName("id") val id: Int,
#SerializedName("email") val email: String,
#SerializedName("authentication_token") val authenticationToken: String) : JSONConvertable
From JSON
val json = "..."
val object = json.toObject<User>()
To JSON
val json = object.toJSON()
You can use similar in Kotlin class
class InventoryMoveRequest {
#SerializedName("userEntryStartDate")
#Expose
var userEntryStartDate: String? = null
#SerializedName("userEntryEndDate")
#Expose
var userEntryEndDate: String? = null
#SerializedName("location")
#Expose
var location: Location? = null
#SerializedName("containers")
#Expose
var containers: Containers? = null
}
And also for nested class you can use same like if there is nested object. Just provide Serialize name for the Class.
#Entity(tableName = "location")
class Location {
#SerializedName("rows")
var rows: List<Row>? = null
#SerializedName("totalRows")
var totalRows: Long? = null
}
so if get response from the server each key will map with JOSN.
Alos, convert List to JSON:
val gson = Gson()
val json = gson.toJson(topic)
ndroid convert from JSON to Object:
val json = getJson()
val topic = gson.fromJson(json, Topic::class.java)
I have one java object which is using enum type
public class Deal{
public enum PriceType {
fixed, hour, month, year
}
#Element(name = "price-type", required = false)
private PriceType priceType;
}
this object is get populated from some API and I am retriving this in database object having string type variable
MyDeal{
private String priceType;
public String getPriceType() {
return priceType;
}
public void setPriceType(String priceType) {
this.priceType = priceType == null ? null : priceType.trim();
}
}
why can't I set my database object like
List<Deal>deals = dealResource.getAll();
MyDeal myDeal = new myDeal();
for (Deal deal : deals) {
myDeal.setPriceType(deal.getPriceType());
}
You can't set a PriceType to a String directly. You need to do something like this
for (Deal deal : deals) {
myDeal.setPriceType(deal.getPriceType().name()); // name() will get that name of the enum as a String
}
Though the for loop looks seriously flawed. You'll just keep overriding the priceType in myDeal over and over again.
Add Enumerated to the property
#Enumerated(EnumType.STRING)
#Element(name = "price-type", required = false)
private PriceType priceType;