I have the following function:
override fun countForTicket(dbc: SQLiteDatabase, ticketId: Long): Int {
var ret: Int
dbc.query(
TABLE_SECOND_CHANCE_PRIZES, arrayOf("count(id)"),
"ticket = ?", arrayOf(ticketId.toString()),
null, null, null
).use { c ->
ret = if (c.moveToFirst()) {
c.getInt(0)
} else {
0
}
}
return ret
}
The problem is that in line return ret ret is underlined with red and when trying to compile it gives me error:
Variable 'ret' must be initialized
From my point of view it seems that ret is always initialized. What am I missing?
Is it because the initialization is happening in a lambda and the compiler cannot guarantee that the variable is initialized?
The compiler isn't smart enough to know for sure the lambda will be run once, so it can't figure this out for you.
The reason we don't have this problem with many of the standard library higher-order functions is that they utilize contracts, which tell the compiler what they are doing with the lambda that is passed in (such as guaranteeing that the lambda will be called exactly once).
Unfortunately, Closeable.use() doesn't specify a contract (possibly because of it re-throwing exceptions?).
But use does return the result of calling the lambda, so you could do
val ret = dbc.query(...).use { c ->
if (c.moveToFirst()) {
c.getInt(0)
} else {
0
}
}
The compiler don't allow unsafe variables like that to be returned. A variable must always be something.
In your case, ret is initialized inside a lambda. The compiler doesn't know if this lambda is executed or not. If not, ret remains in its unsafe state. Throwing a NullPointerException at the end.
If you're sure that this variable is always assigned you can look at lateinit variables. You can also put a default value to it var ret = 0 and ommit the else statement.
Related
The following example of Kotlin source code returns an error when compiled:
fun main() {
var index: Int // create an integer used to call an index of an array
val myArray = Array(5) {i -> i + 1} // create an array to call from
val condition = true // makes an if statement run true later
if (condition) {
index = 2 // sets index to 2
}
println( myArray[index] ) // should print 2; errors
}
The error says that the example did not initialize the variable index by the time it is called, even though it is guaranteed to initialize within the if statement. I understand that this problem is easily solved by initializing index to anything before the if statement, but why does the compiler not initialize it? I also understand that Kotlin is still in beta; is this a bug, or is it intentional? Finally, I am using Replit as an online IDE; is there a chance that the compiler on the website simply is an outdated compiler?
The compiler checks whether there is a path in your code that the index may not be initialized based on all the path available in your code apart from the value of the parameters. You have an if statement without any else. If you add the else statement you will not get any compile error.
I implemented a LinkedList in kotlin and wrote a method to remove duplicates from it:
class Node (value:Int) {
var value = value
var next:Node? = null
fun addNodeToTail(value:Int){
var node = this
while (node.next != null) {
node = node.next
}
val newNode= Node(value)
node.next= newNode
}
fun removeDuplicates (){
val set = HashSet<Int>()
var node = this
set.add(node.value)
while(node.next != null){
if (set.contains(node.next?.value)){
node.next= node.next?.next
}else{
set.add(node.next.value)
node= node.next
}
}
}
}
In the last two lines:
set.add(node.next.value)
node= node.next
(and in the addNodeToTail method), the compiler says that smart cast is impossible because of complex expression. I have to add non-null asserted call (!!).
I want to understand why this solution is not accepted, although the while expression checks that node.next is not null. And I want to know if there is a better solution than using non-null asserted call (!!).
Thank you for your help
Pawel technically answered in the comment.
Basically smart casts are not always possible. In particular, if you define a mutable var of nullable type that is technically accessible by multiple threads, the compiler cannot guarantee that the value stays the same between the null check and the usage. That's why you get this error "smart cast impossible".
A common way of dealing with the problem is to store the value in a local val variable, to guarantee that this value will not change, and allow the compiler to smart cast it.
In your case though, it's not ideal because the while has to check the actual node's value every time. So you'll have to assert that the value is not null at some point, either with !! or with an elvis (?:) and an error() or throw.
I would personally go for:
while (node.next != null) {
val nextNode = node.next ?: throw ConcurrentModificationException()
if (set.contains(nextNode.value)) {
node.next = nextNode.next
} else {
set.add(nextNode.value)
node = nextNode
}
}
class Solution {
val message: String //error : val must be initialized or abstract
message = "love" //error : val cannot be reassigned
}
I understand what's happening in here - val cannot be reassigned.
So when I need val but can not initialize it i used to use by lazy
class Solution {
fun love(){
val message : String
message = "love" //this works
message = "hate" //this is error "val cannot be reassigned"
}
}
Here I can delcare val without initialization and later write codemessage = "love".what's happening here?
#deHaar noticed correctly that only var (mutable variable) is appropriate in your case.
The error you get is absolutely correct and expected.
what's happening here?
When you declare a read-only variable without initializing it you have to make sure that each execution path will have a value in this read-only variable. It means that Kotlin makes sure if your read-only variable was or was not initialized in every place it is used and raises errors if the variable is used inappropriately.
Here you have only one execution path as there are no when or if statements that can split execution into several possible paths.
class Solution {
fun love(){
val message : String
message = "love" // Kotlin knows that `message` was not yet initialized
message = "hate" // Kotlin knows that `message` was yet initialized! It does not allow to modify the value.
}
}
Here is what Kotlin documentation says:
... it is also possible (but discouraged) to split the declaration and the initial assignment, and even to initialize in multiple places based on some condition. You can only read the variable at a point where the compiler can prove that every possible execution path will have initialized it. If you're creating a read-only variable in this way, you must also ensure that every possible execution path assigns to it exactly once.
Example of an execution path
Using when or if statement you create two or more execution paths. Execution paths can be presented as a graph, I'll use #number as a node number. Example:
class Solution {
fun love(){
// #1
val message : String
if (System.currentTimeMillisec() % 2 == 0) {
message = "Not empty"
// #2
}
if (message.isEmpty) { // Error! Message could be not initialized at this point!
println("Empty message")
// #3
}
}
}
Looking at this example, that does not compile, we can calculate at least 3 execution paths.
#1 (none of the if statements was entered. All conditions are false)
#1 -> #2
#1 -> #3
Kotlin can calculate these paths and check if the message variable is initialized in every path it is used. As we can see, as soon as you reach the evaluation of the second if statement (in case of first and third paths) your program will crash because the message has no value. It has no address in memory and a computer which runs this program does not know how to get a value from an address that does not exist.
Now, let's modify this code to make it work:
class Solution {
fun love(){
// #1
val message : String
if (System.currentTimeMillisec() % 2 == 0) {
message = "Not empty"
// #2
} else {
message = ""
// #3
}
if (message.isEmpty) { // Error! Message could be not initialized at this point!
println("Empty message")
// #4
}
}
}
Execution paths:
#1 -> #2
#1 -> #3 -> #4
In this example, Kotlin is sure that the message read-only variable is initialized because there is a 100% chance that one of node 2 or node 3 will be executed. Right after the line where the message gets its initial value (initialized) Kotlin treats this variable as a read-only variable with a value.
Questions are welcome. I will try to simplify this answer.
bitmap1 = Bitmap.createScaledBitmap(
bitmap1, // <---- error is here
(width.toInt()),
(height.toInt()),
false)
numberOfInvaders ++
I also used bitmap2 and bitmap 1 in another class :
if (uhOrOh) {
canvas.drawBitmap(Invader.bitmap1, // <--- error is here
invader.position.left,
invader.position.top,
paint)
} else {
canvas.drawBitmap(Invader.bitmap2, // <---- and here
invader.position.left,
invader.position.top,
paint)
}
here its says : Type mismatch,
Required:Bitmap Found: Bitmap?
Yup, that's true :) You cannot use value like this, because it can be null at some point.
createScaledBitmap requires nonnullable Bitmap, but there is no guarantee that bitmap you use won't be null at the moment of calling given function.
So, what you can do?
Before the call check if bitmap is not null:
if (bitmap != null) { /* code here, still requires !! operator */ }
In multithreaded environment there is a risk that during execution of code block a value will change anyway, so you can use let function with ?. operator (basically the same operator like ., but executes only if value is not null). The block code will be invoked with an effectively final argument which is an instance you use to call this method, in this case "bitmap", called "context object", accessible via it keyword:
bitmap?.let { /* code here, bitmap is passed as effectively final, so for sure it's not null */ }
There other way would be !! operator (but it can finish with NPE exception, if value is null). Use only if you are sure that this value at that moment won't be null, otherwise you can crash your application.
Also, you can use ?: operator - this will take first value if not null, otherwise the second. It's quite nice, because you can use for example default value. Also, you can throw exception there ;)
bitmap ?: throw IllegalStateException("bitmap is null") // exception
bitmap ?: DEFAULT_BITMAP // default bitmap, if any
In this case you will get exception but with very communicative message (instead of just NPE).
bitmap1 = Bitmap.createScaledBitmap(
bitmap1!!, // !! <--- helps
(width.toInt()),
(height.toInt()),
false)
numberOfInvaders ++
if (uhOrOh) {
canvas.drawBitmap(Invader.bitmap1!!, // here
invader.position.left,
invader.position.top,
paint)
} else {
canvas.drawBitmap(Invader.bitmap2!!, // and here too
invader.position.left,
invader.position.top,
paint)
}
What is the syntax to return a value from a CATCH phaser from a block which is not a Routine?
sub foo() {
<1 2 3>.map: -> $a {
die 'oops';
CATCH { default { 'foo' } }
}
}
sub bar() {
<1 2 3>.map: -> $a {
die 'oops';
CATCH { default { return 'bar' } }
}
}
say foo(); # (Nil, Nil, Nil)
say bar(); # Attempt to return outside of immediatelly-enclosing Routine (i.e. `return` execution is outside the dynamic scope of the Routine where `return` was used)
edit: Desired output is:
say baz(); # (baz baz baz)
The use case is maping a Seq with a method which intermittently throws an exception, handling the exception within the block passed to map by returning a default value.
Return exits out of a function scope, but the way you are using it in bar() there are two functions at play.
The bar() method itself.
The lambda you embedded the return within.
This means that your return is ambiguous (well, at least to some people) and the compiler will balk.
Without the "return" the value in foo() is handled as a constant within the block, and the block returns Nil. This means that in foo() you effectively avoided parsing the meaning of return, effectively pushing a Nil on the stack.
That's why you have 3 Nils in the captured output for foo(). For bar() it is unclear if you wished to terminate the execution of the bar() routine on the first thrown exception or if you just wanted to pass 'bar' back as the non-Nil value the CATCH block pushed onto the stack.
The slightly modified version of your code
#!/bin/env perl6
sub foo() {
<1 2 3>.map: -> $a {
die 'oops';
}
CATCH { default { 'foo' } }
}
sub bar() {
<1 2 3>.map: -> $a {
die 'oops';
}
CATCH { default { return 'bar' } }
}
say foo();
say bar();
might make this a bit more clear. It's output is
[edwbuck#phoenix learn_ruby]$ ./ed.p6
Nil
bar
What's happening here is that lazy lists are causing the control flow to be non-obvious.
The return value of both of your functions is a Seq whose values get generated by calling the little lambda on the values a, b, and c in turn. Knowing this, it is easy to see why you can't change the return value of bar: bar has already returned before your lambda even gets called for the first time. Once the list is sayd, all values are generated and the exception is thrown.
The correct way to get what you want is to call .eager on the result of your map, thus causing the whole list to be evaluated before the function returns, which allows you to use return to change the value that bar returns.