Normally, I use the @SerializedName
annotation to map the JSON fields. But, in Google Architecture Component sample projects, I see they use @field:SerializedName
annotation and I can't find anywhere to read the purpose of using that @field
annotation.
I'm new to Kotlin and can someone explain or share the ref links to read? Thank you.
@Entity(
indices = [
Index("id"),
Index("owner_login")],
primaryKeys = ["name", "owner_login"]
)
data class Repo(
val id: Int,
@field:SerializedName("name")
val name: String,
@field:SerializedName("full_name")
val fullName: String,
@field:SerializedName("description")
val description: String?,
@field:SerializedName("owner")
@field:Embedded(prefix = "owner_")
val owner: Owner,
@field:SerializedName("stargazers_count")
val stars: Int
) {...
The @SerializedName annotation can be used to serialize a field with a different name instead of an actual field name. We can provide the expected serialized name as an annotation attribute, Gson can make sure to read or write a field with the provided name.
While it isn't incorrect to use @field:SerializedName(...)
in this case, it is unnecessary: the same bytecode will be generated with or without the addition of field:
because either way only the private backing field will get the annotation. I've never seen an actual project in the wild that prefers @field:SerializedName
to @SerializedName
and I think the use by the Google samples is unnecessarily confusing for beginners. You should feel free to ignore it.
TL;DR: The field:
part is known as a "use-site target", and makes it clear that the annotation is applied to the backing field of the Kotlin property. It is not strictly necessary in this case, though it can make the code more readable.
To simplify, let's say you have:
data class Repo(
@field:SerializedName("name")
var name: String
)
That code is declaring a data class along with its properties via the primary constructor. Kotlin properties are made up of the property itself, a getter, and—if mutable—a setter. What does all this mean for us? It means there's a lot going on here for just one little property. In this context, the annotation on name
could1 apply to any one of the following:
By using field:
, which is known as a "use-site target", you make it clear the annotation is applied to the backing field of the property. No more ambiguity1.
However, I should note that not using field:
in this case would still result in the annotation being applied to the backing field. As described in the documentation below, if no use-site target is used, then Kotlin looks at the @Target
meta-annotation. From there, it applies the annotation to the first valid target from param
(constructor parameter), property
(Koltin property), and field
(backing field), in that order. The @SerializedName
2 annotation is not applicable to param
s. And due to it being a Java annotation, it can't possibly be applicable to property
s. But the annotation is applicable to field
s, and so that's where it would end up.
1. There's not any true ambiguity, because the rules are clearly defined (see the documentation below). But that's regarding the compiler. For a human, at first glance the code may be unclear, and that's really what matters. The use-site target may not be strictly necessary, but it makes the code more readable (at least, in my opinion).
2. You've tagged this question with gson, and so that's where I assume the annotation is from.
Here's the Kotlin documentation discussing this:
Annotation Use-site Targets
When you're annotating a property or a primary constructor parameter, there are multiple Java elements which are generated from the corresponding Kotlin element, and therefore multiple possible locations for the annotation in the generated Java bytecode. To specify how exactly the annotation should be generated, use the following syntax:
class Example(@field:Ann val foo, // annotate Java field @get:Ann val bar, // annotate Java getter @param:Ann val quux) // annotate Java constructor parameter
The same syntax can be used to annotate the entire file. To do this, put an annotation with the target
file
at the top level of a file, before the package directive or before all imports if the file is in the default package:@file:JvmName("Foo") package org.jetbrains.demo
If you have multiple annotations with the same target, you can avoid repeating the target by adding brackets after the target and putting all the annotations inside the brackets:
class Example { @set:[Inject VisibleForTesting] var collaborator: Collaborator }
The full list of supported use-site targets is:
file
;property
(annotations with this target are not visible to Java);field
;get
(property getter);set
(property setter);receiver
(receiver parameter of an extension function or property);param
(constructor parameter);setparam
(property setter parameter);delegate
(the field storing the delegate instance for a delegated property).To annotate the receiver parameter of an extension function, use the following syntax:
fun @receiver:Fancy String.myExtension() { ... }
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 list is used:
param
;property
;field
.
This might help: from the kotlin docs
When you're annotating a property or a primary constructor parameter, there are multiple Java elements which are generated from the corresponding Kotlin element, and therefore multiple possible locations for the annotation in the generated Java bytecode. To specify how exactly the annotation should be generated, use the following syntax:
class Example(@field:Ann val foo, // annotate Java field
@get:Ann val bar, // annotate Java getter
@param:Ann val quux) // annotate Java constructor parameter
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With