I am using Kotlin and I have the kotlinc compiler plugin (using arrow-meta library) in place which changes the .class by adding for example new properties or new method etc during kotlin compilation time. for example, the original source Kotlin A.kt is like below
#MetaData
data class A (val x: String, val y: String)
after applying compiler plugin, the .class will be altered to (from source perspective), basically I will add implicitly MetaData into the primary constructor for all class so long as it is with annonation #MetaData in place, plus a new method fun getMetaData() generated
data class A(val x: String, val y:String, val myMeta: MetaData) {
fun getMetaData() {
//some logic check
return myMeta
}
}
now when it comes to use the new synthetic "class" manipulated as below, IntelliJ complains it cannot find resolve A (it has only the constructor with 2 parameters not 3) and cannot resolve the the synthetic method getMetaData() either.
val x = A("ba", "fo", MetaData(..))
val y = x.getMetaData()
can somebody shed some light on it?
I know lombok seems no problem with it after adding its #Getter annotation for example into Java source code, IntelliJ can recognize its getXXX method (which is generated by lombok).
I don't know how to implement the same for my case for kotlin language. please include the detailed steps if possible.
Related
I am trying to create an extension as a property and I experimented with an extension function as well, below an example for BigDecimal:
val BigDecimal.HUNDRED: BigDecimal
get() = TEN.multiply(TEN)
fun BigDecimal.HUNDRED_ONE(): BigDecimal {
return TEN.multiply(TEN)
}
It seems that Kotlin recognizes neither HUNDRED nor HUNDRED_ONE(). I am using Kotlin version 1.5.21.
Am I doing anything wrong on Kotlin version 1.5.21 that doesn't work correctly?
I have used this functionality before for lists. For instance:
fun <T> toList(list: List<T>?): List<T> {
return list ?: listOf()
}
I assume you wanted to use it like this:
val value = BigDecimal.HUNDRED
As far as I know, this is not possible in Kotlin right now. Extensions work on instances, so they're more like instance members, not static members. With your above code this will work: BigDecimal(0).HUNDRED.
I believe there is only one situation when it is possible to provide "static" extensions. If class has a companion (so it is Kotlin class) then we can add extensions to this companion. But this is not at all applicable to BigDecimal or any other Java class.
I am using #parcelize for gson
Here is my class
#Parcelize
data class CommunityModel(#SerializedName("post") val post: PostModel,
#SerializedName("is_liked") val isLiked: Boolean,
#SerializedName("post_like") val postLike: QuestionModel,
#SerializedName("polling_options") val pollingOptions: List<PollingModel>,
#SerializedName("post_polled") val postPolled: Boolean) : Parcelable
I got error Unable to invoke no-args constructor for class. Register an InstanceCreator with Gson for this type may fix this problem..
But this error only presents on older android versions (below 5.0)
I tried implementing default constructor :
constructor: this(PostModel(), true, QuestionModel(), emptyList(), true)
But it gave me java.lang.VerifyError instead
I am using retrofit2 with rxjava2 and gson converter Version 2.3
My kotlin version is 1.1.51
Is it known bug? Or did I do something wrong?
No-arg compiler plugin
The no-arg compiler plugin generates an additional zero-argument constructor for classes with a specific annotation.
The generated constructor is synthetic so it can’t be directly called from Java or Kotlin, but it can be called using reflection.
This allows the Java Persistence API (JPA) to instantiate the data class although it doesn't have the zero-parameter constructor from Kotlin or Java point of view (see the description of kotlin-jpa plugin below).
Using in Gradle
The usage is pretty similar to all-open.
Add the plugin and specify the list of annotations that must lead to generating a no-arg constructor for the annotated classes.
buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-noarg:$kotlin_version"
}
}
apply plugin: "kotlin-noarg"
Source https://kotlinlang.org/docs/reference/compiler-plugins.html
I'm trying to get annotations from Kotlin data class
package some.meaningless.package.name
import kotlin.reflect.full.memberProperties
annotation class MyAnnotation()
#MyAnnotation
data class TestDto(#MyAnnotation val answer: Int = 42)
fun main(args: Array<String>) {
TestDto::class.memberProperties.forEach { p -> println(p.annotations) }
println(TestDto::class.annotations)
}
I need to process class annotation to make a custom name serialization of GSON however no matter how I declare annotation class it never gets detected
The program always outputs
[]
[#some.meaningless.package.name.MyAnnotation()]
which means only class level annotations are present
Ok,
it seems that the culprit was, that Kotlin annotations have default #Target(AnnotationTarget.CLASS) which is not stressed enough in documentation.
After I added #Target to the annotation class it now works properly
#Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY)
annotation class MyAnnotation()
Now it prints out
[#some.meaningless.package.name.MyAnnotation()]
[#some.meaningless.package.name.MyAnnotation()]
As a side affect it will force the compiler to check that the annotation is applied as required, in current version of Kotlin, if explicit #Targetis not present only class level annotations are kept but no validity checks performed.
As Kotlin reference said as below:
If you don't specify a use-site target, the target is chosen according to the #Target annotation of the annotation being used. If there are multiple applicable targets, the first applicable target from the following: param > property > field.
To make the annotation annotated on a property, you should use site target, for example:
#MyAnnotation
data class TestDto(#property:MyAnnotation val answer: Int = 42)
However, annotations with property target in Kotlin are not visible to Java, so you should double the annotation, for example:
#MyAnnotation // v--- used for property v--- used for params in Java
data class TestDto(#property:MyAnnotation #MyAnnotation val answer: Int = 42)
I am trying to deserialize a Json string into an object of type OperationResult<String> using Jackson with Kotlin.
I need to construct a type object like so:
val mapper : ObjectMapper = ObjectMapper();
val type : JavaType = mapper.getTypeFactory()
.constructParametricType(*/ class of OperationResult */,,
/* class of String */);
val result : OperationResult<String> = mapper.readValue(
responseString, type);
I've tried the following but they do not work.
val type : JavaType = mapper.getTypeFactory()
.constructParametricType(
javaClass<OperationResult>,
javaClass<String>); // Unresolved javaClass<T>
val type : JavaType = mapper.getTypeFactory()
.constructParametricType(
OperationResult::class,
String::class);
How do I get a java class from the type names?
You need to obtain instance of Class not KClass. To get it you simply use ::class.java instead of ::class.
val type : JavaType = mapper.typeFactory.constructParametricType(OperationResult::class.java, String::class.java)
Kotlin has a few things that become a concern when using Jackson, GSON or other libraries that instantiate Kotlin objects. One, is how do you get the Class, TypeToken, TypeReference or other specialized class that some libraries want to know about. The other is how can they construct classes that do not always have default constructors, or are immutable.
For Jackson, a module was built specifically to cover these cases. It is mentioned in #miensol's answer. He shows an example similar to:
import com.fasterxml.jackson.module.kotlin.* // added for clarity
val operationalResult: OperationalResult<Long> = mapper.readValue(""{"result":"5"}""")
This is actually calling an inline extension function added to ObjectMapper by the Kotlin module, and it uses the inferred type of the result grabbing the reified generics (available to inline functions) to do whatever is needed to tell Jackson about the data type. It creates a Jackson TypeReference behind the scenes for you and passes it along to Jackson. This is the source of the function:
inline fun <reified T: Any> ObjectMapper.readValue(content: String): T = readValue(content, object: TypeReference<T>() {})
You can easily code the same, but the module has a larger number of these helpers to do this work for you. In addition it handles being able to call non-default constructors and static factory methods for you as well. And in Jackson 2.8.+ it also can deal more intelligently with nullability and default method parameters (allowing the values to be missing in the JSON and therefore using the default value). Without the module, you will soon find new errors.
As for your use of mapper.typeFactory.constructParametricType you should use TypeReference instead, it is much easier and follows the same pattern as above.
val myTypeRef = object: TypeReference<SomeOtherClass>() {}
This code creates an anonymous instance of a class (via an object expression) that has a super type of TypeRefrence with your generic class specified. Java reflection can then query this information.
Be careful using Class directly because it erases generic type information, so using SomeOtherClass::class or SomeOtherClass::class.java all lose the generics and should be avoided for things that require knowledge of them.
So even if you can get away with some things without using the Jackson-Kotlin module, you'll soon run into a lot of pain later. Instead of having to mangle your Kotlin this module removes these types of errors and lets you do things more in the "Kotlin way."
The following works as expected:
val type = mapper.typeFactory.constructParametricType(OperationalResult::class.java, String::class.java)
val operationalResult = mapper.readValue<OperationalResult<String>>("""{"result":"stack"}""", type)
println(operationalResult.result) // -> stack
A simpler alternative to deserialize generic types using com.fasterxml.jackson.core.type.TypeReference:
val operationalResult = mapper.readValue<OperationalResult<Double>>("""{"result":"5.5"}""",
object : TypeReference<OperationalResult<Double>>() {})
println(operationalResult.result) // -> 5.5
And with the aid of jackson-kotlin-module you can even write:
val operationalResult = mapper.readValue<OperationalResult<Long>>("""{"result":"5"}""")
println(operationalResult.result)
Is there a way to use Parceler with Kotlin data classes and constructor for serialization without using #ParcelProperty annotation for each field?
If I try and use library like this:
#Parcel
data class Valve #ParcelConstructor constructor(val size: Int)
I get Error:Parceler: No corresponding property found for constructor parameter arg0. But if I add #ParcelProperty("size") it works just fine.
Why is that?
Update:
There are other another way to use this library.
I could just remove #ParcelConstructor annotation, but then I will get error
Error:Parceler: No #ParcelConstructor annotated constructor and no default empty bean constructor found.
I think (haven't tested it) I also could make all constructor parameters optional and add #JvmOverloads but that has a side effect that I have to check all properties of the class if they are null or not.
Update 2:
This is what worked for me:
#Parcel
data class Valve(val size: Int? = null)
In short generated Java class must have default empty constructor. One way to achieve that is to do as above - all variables should have default values.
According to the docs, Parceler by default works with public fields. But a usual Kotlin data class (as in your example) is rather a "traditional getter/setter bean", since every Kotlin property is represented by a private field and a getter/[setter].
TL; DR: I think this will work:
#Parcel(Serialization.BEAN)
data class Valve(val size: Int = 10)
Note the default value, it allows Kotlin to automatically generate an additional empty constructor, which is required by the Java Been specification.
Another way would be to mark the constructor that we already have:
#Parcel(Serialization.BEAN)
data class Driver #ParcelConstructor constructor(val name: String)
The specific document: https://github.com/johncarl81/parceler#gettersetter-serialization
I know this question already has an answer, but for future viewers who are also struggling to get Parceler to work with kotlin data objects, I wrote a new annotation processor to generate the Parcelable boilerplate for Kotlin data classes. It's designed to massively reduce the boilerplate code in making your data classes Parcelable:
https://github.com/grandstaish/paperparcel
Usage:
Annotate your data class with #PaperParcel, implement PaperParcelable, and add a JVM static instance of the generated CREATOR e.g.:
#PaperParcel
data class Example(
val test: Int,
...
) : PaperParcelable {
companion object {
#JvmField val CREATOR = PaperParcelExample.CREATOR
}
}
Now your data class is Parcelable and can be passed directly to a Bundle or Intent
Edit: Update with latest API
Just add the default constructor:
#Parcel
data class Valve(val size: Int) {
constructor() : this(0)
}
if you use Kotlin 1.1.4 or above it's easier to use #Parcelize annotation
For doing this first add this to build.gradle
android {
//other codes
//for using latest experimental build of Android Extensions
androidExtensions {
experimental = true
}
}
Then change your class like this
#Parcelize
data class Valve(val size: Int? = null) : Parcelable