Parameter specified as non-null is null - kotlin

I have a function inside of a larger project that is called on startup via another function. Here's the code for it:
val infoDoc = File("res/info.txt")
private fun readInfo(): MutableList<Reminder> {
val reminders = mutableListOf<Reminder>()
infoDoc.forEachLine {
//...
}
return reminders
}
However, when I attempt to run it, I get this error:
java.lang.ExceptionInInitializerError
Caused by: java.lang.IllegalArgumentException: Parameter specified as non-null is null: method kotlin.io.FilesKt__FileReadWriteKt.forEachLine, parameter $receiver
at kotlin.io.FilesKt__FileReadWriteKt.forEachLine(FileReadWrite.kt)
at kotlin.io.FilesKt__FileReadWriteKt.forEachLine$default(FileReadWrite.kt:154)
at main.IoKt.readInfo(io.kt:22)
at main.IoKt.read(io.kt:8)
at main.AppKt.<clinit>(app.kt:23)
Exception in thread "main"
Note that Reminder is a custom class, and that infoDoc is declared in app.kt. I can provide any other code that is required.
Edit: There are checks in place to make sure that info.txt exists, and is readable/writable.

The exception happens because infoDoc is not initialized yet. Initializers in Kotlin are executed from top to bottom, so it looks like the initializer of a property declared above infoDoc in app.kt is calling the readInfo function.
To fix the problem, move the declaration of infoDoc above the place where readInfo is called.

Check if info.txt exists, or try the absolute file path.

Related

How to use spyk

I need to verify that a method has been called on an object. So I make a spy of this object:
obj = spyk(obj)
And then I verify that a method has been called:
verify(exactly = 1) { obj.f(3) }
The test fails with the following error message:
java.lang.AssertionError: Verification failed: call 1 of 1: obj(#2).f(eq(3))) was not called.
However I can clearly see the method f being called:
I can break in that method in debug mode
I print out hello world from f() in that method and see it being printed.
How do I use spies correctly in mockk?
P.S.
I tried doing val obj = spyk(obj()) but I get lateinit has not been initialized error because I need to set a parameter in obj as so:
obj.setDependency(friend)
But in the case where I first do val obj = spyk(obj()) and then call obj.setDependency(friend) like I explained above I end up with a lateinit has not been initialized error
Can someone please help me resolve this issue?
In your case, I don't understand this part obj = spyk(obj). What are you doing here? I think this part don't even compile.
You receive lateinit has not been initialized error because spyk(obj()) calls real constructor. If your object has dependencies, you have to create them too or pass mockk instead of them.
According to the documentation:
Note: the spy object is a copy of a passed object.
You have to create this object like a normal object, so all dependencies have to be filled.
I am using spyk in this way, let me show you a quick example.
fun `should call method testMethod`() {
val spy = spyk<TestClass>()
spy.testMethod(1)
verify (exactly = 1) { spy.testMethod(1) }
}

Kotlin - IllegalArgumentException

Not sure what is causing this, but i am trying request data from the api and i am getting an IllegalArgumentException
package com.example.tmdb
import com.google.gson.Gson
import org.jetbrains.anko.doAsync
import org.jetbrains.anko.uiThread
class MainPresenter(private val view:MainView, private val apiRepository: ApiRepository, private val gson: Gson){
fun getMovieList(){
doAsync {
val data = gson.fromJson(apiRepository.doRequest(TMDBApi.getMovie()),
MovieResponse::class.java)
uiThread {
view.showMovieList(data.result)
}
}
}
}
Line in which i am getting exception is
view.showMovieList(data.result)
and
class MainPresenter(private val view:MainView, private val apiRepository: ApiRepository, private val gson: Gson){
Error message showing in Logcat is:-
java.lang.IllegalArgumentException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull, parameter data
at com.example.tmdb.MainActivity.showMovieList(MainActivity.kt)
at com.example.tmdb.MainPresenter$getMovieList$1$1.invoke(MainPresenter.kt:14)
at com.example.tmdb.MainPresenter$getMovieList$1$1.invoke(MainPresenter.kt:7)
at org.jetbrains.anko.AsyncKt$uiThread$1.run(Async.kt:71)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6776)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1496)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1386)
Parameter specified as non-null is null. What this means is that data.result is coming back as null. There could be multiple reasons for that, the most common one I can think of is that the json that is returned from the api call isn't the same object as what you said in MovieResponse::class.java.
Regardless, the error java.lang.IllegalArgumentException: Parameter specified as non-null is null comes up when you tell the compiler that the param passed into this function will never be null but is in fact null
I'm doing a bit of guessing here, but I assume that the following are true:
MovieResponse is defined in Java
showMovieList() is defined in Kotlin and accepts only non-null values
Values coming from Java are treated specially in Kotlin: you're allowed to work with them as though they are non-null, but null values are allowed. This is a language feature that is meant to make your life better, as the alternative is to treat every value that comes from Java as nullable.
You can read more about this here: https://kotlinlang.org/docs/reference/java-interop.html#null-safety-and-platform-types
What this means for you is that you don't get compile-time null safety, and have to solve the problem yourself. Clearly data.result is null sometimes, so you need to make sure to handle this scenario.
Replace these lines:
uiThread {
view.showMovieList(data.result)
}
with these:
data.result?.let {
uiThread {
view.showMovieList(it)
}
}

Overriding variable creates a NPE when variable is used in the initialization in the super class

Say we have the following set up:
open class Foo(open val img: Image) {
val use = img.graphics
}
class Bar(override val img: BufferedImage) : Foo(img)
On initialization, the use of img.width creates a NPE. What I think the issue is, is that apparently even though img is passed in the constructor of Foo directly in Bar, when it is used in Foo it doesn't point to it, instead it points to the overridden variable in the Bar class. How can I avoid this?
This behavior is caused by the fact that the getter of img is overridden and returns the value of a different field (since Bar overrides img with a different type, it requires creating an additional field of type BufferedImage). The Foo constructor is executed before that field is assigned in Bar.
In general, you should avoid using open members in the initialization logic of your classes, because they may be overridden in the subclasses and may rely on some state that, upon the superclass initialization time, is not properly initialized yet.
For your particular case, make img in the Foo constructor a simple parameter and use the parameter explicitly:
open class Foo(img: Image) {
open val img = img
val use = img.graphics
}

Create Method via GDSL script that has a delegating closure parameter

Using the (scarcely documented) gdsl scripts of Intellij, one can add dynamic methods to a class:
contributor(context(ctype: "my.Type")) {
method name: "doIt", params: [body: {}], type: void
}
One can also configure the delegation of a closure:
contributor(context(scope: closureScope())) {
def call = enclosingCall("doIt")
if (call) {
def method = call.bind()
def clazz = method?.containingClass
if (clazz?.qualName == 'my.Type') {
delegatesTo(findClass('my.Inner'))
}
}
}
Which, when doIt is a method that is defined in the code (not dynamically added), also works as designed.
However, when using the closureScope with the previously created method, the containing class method is always null, meaning that I can not safely delegate inside the closure to the addressed my.Inner class.
What I want is adding a dynamic method equivalent to:
void doIt(#DelegatesTo(my.Inner) Closure)...
I.e. I want the method to be available in code completion (this works), and inside the so created closure, I want correct code completion when addressing methods of my.Inner.
So far, I tried various approaches:
include the #DelegatesTo annotation in the param definition
try more esoteric approaches in finding the owner of the closure, which fails because the GrMethodCall simply has no parent
unconditionally delegating all closures named doIt to my.Inner which works, but is no viable solution since I do have multiple doIt methods (on different classes) delegating to different targets.
So, how can I make IDEA behave as expected and delegate to the correct target?
Edit to make it clearer:
Given the following classes:
package my
class Type {
void doIt(Closure) {}
}
class Inner {
void inInner() {}
}
and the following gdsl:
contributor(context(scope: closureScope())) {
def call = enclosingCall("doIt")
if (call) {
def method = call.bind()
def clazz = method?.containingClass
println clazz?.qualName
if (clazz?.qualName == 'my.Type') {
delegatesTo(findClass('my.Inner'))
}
}
}
when I start typing in a new script:
new Type().doIt {
inInner()
}
When inside the closure, I get the following:
code completion for inInner
inInner is shown as valid
The console output when started with idea.bat from commandline shows the line my.Type (from the println)
Ctrl-B on inInner correctly links to source code.
(The same behaviour can be reached without the gdsl when annotation the Closure Parameter in the doIt method with #DelegatesTo(Inner))
However, I do not want to manually include the doIt method in the source of Type, it is generated by an AST Transformation, so my source file now looks like this:
package my
class Type {
}
class Inner {
void inInner() {}
}
I can tell IntelliJ about the new method using the following gdsl snippet
contributor(context(ctype: "my.Type")) {
method name: "doIt", params: [body: {}], type: void
}
Now the IDE correctly recognizes the doIt method with a closure parameter. However, inside the Closure, the following happens:
sometimes code completion shows inInner, sometimes after changing something, it does not (when using the original code to fix a type, it was shown, but later declared "unresolved", after going through the code changes of this edited example, it is not shown anymore...)
Even when shown, inInner is shown with "cannot resolve symbol" decoration
the console shows null as clazz, i.e. the method is found, but not linked to an owner ASTNode
Ctrl-B does not link to the corresponding method in Inner
So what I want is the same behaviour for an injected doIt method (via Gdsl) as with a method included in the source, i.e. I want the gdsl to inject a doIt method with a delegating closure (to Inner) into the type class.
This worked for me adding the ctype to scope insted of finding the class type from the method
contributor(context(scope: closureScope(), ctype: 'my.Type')) {
def call = enclosingCall("doIt")
if (call) {
delegatesTo(findClass('my.Inner'))
}
}

HashMap errors - containsKey, get

Can anyone shed some light?
Problem code:
protected var table = HashMap<Class<*>, Double>()
if (table.containsKey(object)) {
value = table.get(object)
}
containsKey(K):Boolean is deprecated. Map and Key have incompatible
types. upcast to Any? if you're sure
so I changed it to this:
if (table.containsKey(object as Any?)
which fixes the error, but is this what I should have done to fix it? or is there a better way?
also .get(object) has an error:
Type inference failed. required: kotlin.Double found kotlin.Double?
same error message for this too:
val c = someObject.javaClass // pre j2k code: final Class<? extends SomeClass> c = someObject.getClass();
weight = weightingTable[c] <-- error here
I don't know what to do here
The containsKey call is reported as an error because the type of the argument you pass to it does not match the type of the map key. Your map contains classes as keys, and you're trying to pass an object instance to it. Changing this to object as Any? is not a useful fix, because this call will compile but will always return false. What you need to do instead is to use object.javaClass to get the class of the object.
The weightingTable[c] call is reported as an error because the map does not necessarily contain a value for the key you're passing to it, so the result of the [] operation is nullable. You cannot assign a nullable value to a non-null variable without somehow handling the null case (using a check, an explicit non-null cast or some other option covered in the documentation).
When doing:
myHashMap.get(object)
and getting:
Type inference failed. required: kotlin.Double found kotlin.Double?
even when you already checked with containsKey. You can solve it by using:
myHashMap.getOrElse(key) { defaultValue }