Lets assume, there is a non-null enum MainFlag object ( like enum class MainFlag { PARAM1, PARAM2 }) in an Activity by lateinit var:
private lateinit var flag: MainFlag
Furtheron, I get that later in the onCreate() like:
flag = intent?.extras?.getSerializable(ARG_FLAG) as MainFlag
and I used to use this flag in that activity on several places with null-checks
flag?.let{
//..
}
but then Android Studio complains:
Unnecessary safe call on a non-null receiver of type
MainActivity.Companion.MainFlag
I am not sure using it without null checks, because if intent?.extras? fails, flag will not be set, thus null?
yes it can be null. You are getting the warning because you are down casting to a not nullable type
intent?.extras?.getSerializable(ARG_FLAG) as MainFlag
should be
intent?.extras?.getSerializable(ARG_FLAG) as? MainFlag
First of all you have to declare the variable as
private lateinit var flag: MainFlag?
or
private var flag: MainFlag? = null
then you can set the value of flag like this
flag = intent?.extras?.getSerializable(ARG_FLAG) as? MainFlag
Now the flag variable is nullable. So you need to check for nullable everywhere in the code like this:
flag?.let{
//..
}
If you are sure that intent?.extras?.getSerializable(ARG_FLAG) will never be null (and also none of the intermediate objects: intent and intent.extras), then you can keep everything as is and just use flag.let { ... instead of flag?.let { ....
If there is a chance that any of those is null, then you have to use a MainFlag?-type for your flag, e.g.:
private var flag : MainFlag? = null // having null in place basically makes the lateinit meaningless and rightfully lateinit does not allow nullable types
flag = intent?.extras?.getSerializable(ARG_FLAG) as? MainFlag
... or risk a TypeCastException when keeping as MainFlag.
The reason why Android Studio complains about the unnecessary safe call, is, that you specified the type as non-nullable MainFlag (indirectly via your as MainFlag-cast). So if you access flag it must already be there and it must be not-null (you said it so). The code however will fail at the cast, if there was a null-value involved, i.e. the cast to the non-nullable type could not succeed.
Related
I want to know the difference between String? and String! in Kotlin.
I search on kotlinlang.org but did not find any information.
Kotlin's type system differentiates between nullable and non-nullable types. In that context, String? is a nullable type while String would be the corresponding non-nullable type.
When working with Java libraries, the compiler is not always able to identify whether a type is nullable or not, since Java does not have that differentiation. Such types will then show up as "platform type" String!, meaning (basically): "I have no idea if this can be null but I'll treat it as non-nullable for now".
If you have control over the corresponding Java library, Kotlin supports various annotations to help distinguish between types, otherwise it is up to you as developer to explicitly assign either a nullable or a non-nullable type e.g. upon variable declaration to avoid running into NullPointerExceptions at runtime.
I'll try to answer with some sample code.
1. String?
This means this string is nullable.
Example 1: Use it in the type definition.
fun testStringTypes() {
// When initializing stringA, we can set null as the value
var stringA: String? = null
// And we can also set it to a meaningful string
stringA = "Hello"
// Then we can still set it back to null
stringA = null
}
Example 2: a variance of String?
fun testStringTypes() {
var stringA: String? = null
stringA = "Hello"
stringA = null
val lenOfStringA = stringA?.length ?: 0
}
So here is a brief description about what this val lenOfStringA = stringA?.length ?: 0 means:
Because stringA is defined as nullable;
stringA?.length means, access to the length property only if stringA is not null;
Because if, when stringA is null and if the code still tries to access to length (like in Java), the program will throw a NullPointerException. stringA? a question mark here, is to avoid this, which is called SafeCalls.
2. String!
This is platform types.
Copy from the link above:
As mentioned above, platform types can't be mentioned explicitly in the program, so there's no syntax for them in the language. Nevertheless, the compiler and IDE need to display them sometimes (for example, in error messages or parameter info), so there is a mnemonic notation for them:
I think (correct me if I was wrong), this makes sense when working with Java, because String in Java can be null, in other words, when accessing it from Kotlin, we don't know it is null or not. So String! is kind of a reminder to developer: Hey! Attention, this variable could be null.
Example 3, work with Java method from Kotlin:
// In Java:
public class PlatformTypeTest {
public String returnSomeStringCouldBeNull() {
return null;
}
}
And let's call this method in Kotlin.
fun testStringTypes() {
val someStringFromJava = PlatformTypeTest().returnSomeStringCouldBeNull()
}
fun testStringTypes() {
val someStringFromJava = PlatformTypeTest().returnSomeStringCouldBeNull()
someStringFromJav
}
As we can see from above two screenshots, IDE is reminding us this String from Java can be null.
And for String!, we can access it in different ways:
fun main() {
val someStringFromJava = PlatformTypeTest().returnSomeStringCouldBeNull()
var lenOfString = someStringFromJava?.length ?: 0
// lenOfString = someStringFromJava.length // NullPointerException
// lenOfString = someStringFromJava!!.length // NullPointerException
println(lenOfString)
}
With code snippet above, it works fine with var lenOfString = someStringFromJava?.length ?: 0, but the other two ways will cause NPE, as explained at above.
String? is a nullable type.
String! is a platform type platform type.
From Kotlin website:
Nullable Types Example:
val nullable: String? = item // allowed, always works
val notNull: String = item // allowed, may fail at runtime
Platform Types Example:
- T! means "T or T?",
- (Mutable)Collection<T>! means "Java collection of T may be mutable or not, may be nullable or not",
- Array<(out) T>! means "Java array of T (or a subtype of T), nullable or not"
I have a variable in my fragment class:
private lateinit var dManager: DataManager
And I'm initializing it before the first using here:
override fun onResume() {
super.onResume()
dManager = MyApp.gManager.getDataManager(sp,level,test)
if (dManager.hp< 1) {
...
...
...
}
}
This code works ok for me and most users (99.5%), but sometimes i get crash report
lateinit property dManager has not been initialized
How can this happen? What should I do to prevent it?
lateinit var makes compiler aware that’s not null
If your property is lifecycle-driven (e.g. a reference to a TextView
or ImageView which gets inflated during Android Activity lifecycle)
or it is initialized through injection, you cannot supply a non-null
initializer and you must declare its type as nullable. This in turn
will require you to use null checks every time you reference the
property, which may be a bit inconvenient, especially if you are
absolutely sure the property will get initialized at some point,
before you access it for the first time.
Kotlin has a simple solution for such a scenario, allowing you to mark the property with the lateinit modifier.
If you access the property before initialization, you’ll get
an UninitializedPropertyAccessException.
getDataManager(sp,level,test) may return sometimes null so for safe sides your solution would be like as :-
override fun onResume() {
super.onResume()
dManager = MyApp.gManager.getDataManager(sp,level,test)
if (::dbManager.isInitialized && dManager.hp< 1) {
...
...
...
}
}
May be your getDataManager(sp,level,test) return null value
OR
As per the document you have to check object with .isInitialized property.
Returns true if this lateinit property has been assigned a value, and false otherwise.
Check lateinit var is initialized
lateinit var file: File
if (::file.isInitialized) { ... }
I'm creating one function in Kotlin. It validates email and password fields. I want to apply email and password should not be null. #NotNull kinda annotation here.
Does anyone know how to do this in Kotlin? So the caller cannot send the null value.
private fun isEmailAndPasswordValid(email: String, password: String): Boolean {
if (email.isEmpty()) return false
if (!Patterns.EMAIL_ADDRESS.matcher(email).matches()) return false
if (password.isEmpty()) return false
return true
}
Kotlin differentiates all types by nullable and not-nullable. For example, the class String can be used for the type String, which is not nullable, and the type String?, which IS nullable, i.e. could hold null.
In your example no nullable types are used, so you’re all good - no additional annotation needed.
The documentation should be studied for further information.
The Kotlin language is by default null-safe so when creating a new variable it can't be null, but when you want a nullable variable you can add The exclamation mark to specify that it can be null for Example String?, Int? ...
Not Nullable
var a: String = "bachiri"
a = null // compilation error
Nullable Type
var a: String? = "bachiri"
a = null // OK
and bare in mind if you want to call a function on the nullable Type you should use eighter the check for null variable(1) or use the safe calls(2)
Kotlin has build-in null safety. String is a non-null type while String? is a nullable type. So, isEmailAndPasswordValid(email: String, password: String) will enforce the value passed to it is non-null.
When I check the Kotlin documentation, I can see that a String variable can't be set to null, unless you declare it can be, and your compiler will raise an error.
For example, a regular variable of type String can not hold null
https://kotlinlang.org/docs/reference/null-safety.html
I am confuse for lateinit and nullable variable, which one to use for variable.
lateinit var c: String
var d: String? = null
c = "UserDefinedTarget"
// if not added initialisation for c than throws UninitializedPropertyAccessException
if (c == "UserDefinedTarget") {
//do some stuff.
}
//not throws any exception whether d is initialise or not.
if(d == "UserDefinedTarget") {
//do some stuff
}
A type that is nullable is just that, a thing that has a valid state that is null.
A non-nullable late init var represents something where null is an invalid state, but for some reason you can't populate it in the constructor.
Android Activities are a good example of a use of lateinit. Activities must have a no args constructor and their lifecycle only really starts with onCreate().
These are two completely different concepts.
You can use lateinit to avoid null checks when referencing the property. It's very convenient in case your properties are initialized through dependency injection, or, for example, in the setup method of a unit test.
However, you should keep in mind that accessing a lateinit property before it has been initialized throws an exception. That means you should use them only if you are absolutely sure, they will be initialized.
Nullable types, on the other hand, are used when a variable can hold null.
class A {
lateinit var a: String
fun cat() {
print(a.length) // UninitializedPropertyAccessException is thrown
a = "cat"
print(a.length) // >>> 3
}
}
class B {
var b: String? = null
fun dog() {
print(b.length) // won't compile, null check is obligatory here
print(b?.length) // >>> null
b = "dog"
print(b?.length) // >>> 3
}
}
For more information:
Late-initialized properties
Nullable types
It depends.
Nullable variable means that variable can hold value or null. lateinit means that variable must be initialised later. It should be initialized before accessing it. If you attempt accessing uninitialized lateinit variable UninitializedPropertyAccessException will be thrown.
It's always better to avoid using nulls in your app. Nulls are evil. So if you can initialize variable in onCreate then it's better to use lateinit. Also if you use dependency injection in your app and fields should be injected it's also a valid case to use lateinit instead of handling nulls.
If for some reason you can't initialize variable, initializing code can result to null, or null can be assigned to this variable later than you should use nullable variable. Generally speaking if null is a valid value for the variable.
Use lateinit for properties that cannot be initialized in a constructor.
I'm new to Kotlin and I don't know why compiler complains about this code:
data class Test(var data : String = "data")
fun test(){
var test: Test? = Test("")
var size = test?.data.length
}
Compiler complains with test?.data.length, it says that I should do: test?.data?.length. But data variable is String, not String?, so I don't understand why I have to put the ? when I want to check the length.
The expression test?.data.length is equivalent to (test?.data).length, and the test?.data part is nullable: it is either test.data or null. Therefore it is not null-safe to get its length, but instead you should use the safe call operator again: test?.data?.length.
The nullability is propagated through the whole calls chain: you have to write these chains as a?.b?.c?.d?.e (which is, again, equivalent to (((a?.b)?.c)?.d)?.e), because, if one of the left parts is null, the rest of the calls cannot be performed as if the value is not-null.
If you don't want to use safe call before each non-nullable component of the call chain, you can get the result of the first safe call into a new variable with the standard extension functions run or let:
// `this` is non-nullable `Test` inside lambda
val size = test?.run { data.length }
// or: `it` is non-nullable `Test` inside lambda
val size = test?.let { it.data.length }
Note that size is still nullable Int? here.