How to write getters and setters in kotlin? - java

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.

Related

Setting a default value in a Kotlin data class conditionally

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

error: Type of the parameter must be a class annotated with #Entity or a collection/array of it

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

Parameterizing strings in a model not working

I'm trying to parameterize a string and set that string as the result of a model:
SomePanel.java
public SomePanel( String id, IModel<Person> personModel)
{
tallLabel = new Label( "height", new LoadableDetachableModel() {
pubic String load() {
Person person = personModel.getObject();
boolean isTall = apiCallToCheckIfTall( person );
// 'name' is a property on PersonModel
String name = person.getName();
String tallString = MessageFormat.format(getString("Tall.Label"), name );
String shortString = MessageFormat.format(getString("Short.Label"), name );
return isTall ? tallString : shortString;
}
});
add(tallLabel);
}
Text.properties
Tall.Label = ${name} is tall.
Short.Label = ${name} is short.
I tried implementing a solution but contact.getName() produces an error. My understanding is that personModel.getObject() would give me the actual object (which has getter getName defined) so not sure why this would produce an error.
MessageFormat uses indexed parameters, so you probably mixed up some technologies here.
Here's the simplest solution using Wicket's resource messages with names parameters:
return getString(isTall ? "Tall.Label" : "Short.Label", personModel)
I managed to get it to work with:
SomePanel.java
public SomePanel( String id, IModel<Person> personModel)
{
tallLabel = new Label( "height", new LoadableDetachableModel() {
public String load() {
Person person = personModel.getObject();
boolean isTall = apiCallToCheckIfTall( person );
PersonGetter getter = new PersonGetter ( personModel );
String name = getter.getName();
String RTStringModel = MessageFormat.format( getString("Tall.Label"), person.getName() );
String StringModel = MessageFormat.format( getString("Short.Label"), person.getName() );
return isTall ? RTStringModel : StringModel;
}
});
add(tallLabel);
}
...
private class NameGetter implements Serializable
{
private final IModel<Person> model;
public NameGetter( final IModel<Person> personModel )
{
this.model = person;
}
public String getName()
{
return getFormattedLegalName( this.model.getObject() );
}
}
public static final String getFormattedLegalName( Person person )
{
if ( person == null )
{
return "";
}
else
{
return person.getName();
}
}
Text.properties
Tall.Label = {0} is tall.
Short.Label = {0} is short.
This seems to be a bit too much to extract a value from the model though. I couldn't get the name from the personModel directly (e.g. personModel.getObject().getName()) and went the convoluted route of having to create another class.

Add 2 or more constructors in kotlin

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

How to invoke getter method for instance that locate inside of another instance

I have some classes like below:
#Getter
#Setter
class Person{
#JsonProperty("cInfo")
private ContactInformation contactInfo;
private String name;
private String position;
}
#Getter
#Setter
class ContactInformation{
#JsonProperty("pAddress")
private Address address;
}
#Getter
#Setter
class Address{
private String street;
private String district;
}
And what I am going to do is writing an Utils method for the Person object that take one parameter which is the attributeName as String and return the getter value for this attribute.
Ex:
attributeName = name -> return person.getName()
attributeName = position -> return person.getPosition()
attributeName = cInfo.pAddress.street -> return person.getContactInfo().getAddress().getStreet()
attributeName = cInfo.pAddress.district -> return person.getContactInfo().getAddress().getDistrict()
Below is what I've done: I loop through all the fields in the Person object and check if the attributeName equal to either the JsonProperty's Name or the Field's Name then I will return this getter.
Object result;
Field[] fields = Person.class.getDeclaredFields();
for (Field field : fields) {
JsonProperty jsonProperty = field.getDeclaredAnnotation(JsonProperty.class);
if (jsonProperty != null && jsonProperty.value().equals(attributeName)) {
result = Person.class.getMethod("get" + capitalize(field.getName())).invoke(person);
} else {
if (field.getName().equals(attributeName)) {
result = person.class.getMethod("get" + capitalize(field.getName()))
.invoke(person);
}
}
}
This worked but only with the fields that locate direct in the Person class, ex: name, position. With the fields inside of contactInfo or address I am still getting stuck there. Can anyone give me some hint here how can I do it?
Thank you!
Because path like a.b.c related to different objects. So you need to. split by point and for each token call get and use obtained result for next token
UPDATE: something like:
private static Object invkGen(Object passedObj, String attributeName) throws Exception {
final String[] split = attributeName.split("\\.");
Object result = passedObj;
for (String s : split) {
if (result == null) {
break;
}
result = invk(result, s);
}
return result;
}
private static Object invk(Object passedObj, String attributeName) throws Exception {
Object result = null;
final Field[] fields = passedObj.getClass().getDeclaredFields();
for (Field field : fields) {
JsonProperty jsonProperty = field.getDeclaredAnnotation(JsonProperty.class);
if (jsonProperty != null && jsonProperty.value().equals(attributeName)) {
result = Person.class.getMethod("get" + capitalize(field.getName())).invoke(passedObj);
} else {
if (field.getName().equals(attributeName)) {
result = passedObj.getClass().getMethod("get" + capitalize(field.getName()))
.invoke(passedObj);
}
}
}
return result;
}

Categories