Why is getMessage() an unresolved reference in Kotlin with an Exception class? - kotlin

Kotin documentation says that "All exception classes in Kotlin are descendants of the class Throwable. Every exception has a message, stack trace and an optional cause."
The Java documentation for Throwable shows a getMessage() method. But the Kotlin documentation for Throwable does not have a getMessage(). So this code:
fun main(args: Array<String>)
{
try
{
println("args size: ${args.size}");
}
catch (e: Exception)
{
println(e.getMessage())
System.exit(1)
}
}
gives me this compile error:
test_exception.kt:12:17: error: unresolved reference: getMessage
println(e.getMessage())
^
suggesting that I am using a Kotlin Exception class derived from a Kotlin Throwable class.
However, if I change getMessage() to toString() and add a throw:
fun main(args: Array<String>)
{
try
{
println("args size: ${args.size}");
throw Exception("something went wrong")
}
catch (e: Exception)
{
println(e.toString())
System.exit(1)
}
}
I get this message:
java.lang.Exception: something went wrong
Which seems to say that the Exception class is NOT a Kotlin Exception class - but the java version which has a getMessage() method and I shouldn't get a compile error when I try to use it.

There is no other Throwable except for java.lang.Throwable. This class is used both by Java and by Kotlin programs.
The fact that Throwable has an entry in the Kotlin documentation suggests that this is a special compiler "alias". That means that for all intents and purposes it is a Kotlin class, instances of which are represented by java.lang.Throwable. Black magic.
TL;DR:
The equivalent of e.getMessage() in Kotlin is e.message, which is described in the docs as open val message: String?.
Since Throwable is not used directly from Java, but mapped to Kotlin, you cannot use the Java notation e.getMessage() for the property. Here is more about mapped types: http://kotlinlang.org/docs/reference/java-interop.html#mapped-types

Related

When I use the generic method in Route then I am getting compiler Backend Internal error: Exception during IR lowering in Ktor

I am trying to create a dynamic service based on the data class model defined by user and they registerDataModels() method appDataModule() it should automatically create all based method in the router service. When I try to achive using generics in those method I am getting a compiler error. Is there any other better way to dynamically create route methods like by defining the datamodel by developer and then service should be automatically created?
org.jetbrains.kotlin.backend.common.BackendException: Backend Internal error: Exception during IR lowering
File being compiled: */api/AppConfigService.kt
The root cause java.lang.RuntimeException was thrown at: org.jetbrains.kotlin.backend.jvm.codegen.FunctionCodegen.generate(FunctionCodegen.kt:47)
File is unknown
The root cause java.lang.AssertionError was thrown at: org.jetbrains.kotlin.codegen.coroutines.CoroutineTransformerMethodVisitor.spillVariables(CoroutineTransformerMethodVisitor.kt:636)
fun Application.registerDataModels() {
appDataModule<M1>()
appDataModule<M2>()
appDataModule<M3>()
}
inline fun <reified T: DBModel> Application.appDataModule() {
routing {
createAppData<T>()
updateAppData<T>()
deleteAppData<T>()
}
}
inline fun <reified T: DBModel> Route.createAppData() {
put("/api/data/${getName<T>()}/create") {
authenticated {
create<T>{}
}
}
}
inline fun <reified T: DBModel> Route.updateAppData() {
put("/api/data/${getName<T>()}/update") {
authenticated {
update<T>{}
}
}
}
inline fun <reified T: DBModel> Route.deleteAppData() {
put("/api/data/${getName<T>()}/delete") {
authenticated {
delete<T>{}
}
}
}
Note: This answer assumed that code would be loaded at runtime, which seems not to be the case, and is therefore not completely matching OP's question.
You are using inline functions with reified.
To make a long story short, inline functions are compiled and 'copied' to the location where they are being used, already with a fixed (thats what reified does) class. So when you use an inline function
inline fun <reified T> foo(t: T): T { ... }
and you call it like this:
val myVal = foo("test").uppercase()
then at compile time of that calling line of code, the type of T is known to be String and the target line is compiled accordingly, so you know at runtime which type T is within your function.
It is (for this one calling line) as if that function was like this to begin with:
fun foo(t: String): String { ... }
Because you want to compile these classes dynamically, however, this process fails, because the class obviously does not exist yet. This is simply due to the nature of reified. If you can somehow remove it, it might work.
I agree that the error message of the compiler could be more telling here. Maybe you can raise a task on kotlin's issue tracking platform?: https://youtrack.jetbrains.com/issues/kt?_gl=1*5r6x4d*_ga*MTQyMDYxMjc2MS4xNjMzMzQwMzk5*_ga_9J976DJZ68*MTY2OTM1NjM1MS4yMS4xLjE2NjkzNTYzNTcuMC4wLjA.&_ga=2.265829455.1332696793.1669356352-1420612761.1633340399

Kotlin compiler not warning about potential null pointer exception

I have this simple kotlin snipped that tries to map list elements to strings like this:
sealed class MySealedClass
class MyClass1 : MySealedClass()
class MyClass2 : MySealedClass()
fun doTheMapThing(elements: List<MySealedClass>): List<String> {
return elements.flatMap {
when (it) {
is MyClass1 -> listOf("Yeah")
is MyClass2 -> null
}
}
}
In the flatMap, the when either maps to a list or to null, so the effective return type of the when is List<String>?. I was a bit surprised to see this compiling without any issues. Shouldn't the kotlin compiler warn about this? When I extract the when to a function, I get a proper compiler error. Am I missing something here or could this be a bug?
Using kotlin 1.5.31
Just for completeness: It was a bug. The fix will be released with Kotlin 1.7.0. More details on that can be found here: https://youtrack.jetbrains.com/issue/KT-49658
If you're interested in even more details, you can find the actual fix here: https://github.com/JetBrains/kotlin/commit/37d163d417bfe8ecd2e4baea3e5651906c96e150
A nice thing is that our example actually made it into the test code for kotlin:
fun doTheMapThing1(elements: List<CharSequence>): List<String> {
return elements.flatMap {
<!TYPE_MISMATCH_WARNING!>when (it) { // NullPointerException
is String -> listOf("Yeah")
else -> null
}<!>
}
}

Why does Mockito throw an "InvalidUseOfMatchersException" at for this code?

I am writing a test for a Spring-Boot project written in Kotlin 1.5. This piece of code does fail at test runtime with an InvalidUseOfMatchersException and I struggle to figure out why:
#SpringBootTest
#AutoConfigureMockMvc
internal class ControllerTest {
#Autowired
lateinit var mockMvc: MockMvc
#MockBean
lateinit var mockedAuthFilter: AuthFilter
#BeforeEach
fun setup() {
assertNotNull(mockedAuthFilter)
`when`(
mockedAuthFilter.shouldProceed(
any(HttpServletRequest::class.java),
any(AuthConfig::class.java)
)
).thenReturn(true)
}
#Test
fun `This call should return without an error`() {
mockMvc.perform(get("/api/entities")).andDo(print()).andExpect(status().isOk)
}
}
All I can find in the web for this error is that you tried an argument matcher on a basic type - but that is not the case here. Have you faced this problem and how did you solve it?
From my understanding the failure comes from a masked nullpointer exception caused by the shouldProceed method itself which doesn't allow a call with nulled arguments, which kind of happens internally in Mockito during the setup of the mocked instance after any() returns.
The solution was to not use Mockito with Kotlin, but the MockK test utility instead.

Create an instance of Exception and modify its message

In Kotlin/Java, I would like to inherit from Exception but change the message when the Exception is instantiated. Something like this:
import java.lang.Exception
class TestAlreadyAddedException : Exception {
constructor(details: String) {
this.message = "Test already added: {details}"
}
}
But this is not allowed because Kotlin complains that message is a val that cannot be re-assigned. Is there a way around this?
The Exception class takes it's message as a constructor parameter.
This should do it:
constructor(details: String): super(details) {
}
You can simply do this:
Create simple exception class
class TestException(message:String): Exception(message)
Use it like this
throw TestException("Test already added: $message")

Kotlin SAM Runtime Error: NoSuchMethodError: No static method

Today I ran into a really strange runtime error while developing kotlin / android that involves SAM conversions and sub classing.
Here's a minimal example of pure java + kotlin. Here are two java classes:
public class A {
public interface I {
public void f();
}
public I i;
}
public class B extends A {}
And here is a kotlin main function:
fun main(args: Array<String>) {
A().i = B.I {}
}
This code compiles fine but at run time I get the following error:
Exception in thread "main" java.lang.NoSuchMethodError: B.I(Lkotlin/jvm/functions/Function0;)LA$I;
at MainKt.main(Main.kt:2)
Now, this is already bad -- if code like this does not work (it never will I guess) the compiler should raise an error. But at least one could say that it is bad idea to reference to the interface I via the subclass B instead of the place of definition A (i.e., A.I).
It's less clear though, if this code is in a sub class of B where I can reference I directly using I:
class C: B {
constructor() {
this.i = I {}
}
}
So my questions would be:
Why is this behavior happening at all?
If it is happening, why is the compiler not raising an error already?
PS: In android the error message looks similar to this, which is even more confusing:
Caused by: java.lang.NoSuchMethodError: No static method OnFocusChangeListener(Lkotlin/jvm/functions/Function2;)Landroid/view/View$OnFocusChangeListener; in class Landroid/widget/LinearLayout; or its super classes (declaration of 'android.widget.LinearLayout' appears in /system/framework/framework.jar:classes2.dex)
Define main method as static like-
companion object {
#JvmStatic fun main(args: Array<String>) {
A().i = B.I {}
}
}