What is the difference between String? and String! in Kotlin - android

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"

Related

" ? " in Kotlin Variable.. what's the purpose on this?

I'm on learning basics of Kotlin language for Android Development :), i'm looking on some Kotlin syntax examples and i found out this example below.. the thing that i don't know what is purpose with the ? code.. i'm trying to find on Google but i can't understand completely what they explain, maybe you can help me with your explanation / your example on this one ? I'll appreciate that :)
Example :
fun main() {
val myName :String? = "Maura"
println("Your name is $myName")
val myAge :Int? = 18
println("Your age is $myAge")
}
Its null safety. Basically Int? represents nullable Int, while Int on its own is non-null.
var a: Int = 5
a = null // error
var b: Int? = 5
a = null // :)
In case you have null in your code, you cannot use them directly, if you want to for example call any function in them you have to follow null-safety: Use ?. safecall operator, or !!. null assertion operator (usually shouldn't use).
val c = a.plus(4) // :)
val d = b.plus(4) // Hold on, I'm null you can't use "." on me :P
val e = b?.plus(4) // Ok, if b is not null, add 4 and store that to e, otherwise store null to e
val f = b!!.plus(4) // Hmm, if b was not null I'll add 4 to it and store to f, otherwise I'll crash your program throwing NPE
In contrast to this the type of e would be Int? as you've already read the thing. But what if you want to assign it a default value, easy use the elvis operator (?:):
val g = b?.plus(4) ?: 4 // if b is not null add 4 to it and store otherwise store 4 :)
Edit: The code in your sample works because there's String template calls .toString() and it is defined as fun Any?.toString() i.e. defined on a nullable receiver. So b.toString is valid though can be confusing.
println("Your age is $myAge")
// is same as
println("Your age is" + myAge.toString())
// toString is an extension function valid for nullable types, though member functions aren't :)
well, this is null safety feature of Kotlin lang (awesome btw.)
in short: ? next to type of variable/value means that this variable/value may be null. don't use it so often, it kind-of protects you from NullPointerExceptions, pretty often bug-cause in Java. also in your simple case it is unnecessary
var myName :String? = null // this is possible
var mySecondName :String = null // this isn't possible, build error
var myThirdName :String = "never null"
myThirdName = null // build error, this variable can't be null
myName = myName.replace("a", "b")
// build error above, trying to access nullable variable straightly
myName = myName?.replace("a", "b")
// null safety with ?, if myName is null it won't be executed and myName stay null
myThirdName = myThirdName.replace("a", "b") // this is possible
myThirdName = myThirdName?.replace("a", "b")
// this is also possible, but you will get a warning that ? is unnecessary

Type mismatch. Required: Array<Uri>! Found: Array<Uri?>

The ? and !! syntax is very confusing in kotlin! I'm declaring a value callback as a private member of my activity:
private lateinit var mFilePathCallback: ValueCallback<Array<Uri>>
And I assign it on onShowFileChooser on onCreate method like so:
override fun onShowFileChooser(
webView: WebView?,
filePathCallback: ValueCallback<Array<Uri>>?,
fileChooserParams: FileChooserParams?
): Boolean {
mFilePathCallback = filePathCallback!! //Assigned here
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "image/*"
val PICKFILE_REQUEST_CODE = 100
startActivityForResult(intent, PICKFILE_REQUEST_CODE)
return true
}
But when I try to use it in onActivityResult like this:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if(data != null && resultCode == Activity.RESULT_OK) {
val resultsArray = arrayOfNulls<Uri>(1)
resultsArray[0] = data.data
mFilePathCallback.onReceiveValue(resultsArray)
Log.d("ACTIVITY RESULT", data.data.toString())
} else {
Log.d("ACTIVITY RESULT", "Cannot get file path.")
}
}
I get the following error in onRecieveValue function call: Type mismatch. Required: Array<Uri>! Found: Array<Uri?> This is so confusing!
All I had to do is cast results arrays as Array<Uri>:
mFilePathCallback.onReceiveValue(resultsArray as Array<Uri>)
arrayOfNulls<Uri>(1) returns Array<Uri?> because its elements can be null and are null to start with.
Setting an element with resultsArray[0] = data.data doesn't change the type (why would it?).
So you are passing an Array<Uri?> to mFilePathCallback.onReceiveValue which expects an Array<Uri> (an array whose elements are all Uri which can't be null).
Instead of the cast as in your own answer, just create the array of the correct type to start with:
val uri = data.data
if (uri != null) {
val resultsArray = arrayOf<Uri>(uri)
mFilePathCallback.onReceiveValue(resultsArray)
Log.d("ACTIVITY RESULT", uri.toString()) // why toString?
} else {
// your original code doesn't cover this case
// so decide what to do here
}
Note that if you just do
val resultsArray = arrayOf<Uri>(data.data)
you should ideally get a warning because data.data can be null, but it doesn't seem to be properly marked as #Nullable.
Kotlin is very strict on types. As Alexey Romanov already mentioned, when you create an array with arrayOfNulls, that creates an Array<Uri?>.
A nullable type can contain the type itself, but a non-null type cannot contain null. Conversion from Array<Uri> to Array<Uri>? is fine for an instance. However, due to variance in generics, you can't pass Array<Uri> as an argument of the type Array<Uri?>. More on that here.
You need an Array<Uri>!. The ! is a special syntax you can't use, but that indicates a platform call to a method with either a return type or argument with an undefined nullability type. What's important to keep in mind here is that you can pass a non-null type to it, or a nullable one for that matter. ! just means the compiler doesn't know, so it doesn't strictly enforce null-safety in the same way as it usually would.
For an instance, consider this method:
public void someMethod(String str) {}
Which will have the str parameter interpreted as a String! in Kotlin. Add #Nullable and it's String?, or #NonNull and it's String.
The key point, as outlined in the previous link, the method is interpreted as a potentially nullable array of non-null URIs:
Array <Uri> !
^ Type ^ Generic type (here: non-null) ^ signifies the type (NOT the generic type) is potentially nullable (not normally useable)
Anyway, the main issue here is that you're trying to pass Array<Uri> to Array<Uri?> - as outlined in the post I linked earlier, if you want to support that, you need to add out Uri? as the parameter. The method being a part of the stdlib means you can't do that.
If you're completely sure you'll never get a null type in the array after it's populated, you can, as you outlined in your answer, cast it. That being said, I'd highly recommend you use the safe cast operator in combination with let to avoid a crash if you get null. In case you didn't check, that can happen:
Returns
URI The URI of the data this intent is targeting or null.
You can go with something like Alexey Romanov mentioned - Run an if-else statement to check if data is null, and put it directly into an array using arrayOf if it isn't null. That being said, you should still handle getData() returning null, even if you go with the as approach.
The ? and !! syntax is very confusing in kotlin!
They are not Confusion both Symbol have different Meaning Kotlin.
To Explain Both Symbol :
Lets Take :
Symbol ? and it known as Safe Nullable cast operator
This Symbol work as Nullable. Means when you try to assign Value in Kotlin
val myString : String? = null
and Difference
val myString : String = null // This will give Error So you have to put string with null value (e.g.[""]).
and The Other Symbol !! and it is called as Null Safety:
It means Not Nullable.
When you pass have to Pass Value according to it's datatype. Means if it asks for string you have to pass String.
But when it receives null value or wrong response.
Then it will give you
NullPointerException
If you have any query then just comment it down i'll clear for you.

How to set not null for function params in kotlin

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

Does a.run same as a?.run in kotlin

If a variable is nullable in Kotlin, we need to either do a safety call ?., or !!. for explicitly call.
When I was trying to use some extensions(such as run or let) from nullable variable , I noticed that .run is fine and IDE did not complain it, usually I will receive a warning to remind me it is not a safety call.
Does it make any difference for ?.run{} and .run{} in kotlin? Is it considered as null safety if I use .run{} ?
var a? = "..."
a?.run{}
a.run{}
You'll need to safely handle the null somewhere.
Either when accessing a:
a?.run { }
Or when accessing this inside run on a:
a.run { this?.toSomething() }
Using a String? as an example, these both print null and the compiler is ok with both:
val x: String? = null
println(x.run { this?.toUpperCase() }) // prints null, type is String?
println(x?.run { this.toUpperCase() }) // prints null, type is String?

Kotlin data classes and nullable types

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.

Categories

Resources