I'm accessing my Android apps SharedPreferences via
private val sharedPref = PreferenceManager.getDefaultSharedPreferences(context)`
and then attempting to get data from it using
val lat: String = sharedPref.getString("MyKey", "Default")
But this line gives me an error reading "Type mismatch. Required String, found String?"
According to the documentation the second parameter in the getString method says "Value to return if this preference does not exist. This value may be null."
So what's the point of having a default value then if the value can be null? I cannot seem to get the default value to ever be used and the only way I can get my code to work is to use the elvis operator and rewrite my code as:
val lat: String = sharedPref.getString("MyKey", "Default") ?: "Default"
Which looks ugly. Am I crazy? What am I missing?
Consider this in a such way:
Every String preference in SharedPreferences can exist or not and can be null or not. So the code
val lat: String = sharedPref.getString("MyKey", "Default") ?: "Not Set"
will return:
Default if the preference with this Key doesn't exists (means there is no mapping for this Key)
Not Set if the preference exists, but is null (mapping Key to null created)
any other value if the preference exists and the value of the mapping isn't null.
Update
Apparently, SharedPreferences are much less smart I thought it was. Having this code internally:
String v = (String)mMap.get(key);
return v != null ? v : defValue;
it will return null only if we'll pass null as a default value (and there were no value saved). This means we actually don't need an elvis option and we will not get "Not Set" value. This method returns nullable value, just because it allows you to pass nullable as a default value.
It's because kotlin Null-Safety is kick in when reading the following code:
val lat: String = sharedPref.getString("MyKey", "Default")
if you visit the SharedPreferences code, you can see the following code:
#Nullable
String getString(String key, #Nullable String defValue);
which is give us a probability to use null as defValue parameter. So, Kotlin try to guard it and give you the matching error:
"Type mismatch. Required String, found String?"
You can fix the problem by enabling nullable for your String variable with:
val lat: String? = sharedPref.getString("MyKey", "Default")
though this against Kotlin type system purpose:
Kotlin's type system is aimed at eliminating the danger of null references from code, also known as the The Billion Dollar Mistake.
SharedPreferences is an abstraction over Key/Value databasing provided by Google, how you use it is up to you. If you dislike this syntax, then create a wrapper or extension for your getString(). Just to give an example:
fun PreferenceManager.getStringTheFancyWay(key: String, defaultValue: String): String {
return getString(key, defaultValue) ?: defaultValue
}
val value = getStringTheFancyWay("a", "b")
Personally I dislike this, because null allows for a better control flow over non-existing keys.
This is how I use SharedPreferences
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
val value = preferences.getString("username", null)
if (value != null) {
toast("Hello $value")
} else {
toast("Username not found")
}
or
preferences.getString("username", null)?.let { username ->
toast("Hello $username")
}
Notice the difference?
The fact is simple, just imagine you haven't saved any value regarding to that key(for your case 'MyKey') and tried to get the value for that key(for your case 'MyKey'). What will SharedPreference return ? It will simply return the default value.
You will see that, you must assign null or any other string to default for String type, 0 or any other int value to default for integer type and true or false default value for bolean type. I hope you got the answer.
Related
I'm studying kotlin, but I'm very disappointed, I can not compare two Strings.
What is the right way to compare.
btn_login.setOnClickListener {
val login = input_email.text.trim()
val pass = input_password.text.trim()
if( login.equals( pass ) ){
startActivity<MainActivity>()
}
if (login?.equals(other = pass)){
startActivity<MainActivity>()
}
if (login == pass){
startActivity<MainActivity>()
}
}
According to documentation for structual equality use ==. It is translated to a?.equals(b) ?: (b === null).
In you case convert login and pass from SpannableStringBuilder to String.
val login = input_email.text.trim().toString()
Here is the example for matching the two strings using kotlin.
If you are using == (double equals) for matching the string then it's compare the address & return maximum time wrong result as per java documentation so use equals for the same
If you want to use equal ignore case then pass the true in the equals method of String
if (s1.equals(s2,true))
other wise you can just use this without boolean like
if (s1.equals(s2,false)) or if (s1.equals(s2))
compleate code is below
fun main(args: Array<String>) {
val s1 = "abc"
val s2 = "Abc"
if (s1.equals(s2,true))
{
println("Equal")
}
else
{
println("Not Equal")
}
}
Covert both the SpannableStringBuilder to string with toString, this should work.
val login = input_email.text.trim().toString()
val pass = input_password.text.trim().toString()
if (login == pass){
startActivity<MainActivity>()
}
1. == :
if ( string1 == string2 ){...}
2. equals :
Indicates whether some other object is "equal to" this one.
Implementations must fulfil the following requirements:
Reflexive: for any non-null reference value x, x.equals(x) should
return true.
Symmetric: for any non-null reference values x and y, x.equals(y)
should return true if and only if y.equals(x) returns true.
Transitive: for any non-null reference values x, y, and z, if
x.equals(y) returns true and y.equals(z) returns true, then
x.equals(z) should return true
Consistent: for any non-null reference values x and y, multiple
invocations of x.equals(y) consistently return true or consistently
return false, provided no information used in equals comparisons on
the objects is modified.
/** * Returns `true` if this string is equal to [other], optionally ignoring character case. * * #param ignoreCase `true` to ignore character case when comparing strings. By default `false`. */
public fun String?.equals(other: String?, ignoreCase: Boolean = false): Boolean
3. compareTo :
public override fun compareTo(other: String): Int
Compares this object with the specified object for order. Returns zero
if this object is equal to the specified other object, a negative
number if it's less than other, or a positive number if it's greater
than other.
public fun String.compareTo(other: String, ignoreCase: Boolean = false): Int
Compares two strings lexicographically, optionally ignoring case
differences
i know this is way too late, but as a newbie learning Kotlin, i had the same doubts.
then i came across this wonderful article that articulates the various string comparison types in Kotlin and the differences between them all.
in short both == and .equals() can be used to compare the value of 2 strings in kotlin.
hopefully that helps
With case checking
String a=.....
String b=.....
if(a==b){
}
IgnoreCase
if(a.equals(b,false))
KOTLIN:
if (editText1.text.toString() == editText2.text.toString() ) {
println("Should work now! The same value")
}
Try the following solution, see if it helps:
val passStr: String = textView.text.toString()
if( loginStr.compareTo(passStr, false) ){
startActivity<MainActivity>()
}
Try this surely will work.
val style = buildString { karthik}
val style2 = buildString { karthik }
var result = style.equals(style2)
if(result){//Do something}
I'm working in Kotlin Android.
The string is coming from the server, and it might contain digit, character, empty string or null value. I want to convert that value into double because I want to compare values.
So is there any better way to check that string contains only digit value and not empty or null in a Kotlin way. The list is huge, so I want an efficient way.
Price.kt
data class Price(
var value: String? = null
)
main.kt
val defaultValue : Double = 4.23
val list = listOf(Price("2.33"), Price("fr23"), Price(""), Price(null), Price("4.23"), Price("12.23"))
list.forEach{
if(it == defaultValue){
println("Found It")
}
}
Kotlin already has an efficient solution: toDoubleOrNull
Parses the string as a Double number and returns the result or null if
the string is not a valid representation of a number.
if (list.any { it.value.toDobleOrNull() == defaultValue }) {
println("Found It")
}
I got error message:
AddAddressActivity.kt: (69, 53): Type mismatch: inferred type is String? but String was expected
This is because the getString method returns a nullable value (String?) but you are assigning it to something that takes a non-null value (expects String). If you want to fix it you could assign it to a default string if it is null like this
val villageId = getString("village_id") ?: "default"
or to an empty string
val villageId = getString("village_id").orEmpty()
This is due to your villageId can be null which you have got from getString("village_id")
for solving this you can use default value so when bundle don't contains the provided key it will eventually returns the default value.
val villageId = getString("village_id", "My Default Value")
or you can use kotlin elvis to get other value when it's null.
val villageId = getString("village_id") ?: "My Default Value"
or if you're sure that intent will always contain the village id then you can use !!(not-null assertion operator) which will convert non-null value if not null else will throw exception
val villageId: String = getString("village_id")!!
you can more read about null safety in following link Null safety | Kotlin
from an API call i get as response a body with this structure
open class BaseResponseEntity {
#SerializedName("result")
val result: ResultEnum = ResultEnum.NONE
#SerializedName("errorCode")
val errorCode: String = ""
#SerializedName("errorMessage")
val errorMessage: String = ""
#SerializedName("successMessage")
val successMessage: String = ""
#SerializedName("value")
val code: LastPaymentCodeModel?
}
where the field "value" can be three types: null, String or LastPaymentCodeModel. How can i get this?
I managed to put a ? so that both null and LastPaymentCodeModel are handled, but i don't know how to handle the String type too.
I think the best approach would probably be to use type Any? for code.
Then you should write a custom GSon serializer/deserilizer (JsonDeserializer<BaseResponseEntity>) for the BaseResponseEntity object.
In this Json deserializer, you would need to check the type of value (e.g is it a string or a data structure) and decode it to the correct object type.
Alternative, to avoid the use of Any?, you could leave the model exactly as you have it. You will still need to write a custom JsonDeserializer, however if value is a string then it would still create a LastPaymentCodeModel, using the string value as one of it's properties.
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