Why should I assign the data type of variables in Kotlin if giving it values initially it is capable of inferring the data type of the variable - variables

As mentioned in the code provided. My question is if there is any advantage by mentioning the data type early on when I am just giving the variable it's value. As said in line 2 it can understand that it is int type.
val a: Int = 1 // immediate assignment
val b = 2 // `Int` type is inferred
val c: Int // Type required when no initializer is provided.

A few reasons you might want to specify a type explicitly:
You want a supertype of the value (perhaps so you can to reassign it later), e.g.:
var myList: List<String> = ArrayList<String>()
You want to protect against changes to other code (especially if it's outside your control), e.g.:
val x: MustBeThisType = SomeLibrary.getValue()
(That would give an error if SomeLibrary.getValue() ever changes to returns something other than MustBeThisType or a subtype.)
You want to avoid an explicit numeric conversion, e.g.:
val x: Long = 2
instead of:
val x = 2.toLong()
You want to make it very clear to someone reading your code (especially if that might not be in an IDE).
As Michael says, you may need to specify the nullability of types returned from Java, e.g.:
val x: String = someJavaClass.getAString() // Never returns null
None of those is particularly common in my experience, though.

Related

Kotlin: how to declare a generic function with fixed set of admissible type parameters?

I want to write a function that converts a pair of integer numbers to pair of BigInteger. Allowed parameter types are Int, Long, and BigInteger:
fun <A, B> foo(a: A, b: B) {
val x = if (a is BigInteger) a else a.toBigInteger()
val y = if (b is BigInteger) b else b.toBigInteger()
return Pair(x, y)
}
Is it possible to inform the Kotlin compiler that A and B must be in the set of Int, Long,BigInteger?
Unfortunately I don't think this is currently possible, or would have enough use for them to consider implementing it. I couldn't find any specific mention of the lack of support, but the documentation for (multiple) upper bounds says that:
The passed type must satisfy all conditions of the where clause simultaneously. In the above example, the T type must implement both CharSequence and Comparable.
The only clean (ish) solution would probably be overloading, however this would introduce a lot of duplication if you're allowing any combinations of Int, Long etc.

Understanding Kotlin Type system, What is meant by `{Comparable<{ Double & Int }> & Number}` and how to work with that

So for example:
var a = true
val test = if (a) -1 else -3.2
I was expecting the Type of the test should be the most closest intersection of the type-hierarchy i.e. for Int and Double is Number.
But looking at the IDE, it seems to have a type of {Comparable<{ Double & Int }> & Number}.
And the weird thing is, I cannot specify it like that (since {} is reserved for creating lambdas), I can only set it to a type of Number.
And another wierd thing is that if I try some function from Comparable interface, it throws some error:
// Warning at value 2
// The integer literal does not conform to the expected type {Double & Int}
test.compareTo(2)
3.compareTo(-1.1) // possible
2.3.compareTo(100) // possible
// But why not this is possible, while it has inferred type of Comparable?
test.compareTo(2)
Could somebody help in understanding the concept here? And few questions:
How does that type work all together, i.e. how could one variable hold two types at once?
How could one specify that type explicitly?
How do you use functions from Comparable interface, when test has implementaion of it?
& here means an intersection type (which isn't supported in the Kotlin language itself, but the compiler uses them internally). You can see it mentioned in the (incomplete) specification.
Intersection types are special non-denotable types used to express the fact that a value belongs to all of several types at the same time.
"Non-denotable" means exactly that you can't specify that type. I am not sure but I think the extra { } in types are supposed to indicate exactly this.
In particular, Comparable<Double & Int> means you can only compare test to something which is both Double and Int, but there are no such values. The compiler could probably simplify it to Comparable<Nothing>.
the most closest intersection of the type-hierarchy i.e. for Int and Double is Number.
It's least upper bound, which is closer to union, not intersection. The specification actually calls it "union types", but that's not the normal usage of that term.
This least upper bound is not Number because it also takes least upper bound of the Comparable interfaces which works out to Comparable<Double & Int> because Comparable is contravariant:
lub(Int, Double) =
Number & lub(Comparable<Int>, Comparable<Double>) =
Number & Comparable<Int & Double>
This calculation is described under type decaying:
All union types are subject to type decaying, when they are converted to a specific intersection type, representable within Kotlin type system.
The answer to question 1 is that the compiler is doing its best to infer the type, inventing new constraints to describe it as it goes.
The answer to question 2 is that you cannot.
The answer to question 3 is that you cannot, because Int is not comparable to Double and vice versa. So none of the methods from Comparable are actually usable, but the value definitely implements Comparable against something. This is not useful for Comparable, but could be for another interface. For example, imagine:
interface ZeroAndComparable<T> {
fun compareTo(t: T): Int
fun zero(): T
}
val foo : ZeroAndComparable<Int> = someZeroAndComparableInt()
val bar : ZeroAndComparable<Double> = someZeroAndComparableDouble()
val foobar = if (a) foo else bar
val zero : Any = foobar.zero() // should work
foobar.compareTo(something) // cannot work

Can the Kotlin Compiler Require non-null assertion?

Can the Kotlin Compiler require non-null assertions?
for example. I'm getting a query result from JOOQ, and the relting type is Record!. But the compiler allows me to access members without checking for null.
val orderResult = RowanDatabaseConnector.runInContext(DatabaseContext.BOOKSTORE) {
it.select(SPREE_ORDERS.asterisk())
.from(SPREE_ORDERS)
.where(SPREE_ORDERS.ID.eq(orderId.toInt()))
.fetchOne()
}
return Cart(
user = currentUser,
subTotal = orderResult.get(SPREE_ORDERS.ITEM_TOTAL).toFloat(),
taxTotal = orderResult.get(SPREE_ORDERS.ADDITIONAL_TAX_TOTAL).toFloat(),
total = orderResult.get(SPREE_ORDERS.TOTAL).toFloat(),
lineItems = listOf()
)
Is there something similar to Typescript's strictNullCheck that would require that I assert that orderResult is not null?
For platform types (any type ending with !), you can use an explicit type specification to declare whether it is supposed to be nullable or not.
If you want to make it explict that orderResult might be null, then you could write it like this:
val orderResult: Record? = /* trimmed */
Conversely, if you want to make it explicit that orderResult can't possibly be null, then you could write:
val orderResult: Record = /* trimmed *
By choosing one of these two options, the type will be Record? or Record instead of Record!. And, as such, you will get the null safety you're used to from Kotlin.

Cast reflection "Type" to a List<class> in Kotlin

In my Kotlin code, I have a variable that is the Type interface from
java.lang.reflect
var type: Type
But I need to cast this to:
List<UserInfo>
If I was not casting to a List, I would just do this:
var type = UserInfo::class.java
and this works. But I don't know how to cast it using a List. The closest I found is this:
var type = Array<UserInfo>::class.java
This would compile if my UserInfo was an Array but it's a List.
The issue (as #Miha_x64 says) is type erasure.
The Java Virtual Machine knows nothing about type parameters.  So although the source specified a List<UserInfo>, it compiles down to a plain List.
So this works:
var type = List::class.java
(Your Array example works because arrays are a special case: they're directly supported in the JVM, and keep their types at runtime.)
Java's use of type erasure is at least partly for historical reasons; when generics were added to Java 5, they wanted to preserve compatibility with existing source and bytecode.  For all the gory details (much of which is inherited by Kotlin), see here.
Maybe its late. Try to use KTypeProjection and createType() with KClass instead. Then get the value in KType or convert it back to java Type.
val kClass = YourClass::class
val kTypeProjection = KTypeProjection.invariant(entity.starProjectedType)
val kType = List::class.createType(listOf(kTypeProjection))
val type = kType.javaType
result:
kType: kotlin.collections.List<YourClass>
type: java.util.List<YourClass>

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")