Why #Transient can't be used with val fields? - kotlin

I wrote data class
data class FileHeader(
val relativePath: String,
val orderNumber: Long,
val bodySize: Int
) : Serializable {
#Transient
var headerSize: Int = 0
get() = relativePath.length + 8
}
It works as i expect.
But why i can't use #Transient with val field?
The error is:
This annotation is not applicable to target member property without backing field or delegate
Are there any reasons why it implemented in this way?

The annotation
Marks the JVM backing field of the annotated property as transient, meaning that it is not part of the default serialized form of the object.
The default serialization works on fields and doesn't care about getter methods. So if there's no backing field, there's nothing to serialize (and nothing to mark as transient in bytecode). The annotation would be useless in this case, so the designers chose to make it an error.
If you don't see why there's no backing field:
A backing field will be generated for a property if it uses the default implementation of at least one of the accessors, or if a custom accessor references it through the field identifier.
With your var, the backing field is needed by the default setter; when you change it to val, it isn't.

Try this
#get:javax.persistence.Transient
val headerSize
get() { ... }

Related

How does kotlin compiler know whether a val should be a property or a function

The following kotlin code
val nameHash get() = name.hashCode()
can be compiled into java as follows
public final int getNameHash() {
return name.hashCode();
}
and the property nameHash disapears.
However when the val is changed to var, the compiler says "Property must be initialized"
What is the deeper difference between var and val?
How does kotlin compiler know whether a val should be a property or a function
As far as the Kotlin language is concerned, val denotes properties, never functions. However, there is a difference between these two property declarations:
val nameHash get() = name.hashCode()
var nameHash get() = name.hashCode()
And that is that the first property does not have a backing field. Properties with backing fields must be initialised one way or another, for example:
var nameHash = 0 // for example
get() = name.hashCode()
And this is why your code with var didn't compile.
If you are asking for the situations when a backing field is generated for a Kotlin property, they are listed in the spec:
However, the backing field is created for a property only in the
following cases
A property has no custom accessors;
A property has a default accessor;
A property has a custom accessor, and it uses field property;
A mutable property has a custom getter or setter, but not both.
These are the cases where your property needs a backing field. Your var nameHash satisfies that last case, because it is a "mutable property". If you use val instead, it is not a mutable property anymore and doesn't satisfy any of those cases.
Intuitively, a mutable property without setter needs a backing field because one must need to be able to set the property. How can you set it when it has no setter? Well, the Kotlin compiler solves the problem by generating a backing field and just sets the backing field instead.
Property is a functions set() & get(). Read-only properties implement only the get() function, but still, it's a function, so everything written in the property will be executed every time it's called.
In Kotlin, keywords: val is the same as the read-only property, meaning it's required to implement only get() function. When you put var keyword, compiler expects you to implement both get() & set() functions.
So, compile error there because your property missing set() function that is usually needed to store a value (or as the compiler says: must be initialized).
The error message is a little confusing in this case. The difference between val and var is that val means there is a getter while var means there is a getter and a setter. To fix your code you need to add an implementation for the setter:
var nameHash
get() = name.hashCode()
set(hash: Int) { ... }
Although, in this case I don't think it makes too much sense. We can't set the hash code value of the name.

Kotlin data class secondary constructor init block

Let's imagine that we have data class with two properties and we need secondary constructor for some reasons. Problem is that i need recalculate each argument in primary constructor call instead of using some cached value of raw.split("_"):
data class Id(
val arg1: String,
val arg2: String
) {
constructor(raw: String) : this(raw.split("_")[0], raw.split("_")[1])
}
I can do this in Java but how I can do this in Kotlin?
You can do it this way:
data class Id(
val arg1: String,
val arg2: String
) {
private constructor(splitted: List<String>) : this(splitted[0], splitted[1])
constructor(raw: String) : this(raw.split("_"))
}
It's a good and idiomatic way to solve your problem. Since all secondary constructors must delegate to primary constructor (data class always has it), you can't do what you want in constructor body. In Java it works because there are no primary constructors and no data classes at language level - in Kotlin you can do it like in Java too if you remove data modifier and move properties outside of constructor, but it's a really bad way.

What does get() do when assigning value in Kotlin?

The code here is assigning the _showProgress to showProgress by using the get()
private val _showProgress = MutableLiveData<SingleLiveEventWrapper<Boolean>>()
override val showProgress : LiveData<SingleLiveEventWrapper<Boolean>>
get() = _showProgress
The code here is the same as above and it seems like there is no difference when running the code, it does the same job as above. What does the get() in the above code do? Is it necessary to use the get() when assigning the value?
private val _showProgress = MutableLiveData<SingleLiveEventWrapper<Boolean>>()
override val showProgress : LiveData<SingleLiveEventWrapper<Boolean>> = _showProgress
The get() method doesn't change the value — but it does change the type. (The static, compile-time type, anyway.)
The private property is a MutableLiveData field. I don't know that type, but it looks like a class or interface which wraps some data (in this case a SingleLiveEventWrapper<Boolean>), and allows it to be changed.
The public property, though, is a LiveData. That's probably a superclass or superinterface of MutableLiveData which lacks the method(s) allowing the data to be changed. The overridden getter method simply returns the value of the private property, but in doing so upcasts it to the non-mutable type.
The result is that code within the class can access the mutable field, which other code can only get a read-only view of it. So it's effectively doing some encapsulation, restricting the ability to change the field while still allowing it to be seen.
(You wouldn't need to call the getter explicitly; simply using the property syntax myObject.showProgress will call the getter for you. In Kotlin, all properties get a getter method -- and, if var a setter; you only need to override the default ones if you want different behaviour.)
Adding a bit to gidds' answer and focusing specifically on comparison with
override val showProgress : LiveData<SingleLiveEventWrapper<Boolean>> = _showProgress
If you use = ... without an explicit getter, a backing field is created and _showProgress is stored there while constructing the object. So there are two fields storing the same value. In this case this shouldn't make any difference beyond using a bit more memory, but in other cases it could:
if _showProgress was a var it could be reassigned after construction. Then if showProgress is defined with get(), accessing it will always get the current value of _showProgress, but without get() it'll get the initial one.
Similarly if _showProgress was open protected and overridden in a subclass (due to initialization order).

Do we need to initialize nullable fields in kotlin?

I have recently reviewed some kotlin codes, All nullable field initialized as null.
What is the difference between val x : String? = null and val x : String?
Should we initialize the nullable fields as null?
Everything, even nullable variables and primitives, need to be initialized in Kotlin. You can, as tynn mentioned, mark them as abstract if you require overriding. If you have an interface, however, you don't have to initialize them. This won't compile:
class Whatever {
private var x: String?
}
but this will:
interface IWhatever {
protected var x: String?
}
This too:
abstract class Whatever {
protected abstract var x: String?
}
If it's declared in a method, you don't have to initialize it directly, as long as it's initialized before it's accessed. This is the exactly same as in Java, if you're familiar with it.
If you don't initialize it in the constructor, you need to use lateinit. Or, if you have a val, you can override get:
val something: String?
get() = "Some fallback. This doesn't need initialization because the getter is overridden, but if you use a different field here, you naturally need to initialize that"
As I opened with, even nullable variables need to be initialized. This is the way Kotlin is designed, and there's no way around that. So yes, you need to explicitly initialize the String as null, if you don't initialize it with something else right away.
A property must be initialized. Therefore you have to do the initialization var x : String? = null. Not assigning a value is only the declaration of the property and thus you'd have to make it abstract abstract val x : String?.
Alternatively you can use lateinit, also on non-nullable types. But this has the effect, that it's not null, but uninitialized lateinit var x : String.
val x : String? will create an uninitialized variable or property, depending on where it's defined. If it's in a class (rather than a function), it creates a property, and you cannot create an uninitalized property unless it's abstract. For example take this code:
class MyClass {
val x : String?
}
This won't compile. You'll get Property must be initialized or be abstract.
This code, however, will compile
class MyClass {
fun test() {
val x : String?
}
}
However it's a bit pointless as you will not be able to refer to that variable: as soon as you do you'll get Variable 'x' must be initialized.
So yes, generally when defining a nullable member you should initialize it (e.g. with a value of null), unless it's abstract, in which case the overriding class should initialize it.

Why the modifier on property is the same as Getter and Setter but not Field after Java being translated into Kotlin?

Official Ref says the default Visibility Modifier is public.
But from koan data classes it can be inferred that it is private.
Is it a contradiction? What is the default Visibility Modifier of property in kotlin?
---------The above is the initial question---------------
I didn't mix property and field up. But I did have confusion on how property is accessed.
To resolve my confusion, I actually should have asked the new question in edited title.
Self-answered below.
The default visibility for properties (and functions and classes, and...) is indeed public.
The Koan is a little confusing, because the Java class has private fields with public getters.
A property can be seen as the combination of field, a getter and an optional setter. The property in the Koan has a private backing field and a public getter.
If you for example write
val age = person.age
then Kotlin will basically generate a getAge() method of the Person class, that will be called internally. This method returns the field itself.
It's also possible to add behavior to that getter. You can find more info in that in the documentation.
It's therefore not a contradiction, but a different concept.
What it the default Visibility Modifier for properties in kotlin?
It is public, as the docs say
Why are the fields not private
In this example the fields are immutable, so there are no setters defined automatically. The data class automatically has getters, and uses them, but it simplifies reading the code by not requiring them to be manually called.
Worked example
This code:
object X {
data class Example(val a: String, val b: String)
#JvmStatic
fun main(args: Array<String>) {
val e = Example("a", "b")
println(e.a)
println(e.b)
}
}
The main method of this compiles to this (with checks and metadata removed):
public static final void main(String[] args) {
X.Example e = new X.Example("a", "b");
String var2 = e.getA();
System.out.println(e.getA());
var2 = e.getB();
System.out.println(var2);
}
(Decompiled using IntelliJ IDEA)
Property encapsulates backing field by defintion. Backing field is directly assigned only when being initialized. All accesses except initialization are done through accessors.
So the private modifier on field in Java is no longer needed in Kotlin.
And the public on getter and setter in Java is placed on property(actually, still for getter and setter) in Kotlin.
Therefore the omitted modifier is public and not private.