Kotlin Function Generics - Upper Bound Not Working - kotlin

I faced some issue regarding usage of Kotlin generics in functions
fun <T : CharSequence> doSomething(): T {
return String() as T
}
class Something(intValue: Int)
Something(doSomething()) // Doesn't show any compile error
Now when it is executed it throws error
java.lang.ClassCastException: class java.lang.String cannot be cast to class java.lang.Number
Wanted to know why Kotlin compiler is not throwing error for incompatible typecasting

I think what you are seeing is the major compiler bug KT-47664. Though in the bug report they used a much more complex example to demonstrate the issue, the cause of the bug is the same, that being the compiler has inferred an empty intersection type (the intersection of CharSequence and Int is empty) as the type parameter.
The algorithm apparently treats an empty intersection type the same as any other type, doesn't think anything special of it, and so type inference succeeds.
This bug has been fixed by KT-51221 Deprecate inferring type variables into an empty intersection type. From what I understand from reading the reports, there will now be a warning if an empty intersection type is inferred. However, the fix is only included in Kotlin 1.7.20+, which at the time of writing, is not released yet :(

Related

Kotlin compiler shows compilation error and suggests to "change type from 'T' to 'T"' while there is only one T in the context

I tried to implement some type classes from Haskell but confronted the issue that is probably a bug in the Kotlin compiler.
interface Semigroup<Instance> {
infix fun Instance.assocOp(oother: Instance): Instance
}
inline fun <reified T: Semigroup<T>> Iterable<T>.concat() = this.reduce<T, T> { acc: T, t: T -> acc.assocOp(t) }
The error message is "Expected parameter of type T".
IDEA suggests to "Change type from 'T' to 'T'" (does nothing).
I expect acc to belong to the type T mentioned in generics. But because of some reason compiler tries to find some other type T. I tried to
specify the type explicitly/implicitly
build ignoring IDEA message
change used version of Kotlin compiler (I have tried 1.4.20, 1.4.10, 1.3.72).
Nothing worked.
I suppose that writing the function without reduce (manually) may help to deal with it. Also, writing java code doing the same may help to mitigate the problem. But these solutions are only workarounds for the problem. Is the issue my fault or the compiler bug?
The compiler error clearly is not helpful here. However, it is correct that the code should not compile IMO.
You're defining the method assocOp as a member extension function. The extension applies to any type T, but it's a member of the interface Semigroup<T>.
To call that extension, you need both a receiver or type T and a receiver of type Semigroup<T> (acting as a context).
In your case, the type T both plays the role of the generic type parameter and of the Semigroup<T>, but you still need to have 2 "receivers" for your extension, even if both are the same instance.
Maybe try this:
inline fun <reified T : Semigroup<T>> Iterable<T>.concat(): T =
reduce<T, T> { t1: T, t2: T -> with(t1) { t1.assocOp(t2) } }
The with(t1) { ... } provides a context of type Semigroup<T>, while the t1 used in t1.assocOp(t2) acts as the T receiver.

What is the scope of casted variable in Kotlin?

When casting a variable at the right side of the assign, i'm surprisely realize that the variable still behave as the casted type and not as it was original defined.
Am i doing something wrong or it's a compiler issue?
Code:
val hippoList = listOf<Hippo>(Hippo())
val hippoMutableList : MutableList<Hippo> = hippoList as MutableList<Hippo>
hippoList.add(Hippo())
since hippoList is from a List type, it is immutable. So how does trying to run add function on an immutable type isn't cause to compilation error?
If you're doing casting it means that you know more than a compiler about this context of execution and you're telling the compiler that this hippoList is a MutableList so on every next usage of hippoList compiler already knows that this have to be a MutableList and allows you to use add method because you casted it to MutableList previously. In fact you will get a runtime error UnsupportedOperationException which means that you didn't really know more about this context of execution and you did something wrong. So instead of using casting on your own allow compiler to do it's work.
In your case instead of a casting to MutableList, transform hippoList to MutableList with
hippoList.toMutableList()
The same happens when you're using !! from nullable type to not null type, when you're using it when you know more than a compiler about the context of execution. Here's a little example
val someNullableType: String? = null
val thisStringIsNotNull = someNullableType!!
by using !! on someNullableType we're telling the compiler that someNullableType is not null as well, so we're allowed to write (as in you're case where you're telling that your List is a MutableList as well)
someNullableType.length
but we will receive exception earlier (in place where we used !! to tweak the compiler)

Kotlin "no cast needed" in IDE

I'am new to Kotlin (and Java) so may be a stupid question, but IntelliJ keeps telling me "No cast needed" on the second function call. If i switch the order of the functions the same for the other functions.
I could imagine 2 things:
Kotlin is smart it knows: Hey first cast is fine, so i will cast the second
IntelliJ problem ?
(this as Exec).setVersionToDeploy()
(this as Exec).setEcsTaskMemory()
Both functions are defined as (Gradle-Plugin):
fun Exec.XX()
Your first guess is correct!
This is known as a smart cast: the compiler knows that, if execution reaches your second line, the type of this must be Exec (else the first line would have thrown a ClassCastException and it wouldn't have reached the second line).  So it infers the specific type, and a further cast is not needed
In general, the compiler infers types in cases such as this, so you don't need to cast explicitly.  (It's not an error to do so, only a warning; but IDEA is very keen on showing ways your code can be improved.)
You see this most commonly with nullability (since that's part of the type system).  For example, if you have a nullable field, the compiler won't let you call its methods directly:
val myString: String? = "abc"
println(myString.length) // COMPILE ERROR, as myString could be null
but if you add a manual check, the compiler smart-casts the field to its non-nullable type, so you don't need a cast:
val myString: String? = "abc"
if (myString != null)
println(myString.length) // OK; compiler infers type String

how to convert Java Map to read it in Kotlin?

I am facing some very basic problem (that never faced in java before) and might be due my lack of knowledge in Kotlin.
I am currently trying to read a YML file. So Im doing it in this way:
private val factory = YamlConfigurationFactory(LinkedHashMap::class.java, validator, objectMapper, "dw")
Best on Dropwizard guide for configurations.
https://www.dropwizard.io/1.3.12/docs/manual/testing.html
So later in my function I do this"
val yml = File(Paths.get("config.yml").toUri())
var keyValues = factory.build(yml)
When using my debugger I can see there is a Map with key->values, just as it should be.
now when I do keyValues.get("my-key")
type inference failed. the value of the type parameter k should be mentioned in input types
Tried this but no luck
var keyValues = LinkedHashMap<String, Any>()
keyValues = factory.build(yml)
The YamlConfigurationFactory requires a class to map to, but I dont know if there is a more direct way to specify a Kotlin class than with the current solution +.kotlin, like
LinkedHashMap::class.java.kotlin
Here it also throws an error.
Ideas?
Well, this is a typical problem with JVM generics. Class<LinkedHashMap> carries no info on what are the actual types of its keys and values, so the keyValues variable always ends up with the type LinkedHashMap<*, *> simply because it can't be checked at compile time. There are two ways around this:
Unsafe Cast
This is how you would deal with the problem in standard Java: just cast the LinkedHashMap<*, *> to LinkedHashMap<String, Any> (or whatever is the actual expected type). This produces a warning because the compiler can't verify the cast is safe, but it is also generally known such situations are often unavoidable when dealing with JVM generics and serialisation.
YamlConfigurationFactory(LinkedHashMap::class.java, ...) as LinkedHashMap<String, Any>
Type Inference Magic
When using Kotlin, you can avoid the cast by actually creating instance of Class<LinkedHashMap<String, Any>> explicitly. Of course, since this is still JVM, you lose all the type info at runtime, but it should be enough to tell the type inference engine what your result should be. However, you'll need a special helper method for this (or at least I haven't found a simpler solution yet), but that method needs to be declared just once somewhere in your project:
inline fun <reified T> classOf(): Class<T> = T::class.java
...
val factory = YamlConfigurationFactory(classOf<LinkedHashMap<String, Any>>(), ...)
Using this "hack", you'll get an instance of LinkedHashMap directly, however, always remember that this is just extra info for the type inference engine but effectively it just hides the unsafe cast. Also, you can't use this if the type is not known at compile type (reified).

I cannot understand how Dart Editor analyze source code

Dart Editor version 1.2.0.release (STABLE). Dart SDK version 1.2.0.
This source code produces runtime exception.
void main() {
test(new Base());
}
void test(Child child) {
}
class Base {
}
class Child extends Base {
}
I assumed that the analyzer generates something like this.
The argument type 'Base' cannot be assigned to the parameter type 'Child'
But I can only detect this error at runtime when occurred this exception (post factum).
Unhandled exception:
type 'Base' is not a subtype of type 'Child' of 'child'.
The analyzer is following the language specification here.
It only warns if a the static type of the argument expression is not assignable to the type of function the parameter.
In Dart, expressions of one type is assignable to variables of another type if either type is a subtype of the other.
That is not a safe type check. It does not find all possible errors. On the other hand, it also does not disallow some correct uses like:
Base foo = new Child();
void action(Child c) { ... }
action(foo); // Perfectly correct code at runtime.
Other languages have safe assignment checks, but they also prevent some correct programs. You then have to add (unsafe/runtime checked) cast operators to tell the compiler that you know the program is safe. It's a trade-off where Dart has chosen to be permissive and avoid most casts.
Let's try to be polite and answer the question without any prejudice.
I think I understand what you expected and here my angle on what the error means:
You are invoking the method with the argument of type Base
The method is expecting an argument of type Child
The Child is not equal to the Base, neither is a subtype of it (as a fact it is the Child that is a subtype of the Base)
It is working as expected as it makes only sense to provide object of the expected type (or it's subtypes - specialisations).
Update:
After reading again your question I realised that you are pointing out that editor is not finding the type problem. I assume this is due to the point that Dart programs are dynamic and hence certain checks are not done before the runtime.
Hope it helps ;-)