How can I create an instance of this Subject with an Int:
class NonNullableBehaviourSubject<T : Any>(defaultValue: T) : Subject<T>() {
private val behaviourSubject = BehaviorSubject.createDefault<T>(defaultValue)
}
I then create an instance of NonNullableBehaviourSubject like this:
val emailValidationSubject = NonNullableBehaviourSubject(Int)
But I then get a compile error using this:
emailValidationSubject.onNext(error?.errorResId)
The compile error I get is:
Required: Companion.Int
Found: Int?
Also, Int must not be nullable. Yet, NonNullableBehaviourSubject does define a nullable parameter.
Your error object has nullable type, and you use safe call (?.) on it, so the whole expression is nullable.
To make the value non-nullable you can either assert value is not null (error!!.errorResId will crash if error is null, error?.errorResId!! will crash if the whole expression is null) or provide the fallback value for null case ((error ?: DEFAULT_ERROR).errorResId or error?.errorResId ?: DEFAULT_ERROR_RES).
Related
I expected that the type of a variable is promoted to a non-null type after a not-null check (like in the Dart language).
val someMap = mapOf("a" to 0L)
val a = someMap['a'] // a is of type Long?
if (a != null) {
val b = a // b is of type Long? and not of type Long. Why?
}
Can someone explain why this is not the case? Just a matter of taste of the language designers?
Since there is smart-casting, it doesn't matter. It will allow you to use members of a or b inside the if statement without null-safe calls (?.) or null assertions (!!). You can also safely declare b to be a Long without the compiler complaining:
if (a != null) {
val b: Long = a
}
It is I think a design choice for how implicit types should be inferred that b's type must be explicitly declared if you want it to be considered non-nullable. This is only relevant if passing it to a function with generics, since there is smart-casting.
What you can do instead of explicit null check is using let{} as follows:
val someMap = mapOf('a' to 0L)
val a = someMap['a'] // a is of type Long?
a?.let {
val b = it // b is of type Long
}
It is called smart casting, basically Kotlin is smart enough to determine that variable can no longer be null after check. More detail and can be found here if you are interested
As to why, only the creators of kotlin can know. But what you can do is this if you want a Long instead of Long? there is this
val b = a!!
I have an instance which can be null. Fox example
var str: String? = null
So I need to check if str is String. Do I need to check for null if I use the is operator.
First option:
if(str is String) {}
Second option:
if(str != null && str is String) {}
Please help me which way is better to use ?
The is operator is safe and returns false in the case you supply a null instance
https://pl.kotl.in/HIECwc4Av
Somewhere, you HAVE to nullcheck.
Kotlin provides many ways to enforce non-null:
Use a non-null type:
var nonNull : String = ""
var nullable : String? = "" // notice the ?
nullable = null // works fine!
nonNull = null // compiler error
and if you encounter a nullable type, you can use let {} ?: run {} construct to unwrap it and execute your code with a non-nullable:
nullable?.let { // use "it" to access the now non-null value
print(it)
} ?: run { // else
print("I am null! Big Sad!")
}
Kotlin strictly distinguishes between nullable T? and nonnull T.
Use T wherever possible to avoid null checks.
example:
fun main(){
var userInput: String?
//userInput = null
userInput = "asbdef"
var inputLength:Int? = userInput!!.length
println("Length of the string is :"+inputLength)
}
Output :
Length of the string is :6
fun main(){
var userInput: String?
userInput = null
//userInput = "asbdef"
var inputLength:Int? = userInput!!.length
println("Length of the string is :"+inputLength)
}
Output :
Unresolved reference: length
I want to know why it gives compile error?
If I just replace (!!) operator with (?) it compiles well but prints output as null.
PS: I'm newbie in Kotlin
The ?. operator short-circuits if the left side evaluates to null. So the result of nullVariable?.length is null and .length is never evaluated. Your first example is effectively doing:
println("Length of the string is :" + null)
The !! operator says "throw a NullPointerException if the left side is null. Otherwise, we know it's not null on the right side". You can see this if you change your second example a little:
val userInput: String? = null
val inputLength:Int = userInput!!.length // throws NullPointerException
However, I'm not sure why are you are getting Unresolved reference: length. It looks like that the compiler is doing some optimization when you assign null directly to userInput, so rather than compiling it to a String? which throws an NPE at runtime, it knows the value is only null (not String?) at compile time, and therefore can't have the length property because null is not a reference.
You can force it to give you to NullPointerException by adding a layer of abstraction via a function call:
fun main(){
var userInput: String? = "foo"
userInput = alwaysNull()
val inputLength:Int = userInput!!.length // NullPointerException
}
fun alwaysNull(): String? = null
I don't see anything n the Null Safety documentation about the difference in behaviour between initializing in one line / via a function call vs. assigning null directly to a var though, so what's happening under the hood to get Unresolved reference is just a guess.
Take a look at this kotlin one liner:
val nonNullArr : List<NonNullType> = nullArray.filter {it != null}
The compiler gives a type error at this line, saying that a list of nullables can't be assigned to a list of non-nulls. But the filter conditional makes sure that the list will only contain non null values. Is there something similar to !! operator that I can use in this situation to make this code compile?
It seems logical to assume that the compiler would take into account the predicate
it != null
and infer the type as
List<NonNullType>
but it does not.
There are 2 solutions:
val nonNullList: List<NonNullType> = nullableArray.filterNotNull()
or
val nonNullList: List<NonNullType> = nullableArray.mapNotNull { it }
As far as I know, you cannot convert nullable types into nonNull types by just verifying that they are not null. To achieve what you want, you need to manually map nullable values to non-null type by simply creating NonNull type object. For this you can use map extension function.
val nullableArray: Array<String?> = arrayOf("abc", "xyz", null, "efg")
val nonNullList: List<String> = nullableArray.filter { it != null }.map {
it.toString()
}
Or you can use filterNotNull() method as #forpas suggested in comments
val nonNullList: List<String> = nullableArray.filterNotNull()
Hope it helps you!
You can't assign a nullable type to a non-nullable type of value.
The type-matching maybe works when you assign a value, not after filter operation called.
// the type-matching works before `.filter` is called
val nonNullArr : List<NonNullType> = nullArray//.filter {it != null}
instead, if you want to do this without an error or without concerning the type. Remove the type from the val, so it goes like this
val nonNullArr = nullArray.filter {it != null}
Hope it helps
try using listOfNotNull instead of listOf(), it is equivalent to list.filterNotNull()
What is the difference between
x as? String
and
x as String?
They both seem to produce a String? type. The Kotlin page doesn't answer it for me.
UPDATE:
To clarify, my question is:
What is the purpose of having an as? operator at all, since for any object x and for any type T, the expression x as? T can be (I think) rephrased as x as T? ?
The difference lies in when x is a different type:
val x: Int = 1
x as String? // Causes ClassCastException, cannot assign Int to String?
x as? String // Returns null, since x is not a String type.
as? is the safe type cast operator. This means if casting fails, it returns null instead of throwing an exception. The docs also state the returned type is a nullable type, even if you cast it as a non-null type. Which means:
fun <T> safeCast(t: T){
val res = t as? String //Type: String?
}
fun <T> unsafeCast(t: T){
val res = t as String? //Type: String?
}
fun test(){
safeCast(1234);//No exception, `res` is null
unsafeCast(null);//No exception, `res` is null
unsafeCast(1234);//throws a ClassCastException
}
The point of the safe cast operator is safe casting. In the above example, I used the original String example with integers as the type. unsafeCast on an Int of course throws an exception, because an Int is not a String. safeCast does not, but res ends up as null.
The main difference isn't the type, but how it handles the casting itself. variable as SomeClass? throws an exception on an incompatible type, where as variable as? SomeClass does not, and returns null instead.