Safely casting jOOQ's TableField<*, *> to TableField<*, {some object type}> in Kotlin - kotlin

So I have this function in Kotlin:
fun getJooqOperator(field: TableField<*,*>, value: String): org.jooq.Condition {
// In this case, "this" is the operator enum.
// The combination of field and operator is strictly handled in the front-end (Elm)
return when (this) {
EQ -> (field as TableField<*, String>).eq(value)
NEQ -> (field as TableField<*, String>).ne(value)
GT -> (field as TableField<*, Int>).gt(Integer.parseInt(value))
}
}
This piece is used in a Kotlin class that will deserialize some JSON from the front-end. From this JSON, the class will build a jOOQ-query based on the input from the user. This part is about using the correct operator with the corresponding column and input value.
However this will not result in compile errors, IntelliJ is complaining about the casting of the field. I know for sure that these fields can be casted safely for the specific operator enum. This is the warning IntelliJ throws:
I don't want to Change type arguments to <*, *>, because without the casting, the jOOQ-condition won't work with the values.
Is there a way to rewrite my function properly, or can I safely ignore these warnings?

Casting
[...] or can I safely ignore these warnings?
I'm not sure what kinds of safety you're expecting here. Your assumption that EQ and NEQ are String based, whereas GT is Int based is quite a strong one. You probably have your reassons for this. If you encode things this way, and know why that is, then yes, you can safely ignore the warnings. This doesn't translate to such casts being "safe" in general, they're not. The * translates to any unknown type, yet in your cast, you make an assumption that effectively, the type should have been known, it's just not possible to express it in your type system.
Coercion
You can always use data type coercion as well, e.g.
return when (this) {
EQ -> field.coerce(SQLDataType.VARCHAR).eq(value)
NEQ -> field.coerce(SQLDataType.VARCHAR).ne(value)
GT -> field.coerce(SQLDataType.INTEGER).gt(Integer.parseInt(value))
}
Although, that does have an effect on the expression tree you're building, unlike the unsafe cast. Probably not an important effect in your case.

Related

How to stop Kotlin from widening types?

So I'm trying to define a method like this
fun <R,F> myFunction(prop: KProperty1<R, F>, value:F) {}
// so that the compiler only allows me to invoke it like
myFunction(User::name, "Alejandro")
// and stops developers from doing illegal things like
myFunction(User::name, 123)
//However, compiler doesn't complain if I do that... it widens the type to Any
How can I achieve that?
Kotlin is "widening" the type here because the value type parameter (i.e. the second type parameter) of KProperty1 is defined with keyword out which makes that parameter covariant.
This means that for instance KProperty1<User, String> is a subtype of KProperty1<User, Any>, and hence User::name which is presumably a KProperty1<User, String>, can also be seen as a special case of KProperty<User, Any>. Therefore, it is totally legal to call myFunction<User,Any>(User::name, 123).
The logic behind this can be derived from the name of the out keyword: It is expected that the typed parameter is only used in "out position" of any function call. In the case of KProperty1 this makes sense, because it is the type of the return value of the property. When you get a value from a KProperty1<K, V>, that value is of type V and thus it can be used anywhere where it is okay to have some supertype of V.
This should only be a problem, if you want to use the value in the "in position" of some function, for instance, if you want to write a function that takes a value of type V and store it in a KProperty1<K, V>.
If this is what you want, you are lucky, because you can and should just use KMutableProperty1<K,V> where the value parameter does not have an out keyword which means that it is invariant. Also, that interface allows you to put the value into the property.
Changing your function definition to
fun <R,F> myFunction(prop: KMutableProperty1<R, F>, value:F) {}
makes that the compiler allows myFunction(User::name, "Alejandro"), but it complains on myFunction(User::name, 123).
See also: Kotlin documentation on Variance

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 do I read / interpret this Kotlin code effectively?

I know how to read/interpret Java code and I can write it. However being new to kotlin I find code like below hard to read. Perhaps I am missing key concepts in the language.
But, how would you go about interpreting this code? Where do you propose one to start reading it in order to understand this piece of code quickly and efficiently? Left to right? Right to left? Break down parameters first? Look at return values?
inline fun <T : Any, R> ifNotNull(input: T?, callback: (T) -> R): R? {
return input?.let(callback)
}
So, like Java this is a generic function. It has two type parameters T which is of type 'Any' ('Any' is like 'Object' in Java) and R. The input parameter is a nullable T, as denoted by the question mark. Nullable types mean that the value can be null. The other function parameter is a function that takes in a T (non nullable type) and returns R. The return type of the function is a nullable R. The body of the function says that if input is not null, call and pass that to the callback and return that value. If input is null, then null is what gets returned.
Let's dissect the function definition piece by piece:
inline: Indicates that the code of the function will be copied directly to the call site, rather than being called like a normal function.
fun: We're defining a function.
<T : Any, R>: The function takes two generic type parameters, T and R. The T type is restricted to the Any type (which is Kotlin's Object-type). That might seem redundant, but what it actually says is that T cannot be a nullable type (Any?).
ifNotNull: The name of the function.
input: T?: The first parameter of type T?. We can put the ? on the T type here because we restricted it to non-nullable types in the type declaration.
callback: (T) -> R: The second parameter is of type (T) -> R, which is a function type. It's the type of a function that takes a T as input and returns an R.
: R?: The function returns a value of type R or null.
return input?.let(callback): The function body. The let function takes a function parameter, calls it with its receiver (input), and then returns the result of the function. The ? after input says that let will be called only if input is not null. If input is null, then the expression will return null.
The function is equivalent to this Java method (except for the inlining and nullable types):
public <T, R> R ifNotNull(final T input, final Function<T, R> callback) {
if (input == null) {
return null;
}
return callback.apply(input);
}
Matt's answer explains everything well in one go; I'll try to look at how you might go about reading such code.
Skipping over the first word for now, the most important thing is the second word: fun.  So the whole thing is defining a function.  That tells you what to expect from the rest.
The braces tell you that it's a block function, not a one-liner, so the basic structure you're expecting is: fun name(params): returnType { code }.  The rest is filling in the blanks!  (This fits the general pattern of Kotlin declarations, where the type comes second, after a colon.  The Java equivalent would of course be more like returnType name(params) { code }.)
As with Java, the stuff in angle brackets is giving generic parameters, so we can skip that for now and go straight to the next most important bit, which is the name of the function being defined: ifNotNull.
Armed with those, we can read the rest.  inline is a simple modifier, telling you that the function will be inlined by the compiler.  (That enables a few things and restricts a few others, but I wouldn't worry about that now.)
The <T : Any, R> gives the generic parameter types that the function uses.  The first is T, which must be Any or a subtype; the second is R, which is unrestricted.
(Any is like Java's Object, but can't be null; the topmost type is the related Any?, which also allows null.  So except for the nullability, that's equivalent to the Java <T extends Object, R>.)
Going on, we have the function parameters in parentheses.  Again, there are two: the first is called input, and it's of type T?, which means it accepts any value of type T, and also accepts null.  The second parameter is called callback, and has a more complicated type, (T) -> R: it's a function which takes a T as its parameter, and returns an R.  (Java doesn't have function types as such, so that probably looks strangest.  Java's nearest equivalent is Function<R, T>.)
After the parentheses comes the return type of this function itself, R?, which means it can return either an R or null.
Finally, in braces is the actual code of the function.  That has one line, which returns the value of an expression.  (Its effect is to check whether the value of input is null: if so, it returns the null directly.  Otherwise, it calls the callback function given in the parameter, passing input as its parameter, and returns its result.)
Although that's a short declaration, it's quite abstract and packs a lot in, so it's no wonder you're finding it hard going!  (The format is similar to a Java method declaration — but Kotlin's quite expressive, so equivalent code tends to be quite a bit shorter than Java.  And the generics make it more complex.)  If you're just starting to learn Kotlin, I'd suggest something a bit easier :-)
(The good news is that, as in Java, you don't often need to read the stdlib code.  Although Kotlin's doc comments are rarely up to the exemplary level of Java's, they're still usually enough.)

Cast Any to Array in Kotlin

I'm initializing a class by loading data from a Map<String, Any> in Kotlin. As this Map is gleaned directly from JSON, I don't know for certain that any given key exists, or that its value is of the type I expect. To unpack this Map safely I'm doing the following, which appears to work perfectly:
a = rawData["A"] as? String ?: ""
Some of this data is in further nested JSON, which I'm unpacking to Arrays; I've tried to do this in the same way:
b = rawData["B"] as? Array<String> ?: arrayOf<String>()
However, when I attempt this using an array (as above) IntelliJ kicks up a fuss, saying
Warning:(111, 30) Kotlin: Unchecked cast: Any? to Array<String>
Is this just the IDE getting itself in a twist or is this method genuinely unsafe for Arrays despite being seemingly perfectly safe for other types?
For any future readers of this question, to expand on the accepted answer with a solution:
To safely cast Any to an array of a particular type in Kotlin, you have to first cast to an untyped array (see zsmb13's answer above for why), and then filter that array to the desired type.
For example, to cast input: Any to an array of String instances, you would call:
val inputAsArray = (input as? Array<*>)?.filterIsInstance<String>()
I was ready to call this a bug, because Array is a reified type, meaning its generic parameter can actually be checked at runtime (unlike a List, for example). I've tried looking to see if it's been filed yet, and it turns out the compiler is actually right to show you a warning. As you can see in the response to this issue, there's still a nullability problem with casts of this kind.
val a = arrayOf("foo", "bar", null) as Array<String>
println(a[2].length)
Arrays like the one in this example are successfully cast (using as, they don't throw an exception, using as?, they don't return null), however, the cast can not ensure that this is an Array<String>, only that it's an Array<String?>.
This means that you can later read null values from a variable that is typed as an Array<String> after the cast, with no further warnings from the compiler.

Fix generic type to the type of the first parameter

I want to write an extension function which will be available on any type and accept parameter of the same type or subtype, but not a completely different type.
I tried naive approach but it didn't work:
fun <T> T.f(x: T) {
}
fun main(args: Array<String>) {
"1".f("1") // ok
"1".f(1) // should be error
}
It seems that compiler just uses Any for T. I want T to be fixed to receiver type.
The only way to do it requires telling the compiler what you want.
fun <T> T.f(x: T) {
}
In order to use it, you have to tell Kotlin what you want the type to be.
"1".f<String>("2") // Okay
"1".f(2) // Okay (see voddan's answer for a good explanation)
"1".f<String>(2) // Fails because 2 isn't a String
"1".f<Int>(2) // Fails because "1" isn't an Int
When you call fun <T> T.f(x: T) {} like "1".f(1), the compiler looks for a common super-type of String and Int, which is Any. Then it decides that T is Any, and issues no error. The only way to influence this process is to specify T explicitly: "1".f<String>(1)
Since all the checks are performed by the compiler, the issue has nothing to do with type erasure.
Your issue is like saying "John is 3 years older than Carl, and Carl is 3 years younger than John" ... you still don't know either of their ages without more information. That's the type of evidence you gave the compiler and then you expected it to guess correctly. The only truth you can get from that information is that John is at least 3 years old and Carl is at least 1 day old.
And this type of assumption is just like the compiler finding the common upper bounds of Any. It had two strong literal types to chose from and no ability to vary either. How would it decide if the Int or String is more important, and at the same time you told it that any T with upper bounds of Any? was valid given your type specification. So the safe answer is to see if both literals could meet the criteria of T: Any? and of course they do, they both have ancestors of Any. The compiler met all of your criteria, even if you didn't want it to.
If you had tie-breaking criteria, this would work out differently. For example, if you had a return type of T and a variable of type String receiving the value, then that would influence the decision of Type inference. This for example produces an error:
fun <T: Any> T.f2(x: T): T = x
val something: String = "1".f2(1) // ERROR
Because now the type T is anchored by the "left side" of the expression expecting String without any doubt.
There is also the possibility that this could also be an type inference issue that is not intended, check issues reported in YouTrack or add your own to get a definite answer from the compiler team. I added a feature request as KT-13138 for anchoring a specific type parameter to see how the team responds.
You can fix T to the receiver type by making f an extension property that returns an invokable object:
val <T> T.f: (T) -> Unit
get() = { x -> }
fun main(vararg args: String) {
"1".f("1") // will be OK once KT-10364 is resolved
"1".f(1) // error: The integer literal does not conform to the expected type String
}
Unfortunately "1".f("1") currently causes an error: "Type mismatch: inferred type is String but T was expected". This is a compiler issue. See KT-10364. See also KT-13139. You can vote on and/or watch the issues for updates. Until this is fixed you can still do the following:
"1".f.invoke("1")
/* or */
("1".f)("1")
/* or */
val f = "1".f
f("1")