I'm trying to introduce Kotlin into my current project. I've decided to begin with entities, which seem to map perfectly to data classes.
For example I have a data class:
data class Video(val id: Long, val ownerId: Long, val title: String, val description: String? = null,
val imgLink: String? = null, val created: Date? = null, val accessKey: String? = null,
val views: Long? = null, val comments: Long? = null, val videoLink: String? = null): Entity
Which implements Java interface:
public interface Entity {
Long getId();
}
But for some reason compiler doesn't understand that method is implemented already:
Class 'Video' must be declared abstract or implement abstract member public abstract fun getId(): kotlin.Long! defined in net.alfad.data.Entity
Do I have to use any additional keywords for id param? What does "!" mean in the signature?
The problem here is that Kotlin loads the Java class Entity first and it sees getId as a function, not as a getter of some property. A property getter in a Kotlin class cannot override a function, so the property id is not bound as an implementation of the getId function.
To workaround this, you should override the original function getId in your Kotlin class. Doing so will result in JVM signature clash between your new function and id's getter in the bytecode, so you should also prevent the compiler from generating the getter by making the property private:
data class Video(
private val id: Long,
...
): Entity {
override fun getId() = id
...
}
Note that this answer has been adapted from here: https://stackoverflow.com/a/32971284/288456
If this is your whole data class then you're not overriding getId(). I see that you have a property called id and Kotlin should generate a getter for that but that won't be marked with the override keyword which you need to indicate that you're overriding an abstract function.
-- EDIT --
Alexander beat me to it! His answer is better anyway! ;)
Related
In Kotlin, how can an instance's read-only val field be mutated?
In the following Kotlin code, gson.fromJson(...) gets a Thing(0) from the InstanceCreator and somehow manages to mutate value from 0 to 1, then return that object. How?
import com.google.gson.GsonBuilder
import com.google.gson.InstanceCreator
class Thing(val value: Int)
fun main() {
val creator = InstanceCreator { Thing(0) }
val gson = GsonBuilder().registerTypeAdapter(Thing::class.java, creator).create()
val thing = gson.fromJson("""{"value":1}""", Thing::class.java)
println(thing.value) // 1
}
I verified that the object returned by gson.fromJson(...) is the same object provided by the InstanceCreator, so it's not creating a new instance based on the one provided by the InstanceCreator.
I also tried setting value using reflection, but didn't find a way. There was no setter available on the val field.
I also tried setting value using reflection
Well, you can do it if you use Java's reflection API, rather than Kotlin's.
Kotlin val properties translates to a private Java field with only a public getter. You can set the private field using reflection. For example:
val x = Thing(10)
val valueField = x.javaClass.getDeclaredField("value")
valueField.trySetAccessible()
valueField.setInt(x, 20)
println(x.value)
This is probably also what Gson does under the hood.
I am making a call from service A which is in Kotlin to service B which is in Java. It return me an object which contains multiple fields. One of the fields returned in the Java object is an enum. In my kotlin code I have defined a DTO which maps the returned response to kotlin. I need to map this enum to a string value in kotlin.
DTO in Java:
public class PersonDTO
{
private Long id;
private String name;
private CountryCode countryCode;
}
The CountryCode is an enum.
Data class in Kotlin:
data class PersonDTO(
val id: Long? = null,
val name: String? = null,
val countryCode: String? = null //How to map the enum to string here..???
)
Any help would be appreciated.
Answer to the edited question: How to map a Java enum to a String?
you can call name() or toString() on an enum to get a String representation of it.
name() cannot be overwritten and always returns the textual representation of the value defined in the code, while toString() can be overwritten, so it might be depending on your use case what to use. Because of the fact that name() cannot be overwritten I prefer to always use name() wich can have less side effects or unexpected behavior when working with libraries which are not under your control.
Original Answer:
1 you don't have to do this. You can use the same Java class also in Kotlin code.
2 You could just reuse the enum, like in option 1) you can reuse the Java enum in Kotlin code:
data class PersonDTO(
val id: Long? = null,
val name: String? = null,
val countryCode: CountryCode
)
3 You can write a Kotlin enum with a mapping function to create the matching instance of the enum:
enum class KotlinCountryCode {
EXAMPLE;
fun fromJavaCountryCode(input: CountryCode): KotlinCountryCode? {
if (input.name() == EXAMPLE.name) {
return EXAMPLE
}
return null
}
}
The problem is when I created the instance of my Model class and pass the non-nullable variable to the constructor, the compiler shows the error Type-mismatch.
I have fixed the type-mismatch error by making model class variable as nullable
But I couldn't understand the error shown by the compiler.
Model class
class SharedPreferenceEntry (val name:String, val dateOfBirth:Calendar, val email:String)
Helper class SharedPreferencesHelper, where I created an instance of Model class and return that instance from function
fun getPersonalInfo(): SharedPreferenceEntry { // Get data from the SharedPreferences.
val name = mSharedPreferences.getString(KEY_NAME, "")
val dobMillis =
mSharedPreferences.getLong(KEY_DOB, Calendar.getInstance().getTimeInMillis())
val dateOfBirth: Calendar = Calendar.getInstance()
dateOfBirth.setTimeInMillis(dobMillis)
val email = mSharedPreferences.getString(KEY_EMAIL, "")
// Create and fill a SharedPreferenceEntry model object.
return SharedPreferenceEntry(name, dateOfBirth, email)
}
As #sonnet commented, the use of mSharedPreferences.getString(...) will return null if the key is mapped to null. To ensure, the value of mSharedPreferences.getString(...) is non-null, change it to mSharedPreferences.getString(...) ?: "".
I have an interface with two concrete types as part of my model that I'd like to serialize/deserialize with Moshi. My issue is that I don't fully understand if PolymorphicJsonAdapterFactory is actually meant for my use case. I've looked at the samples and a few blog posts and (if I'm understanding them correctly) all of them seem to point to the fact that your interface is supposed to have a field in the interface that allows you to determine the type. I'm working in an existing codebase and so I can't easily add a field that would allow me to figure out what type it is by some string literal.
This is where I'm at with my Moshi code and I'm seeking validation on whether or not I'm using PolymorphicJsonAdapterFactory correctly. Note: I'm using java for the moshi portion of the code and for my model. My interface and it's concrete types are in kotlin
String json = ...;
Moshi moshi = new Moshi.Builder()
.add(PolymorphicJsonAdapterFactory.of(PersonInterface.java, "")
.withSubtype(BusinessPerson.java, "occupation")
.withSubtype(PolicePerson.java, "rank")
)
.build();
JsonAdapter<MyModel> jsonAdapter = moshi.adapter(MyModel.class);
MyModel myModel = jsonAdapter.fromJson(json);
Note: I'm using java for the moshi portion of the code and for my model. My interface and it's concrete types are in kotlin
MyModel is defined as the following
class MyModel {
String month;
PersonInterface person;
}
My interface and concrete classes in kotlin:
Interface PersonInterface {
val personsName: String?
}
data class BusinessPerson(
override val personsName: String,
val occupation: String?
) : PersonInterface
data class PolicePerson(
override val personsName: String,
val rank: String?
) : PersonInterface
The goal is to hopefully have Moshi be able to create a
class MyModel {
String month;
BusinessPerson person;
}
or a
class MyModel {
String month;
PolicePerson person;
}
depending on whether or not the person field contains an occupation (which means it's of type BusinessPerson) or if it contains the field rank (which means it's of type PolicePerson).
PolymorphicJsonAdapterFactory.of(PersonInterface.java, "")
.withSubtype(BusinessPerson.java, "occupation")
.withSubtype(PolicePerson.java, "rank")
means that JSON for every person is supposed to contain a key "" and "occupation" or "rank" is the value for this key, not a key name itself as you want. So e.g. it would encode a
BusinessPerson("John", "CEO")
as
{"": "occupation", "personsName": "John", "occupation": "CEO"}
I think you'll have to write your own adapter factory for this use-case if you want to avoid a discriminator field.
I'm learning scala, and to practice, I have started to convert some of my existing java classes into scala, so I can also learn about scala-java inter-op. Following is my project setup:
Scala class :
#Entity
class DemoBeanScala{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#BeanProperty
var Id: Long= _
#BeanProperty
var str: String= _
#BeanProperty
var num: Int = _
#BeanProperty
var d:Double = _
def testFunc(printText: () => Unit){
printText()
}
val pr = () => {
println("functional programming test")
}
}
Java class(extract):
#RequestMapping("/demo")
public DemoBeanScala demo(#RequestParam(value="id") Long Id, #RequestParam(value="str") String str, #RequestParam(value="num") int num, #RequestParam(value="d") double d)
{
DemoBeanScala dbs = new DemoBeanScala();
dbs.setId(123456);
dbs.setStr("sample text");
dbs.setNum(1);
dbs.setD(2.1);
dbs.testFunc(dbs.pr);
return dbs;
}
From what I have learnt, pr in DemoBeanScala class should be accessible in my java class, since no modifier is declared before it. But, after maven compilation(using scala:compile) and running java code, I'm getting an error saying that pr is private in DemoBeanScala class. What am I doing wrong?
If you look at your compiled class in javap, you will see somethign like this :
public class DemoBeanScala {
private final scala.Function0<scala.runtime.BoxedUnit> pr;
public scala.Function0<scala.runtime.BoxedUnit> pr();
// And some other stuff...
}
You will notice two important difference with Java :
Scala respects the uniform access principle, so there is no difference between calling a method without a parameter list or accessing a property. To make that work, the scala compiler will generate a public accessor method and a private field to represent a public property.
Java doesn't have methods without a parameter list: they always have at least an empty parameter list.
So, from Java, you need to access the pr property with dbs.pr(), not dbs.pr (or generate a Java-style getter with #BeanProperty, as you did for your other properties, and access it as dbs.getPr()).
When you add #BeanProperty that particular property is not visible in java class, only the generated public setters and getters are available or you may access scala like setter (pr_$eq()) and getter (pr()).
I could not find a proper reference, but the following links may helpful for you, they are not directly related to this question, but they will help you understanding.
Scala: Can I declare a public field that will not generate getters and setters when compiled?
https://issues.scala-lang.org/browse/SI-4481