Kotlin - nullable property default to null value - properties

Suppose I define a class with a nullable property
class ABC {
var myProperty: String? = null
}
Is there a way to default it to null value?
Maybe something similar to SCALA?
var myProperty: String? = _ // compilation error
or simply:
var myProperty: String? // compilation error
I know we could have used a lateinit variable that from Kotlin 1.2 can be later checked for initilization like so:
lateinit var myProperty: String
if (::myProperty.isInitialized) {
//value is not-null
}
So is lateinit the preferred way? Is defaulting to null value possible or it's omitted on purpose?

Kotlin intentionally requires you to initialize all properties explicitly. There is no shorthand syntax. For a property of a nullable type, the preferred way is not to use lateinit, but to declare it with a null initializer.
The isInitialized method for lateinit properties is designed to handle complex cases like cleanup of resources; it's not intended to be used as a replacement for a null check for users who want to save on writing = null as part of a property declaration.

Short answer is no.
Unlike some other languages (Scala, Go), Kotlin doesn't assume it knows best what the default value for the type is (who said that it should be 0 for int anyway?)

You could also declare a global function
fun nullString(): String? {
return null
}
and use it like
class ABC {
var myProperty = nullString()
}
Since there is only one null reference in the language, a global variable will also work
var nullString : String? = null
class ABC {
var myProperty = nullString
}
P.S. -
unfortunately the spacial characters like '_' are reserved in Kotlin so you can't use it to name your variable and create var myProperty = _

Related

Getters cannot be used to identify return type properly in Kotlin

I have a data class that has the following form:
data class ContentElementField(val type: String) {
val text: String? = null
get() = requireNotNull(field)
val style: String? = null
get() = requireNotNull(field)
val path: String? = null
get() = requireNotNull(field)
val caption: String? = null
get() = requireNotNull(field)
}
The problem arises when I want to perform the following operation:
when (it.type) {
"text" -> TextElement(Text(it.text), Style(it.style))
"image" -> ImageElement(Path(it.path), Caption(it.caption))
}
The compiler warns me about that You cannot send a nullable type to a function that does not accept nullable arguments.
Even if the field is signed to be nullable, its getter is signed to be not nullable, though.
The compiler should use getters to resolve whether to give this warning.
What would you offer to get around this problem?
It doesn't matter if your getter happens to crash if the current value is null - the type is still nullable, the getter's return type is still String?.
Why are you doing this anyway? Why not just make the fields non-null as normal and let a null assignment throw the exception instead? That way you won't have to fight the type system.
If what you have in mind is different and this was just meant to be a simple example, then you have a few options:
Use !! at the call site since you're guaranteeing it's not null
"text" -> TextElement(Text(it.text!!), Style(it.style))
Expose the private nullable property through a non-null one:
// I see people do this a lot in Activities and Fragments even though
// they should probably just be making the one property lateinit instead
private val _text: String? = whatever
val text: String get() = requireNotNull(_text)
Maybe look at Kotlin contracts which allow you to make guarantees to the compiler about values (no example because I've never used it)
It's not really clear what you actually want to do though, or why this is useful. Your example is even using vals and assigning null to them. Whatever your real use case is, there's probably a better way.
(Also in case you're not aware, properties that aren't constructor parameters aren't included in the basic data class behaviour, i.e. its equals/hashCode/toString implementations. Another reason just making the types non-null helps, you can stick them in the constructor instead of having to do this logic)

Is this correct "lateinit var text : String?"?

I need to know if this line code is right, my teacher told me it's correct, but I disagree 'cause "lateinit" can't be with a variable that could be null or not.
Line code:
lateinit var text : String?
Code:
val cadena = null
lateinit var text : String?
text = null
text = cadena ?: "Hola"
text?.let { println(text) }
You are correct and your teacher is wrong. Proof: lateinit var text : String? results in compilation error with Kotlin 1.3.50:
'lateinit' modifier is not allowed on properties of nullable types
How any teacher can possibly claim such code is correct is beyond me...
I would like to add a little deep dive into lateinit properties in Kotlin.
'lateinit' modifier is not allowed on properties of nullable type - this can be found in Kotlin documentation. This kind of modifier is for special kind of constructions. It's for fields that will be initialized somewhen after object creation. For example, via DI framework or mocking framework.
But, what is under that field? If we would check it, we will simply find that before initialization property has null value. Nothing more, nothing less, just null. But, if we would like to access that property before initialization UninitializedPropertyAccessException is thrown.
In Kotlin 1.3 lateinit properties got new property - isInitialized (to use it: ::lateinitiProperty.isInitilized). So, before we access that property we are able to check if under that field is null or something else, without throwing exception.
But, lateinit means that object will be initialized later as not null property. And a programmer guarantee that this value is not null after intialization. If it could be, why just not use nullable type?
If there is a way to uninialize lateinit property? Yes, it is. Via reflection we can set that value to null again (JVM is not null-safe). And accessing that field will not finish with NPE execption, but with UninitializedPropertyAccessException. And .isInitialized will return false for field that refers to null.
And how does it work?
class MyClass {
lateinit var lateinitObject: Any
fun test() {
println("Is initialized: ${::lateinitObject.isInitialized}") // false
lateinitObject = Unit
println("Is initialized: ${::lateinitObject.isInitialized}") // true
resetField(this, "lateinitObject")
println("Is initialized: ${::lateinitObject.isInitialized}") // false again
lateinitObject // this will throw UninitializedPropertyAccessException
}
}
fun resetField(target: Any, fieldName: String) {
val field = target.javaClass.getDeclaredField(fieldName)
with (field) {
isAccessible = true
set(target, null)
}
}
Ofc, using lateinit that way is probably not what you want, and treat is as a curio about lateinit design in JVM.
And due to your teacher - he wasn't right. Even if lateinit may refer to null (and it does actually), you cannot declare it as a nullable type. If you need to, you don't need lateinit modifier.

How to uninitialize lateinit in Kotlin

I have a lateinit var as
lateinit var someVariable: SomeVariable
I initialize this value like someVariable = SomeVariable() and use it whenever I need.
At a certain point, I want to set everything to default and want to "uninitialize" someVariable. How can I do that?
I am not looking for changing its type to a nullable object and set null. I need to keep it a Non-Null type.
I don't think it's possible without some kind of wrapper (or reflection, but about it in a moment).
In fact, lateinit is design for compatibility with DI frameworks etc. If you know the value can be uninitialized in any moment then you should use nullable type.
So, what about that reflection thing? lateinit is in fact a kind of smart wrapper that makes nullable value to act like not nullable, and instead of throwing NullPointerException throws UninitializedPropertyAccessException. lateinit property at the moment of declaration in JVM is null, so, let's make it null again ;)
So...
class MyClass {
lateinit var lateinitObject: Any
fun test() {
println("Is initialized: ${::lateinitObject.isInitialized}") // false
lateinitObject = Unit
println("Is initialized: ${::lateinitObject.isInitialized}") // true
resetField(this, "lateinitObject")
println("Is initialized: ${::lateinitObject.isInitialized}") // false
lateinitObject // this will throw UninitializedPropertyAccessException
}
}
fun resetField(target: Any, fieldName: String) {
val field = target.javaClass.getDeclaredField(fieldName)
with (field) {
isAccessible = true
set(target, null)
}
}
fun main() {
MyClass().test()
}
So, setting that field to null (and it's possible only via reflection) makes this filed uninitialized again. And one important thing - treat it as a curiosity, not like thing that should be in your production code.

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.

How to pass nullable type into function that takes a non null type?

Is this possible if I do a null check before passing? For example:
fun main(args: Array<String>) {
var num: Int? = null
// Stuff happens that might make num not null
...
if (num != null) doSomething(num)
}
fun doSomething(number: Int) {
...
}
I don't understand why the compiler won't allow me to pass a nullable even though I check that it's not null first. Can anyone explain?
NOTE: starting from compiler version 1.0 beta the code in question works as is
The compiler can tell if the variable is mutated between check and use, at least in case of local variables like in this question, and in some other cases. See Jayson's answer for details.
http://kotlinlang.org/docs/reference/null-safety.html#checking-for-null-keyword--in-conditions says
The compiler tracks the information about the [null] check ... this only works where b is immutable (i.e. a local val or a member val which has a backing field and is not overridable), because otherwise it might happen that b changes to null after the check.
So something like this should work:
fun main(args: Array<String>) {
var num: Int? = null
// Stuff happens that might make num not null
...
val numVal: Int? = num
if (numVal != null) doSomething(numVal)
}
fun doSomething(number: Int) {
...
}
Of course, it would be nicer to rewrite "stuff happens" in such a way that you could make num into a val in the first place.
In current Kotlin (1.0 beta or newer) you do not have this issue anymore. Your code would compile. A local variable that is val or var can safely Smart Cast since the compiler can determine if the value could have mutated or not (on another thread for example).
Here is an excerpt from another Stack Overflow question that covers more aspects of nullability and Kotlin's operators for dealing with them.
More about null Checking and Smart Casts
If you protect access to a nullable type with a null check, the compiler will smart cast the value within the body of the statement to be non nullable. There are some complicated flows where this cannot happen, but for common cases works fine.
val possibleXyz: Xyz? = ...
if (possibleXyz != null) {
// allowed to reference members:
possiblyXyz.foo()
// or also assign as non-nullable type:
val surelyXyz: Xyz = possibleXyz
}
Or if you do a is check for a non nullable type:
if (possibleXyz is Xyz) {
// allowed to reference members:
possiblyXyz.foo()
}
And the same for 'when' expressions that also safe cast:
when (possibleXyz) {
null -> doSomething()
else -> possibleXyz.foo()
}
// or
when (possibleXyz) {
is Xyz -> possibleXyz.foo()
is Alpha -> possibleXyz.dominate()
is Fish -> possibleXyz.swim()
}
Some things do not allow the null check to smart cast for the later use of the variable. The example above uses a local variable that in no way could have mutated in the flow of the application, whether val or var this variable had no opportunity to mutate into a null. But, in other cases where the compiler cannot guarantee the flow analysis, this would be an error:
var nullableInt: Int? = ...
public fun foo() {
if (nullableInt != null) {
// Error: "Smart cast to 'kotlin.Int' is impossible, because 'nullableInt' is a mutable property that could have been changed by this time"
val nonNullableInt: Int = nullableInt
}
}
The lifecycle of the variable nullableInt is not completely visible and may be assigned from other threads, the null check cannot be smart cast into a non nullable value. See the "Safe Calls" topic below for a workaround.
Another case that cannot be trusted by a smart cast to not mutate is a val property on an object that has a custom getter. In this case the compiler has no visibility into what mutates the value and therefore you will get an error message:
class MyThing {
val possibleXyz: Xyz?
get() { ... }
}
// now when referencing this class...
val thing = MyThing()
if (thing.possibleXyz != null) {
// error: "Kotlin: Smart cast to 'kotlin.Int' is impossible, because 'p.x' is a property that has open or custom getter"
thing.possiblyXyz.foo()
}
read more: Checking for null in conditions
You can use let to simplify the code. The kotlin scope function introduces a local variable in the context of "num". No need to declare temporary variable numVal.
fun main(args: Array<String>) {
var num: Int? = null
// Stuff happens that might make num not null
...
num?.let{
doSomething(it)
}
}
Which works same as below but simpler and cleaner.
fun main(args: Array<String>) {
var num: Int? = null
// Stuff happens that might make num not null
...
val numVal: Int? = num
if (numVal != null) doSomething(numVal)
}
Use can use Scoping function let or apply along with null safe operator ?.
fragmentManager?.let{
viewPager.adapter = TasksPagerAdapter(it)
}
This way you can pass a nullable type to a non-nullable type parameter