Declaration-site variance may cause ClassCastException - kotlin

Kotlin introduces Declaration-site variance described at here.
The out/in keywords for generic parameters may cause ClassCastException in some case. My program is shown below.
fun main(args: Array<String>) {
var l: List<String> = mutableListOf("string")
demo(l)
println("======")
for (s in l) {
println(s)
}
}
fun demo(strs: List<String>) {
val objects: List<Any> = strs // This is OK, since T is an out-parameter
if (objects is MutableList) {
val obs: MutableList<Any> = objects as MutableList<Any>
obs.add(TextView())
}
}
Output:
Exception in thread "main" java.lang.ClassCastException: com.kotlin.demo.clzz.TextView cannot be cast to java.lang.String
at com.kotlin.demo.clzz.Declaration_Site_VarianceKt.main(Declaration-Site-Variance.kt:14)
======
adn
Is the way to use out/in keywords a recommended practice? and Why?

Your code can be compiled without any warnings, this is because declaration-site variance only available in Kotlin.
This is in contrast with Java's use-site variance where wildcards in the type usages make the types covariant.
For example 2 Soruce interfaces use declaration-site variance in Kotlin:
interface Source<out T>
interface Source<in T>
Both of the two Source interfaces will be generated into the same source code in Java as below:
// v---`T extends Object` rather than `? extends T`
public interface Source<T>{ /**/ }
This is because wildcard ? is used as a type argument rather than a type parameter in Java.
The T in Source<T> is a type parameter and the ? extends String in Source<? extends String> is a type argument.
So if you use type projections to make the objects force to a List<out Any>, then the compiler will reports an UNCHECKED_CAST warning , for example:
fun demo(strs: List<String>) {
// v--- makes it explicitly by using out type proejction
val objects: List<out Any> = strs
if (objects is MutableList) {
// v--- an UNCHECKED_CAST warning reported
val obs: MutableList<Any> = objects as MutableList<Any>
obs.add(TextView())
}
}
In other words, you can't assign a List<out Any> to a MutableList<Any>. Otherwise, you will get a compilation error. for example:
fun demo(strs: List<String>) {
val objects: List<out Any> = strs
if (objects is MutableList) {
// v--- ? extends Object
//ERROR: can't assign MutableList<out Any> to Mutable<Any>
// v ^--- Object
val obs: MutableList<Any> = objects
obs.add(TextView())
}
}
IF you assign the objects to a MutableList<out Any> variable, you'll found that you can't adding anything, since you can't create Nothing in Kotlin at all. for example:
fun demo(strs: List<String>) {
val objects: List<out Any> = strs
if (objects is MutableList) {
// v--- down-casting to `MutableList<out Any>`
val obs: MutableList<out Any> = objects
// v---ERROR: can't be instantiated
obs.add(Nothing())
}
}
Q: Is the way to use out/in keywords a recommended practice?
Java has described how to use a wildcard and it also applies in Kotlin.
An "in" Variable, note "in" in here is ? extends T and it is same with Kotlin out variance:
An "in" variable serves up data to the code. Imagine a copy method with two arguments: copy(src, dest). The src argument provides the data to be copied, so it is the "in" parameter.
An "out" Variable, note "out" in here is ? super T and it is same with Kotlin in variance:
An "out" variable holds data for use elsewhere. In the copy example, copy(src, dest), the dest argument accepts data, so it is the "out" parameter.

Related

Default value for generic member

I'm trying this:
class Foo<T> {
var member: T = T()
}
...but the Kotlin compiler gives me an error: Type parameter T cannot be called as function.
How do I default-construct a generic member variable?
Well, to access the type information, we need to use the reified keyword on the type, but this is only applicable in inlined functions. So instead of relying on direct construction, a workaround can be to use a generator function wrapped in the companion object that immediately sets the member right after construction
// Test class to verify the implementation
class Yolo {
override fun toString() = "Yolo swag"
}
class Foo<T : Any> {
lateinit var member: T
companion object {
inline fun <reified T : Any> newInstance() =
T::class.java.newInstance().let { memberInstance ->
Foo<T>().apply { member = memberInstance}
}
}
}
fun main() {
// generate a Foo<Yolo>
val foo = Foo.newInstance<Yolo>()
println(foo.member) // displays "Yolo swag"
}
It's implied that T has a public no-arg constructor, but in general case it may not be true. This code uses reflection to bypass compiler complains about it (which may end up with runtime error if you dissapoint the JVM expectations and indeed pass T without public no-arg constructor).
//Reified generics at class level are not yet supported in Kotlin (KT-33213),
// so you have to pass instance of `KClass` manually as a consructor parameter
class Foo<T : Any>(clazz: KClass<T>) {
var member: T = clazz.createInstance()
}

Kotlin `object` initialization order leads to unexpected null instance

Consider the following code:
sealed class DataType<T : Any> {
abstract fun inputToType(input: String): T
abstract fun typeToSql(value: T): String
companion object {
val all = listOf(StringDt, LongDt)
}
}
object StringDt : DataType<String>() {
override fun inputToType(input: String) = input
override fun typeToSql(value: String) = "\"${value}\""
}
object LongDt : DataType<Long>() {
override fun inputToType(input: String) = input.toLong()
override fun typeToSql(value: Long) = value.toString()
}
val dataTypeList = listOfNotNull(StringDt, LongDt)
println(dataTypeList)
println(DataType.all)
Things to consider:
object as per documentation (and my understanding as well) is singleton and always instantiated
the two objects (StringDt and LongDt) are quite similar
The result of println(DataType.all) shows that one of the objects are not initialized. How is that possible? I would expect all the list elements to be initialized.
IntelliJ version: CE 2020.2
Kotlin plugin version: 1.4.0-release-IJ2020.2-1
Here's a running example which shows that the static list has a null element, while the non-static one contains both objects initialized.
It happens due to cyclical static initializations. It's pretty hard to explain this problem in two words but you can read about it here.
To fix this behavior you can change all initialization like this:
val all by lazy { listOf(StringDt, LongDt) }

Can we implement Rust like Traits using Kotlin Interfaces

Can we implement Rust like Traits and generic Traits using Kotlin Interfaces?
Also is there any way of using fn(&self) like construct in Kotlin class/interface default function implementations?
Can some examples be shown please?
Thanks
I don't know much about Rust, I'm referrring to these two videos as for what you're talking about, generic traits and &self explaination.
In kotlin you'd implement them using interfaces and classes as you've guessed.
An example of that is:
interface GenericTrait { // Same as traits
// <T:Any> just makes method to be called for non-null values, if you use <T>, you can pass null as well
fun <T: Any> method(value: T)
}
class TraitImpl : GenericTrait { // Same as structs
val isDisabled = Random.nextBoolean() // instance variable
// you can access instance parameter using the this or even not using it at all as in below
override fun <T: Any> method(value: T) {
println("Type of value is ${value::class}, and the value is $value. I am $isDisabled")
// or explicitly call ${this.isDisabled}, both are the same
}
}
fun main() {
TraitImpl().method("Hello")
TraitImpl().method(23)
TraitImpl().apply { // this: TraitImpl
method(23)
method(Unit)
}
}
Result:
Type of value is class kotlin.String, and the value is Hello. I am true
Type of value is class kotlin.Int, and the value is 23. I am true
Type of value is class kotlin.Int, and the value is 23. I am false
Type of value is class kotlin.Unit, and the value is kotlin.Unit. I am false
You can extract implementation outside if you want as an extension function just like you do in Rust.
interface GenericTrait {
val isDisabled: Boolean
}
class TraitImpl : GenericTrait {
override val isDisabled = Random.nextBoolean()
}
// define methods out of class declaration
fun <T: Any> GenericTrait.method(value: T) {
println("Type of value is ${value::class}, and the value is $value. I am $isDisabled")
}

Why the type of expression objectOfTypeT::class is KClass<out T>?

Suppose we have generic function:
fun <T: Any> foo(o: T) {
o::class
}
The o::class's type is KClass<out T>. Why there is the out variance annotation, and why is it not KClass<out Any> (because T's erasure is Any)
This out variance annotation screwed my nice reflection code
EDIT:
After digging a while, I found kotlin rely on Object::getClass to get a Class to create a KClass, the actual creation code has a signature like fun <T: Any> create(jClass: Class<T>): KClass<T>. However this leads to another problem. The o::class should be of type KClass<Any> because jClass parameter of that create method should be of type Class<Object>, since the erasure of static type T is just Any (or Object, to which is mapped on JVM).
Why there is the out variance annotation?
This is expected behavior of the Bounded Class Reference in kotlin 1.1.
We know an instance of subclass can be assign to a supperclass, for example:
val long:Number = 1L;
val int:Number = 1;
We also know generic inheritance is not like class inheritance, for example:
val long:KClass<Long> = Long::class;
val number:KClass<Number> = long;
// ^
// can't be compiled, required KClass<Number> but found KClass<Number>
So we makes the code to be compiled by using Generic Type Projection as below:
val number:KClass<out Number> = long;
In Short, an variable of supperclass (Number) can be assign to an instances of any its subclasses (Long, Int, Double and .etc), but when get the KClass reference from the Number reference it should be return a KClass<out Number> rather than KClass<Number>, because KClass<Int> is not a subtype of the KClass<Number>.
The same rule applies in java, for example:
Number number = 1L;
Class<? extends Number> type = number.getClass();
Why is it not KClass (because T's erasure is Any)?
Because your method uses generic parameter T, but java.lang.Object#getClass don't uses any generic parameter at all and its return type is Class<? extends Object>.
However, the T#javaClass property takes a generic parameter T and you can see the code below that kotin cast a Class<?> to a Class<T>. so the Upper Bounded Wildcard of the o::class in the foo method is KClass<? extends T> rather than KClass<? extends Object> in java.
public inline val <T: Any> T.javaClass : Class<T>
#Suppress("UsePropertyAccessSyntax")
get() = (this as java.lang.Object).getClass() as Class<T>
// force to casting a Class<?> to a Class<T> ---^
A KClass<? extends T> is a subtype of KClass<?>, according to LISP principle you can't assign a superclass instance to a subclass type.
fun <T : Any> foo(value: T) {
val type: Class<out T> = (value as java.lang.Object).getClass();
// ^
// Compilation Error:
// you can't assign Class<? extends Object> to a Class<? extends T>
}
you can also see the method generic signature as below:
val noneGenericParametersMethod= Object::class.java.getDeclaredMethod("getClass")!!
val genericParametersMethod by lazy {
val it = object {
fun <T : Any> foo(): Class<out T> = TODO();
};
return#lazy it.javaClass.getDeclaredMethod("foo")!!;
}
println(genericParametersMethod.toGenericString())
// ^--- its return type is java.lang.Class<? extends T>
println(noneGenericParametersMethod.toGenericString())
// ^--- its return type is java.lang.Class<? extends Object>
Base on the above, the expression o::class actually returns a Raw Type KClass rather than a parameterized type KClass<out T>, and a Raw Type can be assign to any Parameterized Type, However, kotlin has no Raw Type, so kotlin compiler narrow the raw type KClass into the parameterized type KClass<out T>, just like as narrow an List<*> to an Iterable<*>. an example of Raw Type in java:
Box rawBox = new Box(); // rawBox is a raw type of Box<T>
Box<Integer> intBox = rawBox; // warning: unchecked conversion
Why java can't assign T.getClass() into a Class<? extends T>?
If you get deep into the documentation of the java.lang.Object#getClass method, you will found the result as below:
The actual result type is Class<? extends |X|> where |X| is the erasure of the static type of the expression on which getClass is called.
"The erasure of the static type": which means |X| is the bounded type rather than the actual generic argument type in runtime, for example:
// Object.getClass() using the bounded static type `Number`
// v
<T extends Number> Class<? extends Number> foo(T value){
return value.getClass();
}

How to create a generic array filled with nulls in Kotlin?

I tried this, and the code didn't compile.
class GenericClass<T>() {
private var arr : Array<T>? = null
{
arr = Array<T>(10, { null })
}
}
There are two compiler errors reported in this code: one is about nullable types and another about generics.
Nullable types. Kotlin enforces a discipline of nullable references, and since T may be instantiated with, say, String making arr be of type Array, the compiler does not allow you to put nulls into this array. If you want nulls, you have to change the type to Array:
class GenericClass<T>() {
private var arr : Array<T?>? = null
{
arr = Array(10, { null }) // No need to specify type arguments again
}
}
Generics. The example above still has a compile-time error, because we are trying to construct an array of an unknown type T. Note that this problem exists in Java as well. Kotlin being compiled to JVM byte code entails two things:
generics type arguments are erased at runtime,
except for generic arguments of arrays.
This means that in the byte code Kotlin has to create an array of some concrete type, and not an unknown type T. It could create arrays of Objects whenever it sees Array, but this would not work, for example, in this case:
fun test() {
fun foo(srts: Array<String?>) {
// ...
}
val gc = GenericClass<String>()
foo(gc.arr)
}
Here, in the last line, we are trying to pass Object[] where String[] is expected, and get a runtime error.
This is why Kotlin refuses to create arrays of T. You can work around this problem by explicitly suppressing the type system, i.e. by using type casts:
class GenericClass<T>() {
val arr : Array<T?>
{
arr = Array<Any?>(10, { null }) as Array<T?>
}
}
Here we explicitly request creation of an array of Any (compiled to Object[]), and then type-cast it to an array of T. The compiler issues a warning, but obeys our will.
Note that the problematic example above remains, i.e. if you pass the array created this way where an array of strings is expected, it ill fail at run time.
method
val array : Array<T?> = kotlin.arrayOfNulls<T>(size)
from docs
/**
*Returns an array of objects of the given type with the given [size],
*initialized with null values.
*/
public fun <reified #PureReifiable T> arrayOfNulls(size: Int): Array<T?>
If you need to initialize array in the constructor, you can add an inline factory method and parametrize it using reified T. This solution is inspired by answer https://stackoverflow.com/a/41946516/13044086
class GenericClass<T> protected constructor(
private val arr : Array<T?>
) {
companion object {
inline fun <reified T>create(size: Int) = GenericClass<T>(arrayOfNulls(size))
}
}
fun main() {
val strs = GenericClass.create<String>(10)
...
}
Notice that the constructor is protected, because inline function can't access a private constructor.
If you need to create an array after the object is created, you can pass lambda that creates the array into the method. Lambda can be created inside of extension function, so information about type of the array is preserved. #PublishedApi annotation is used to encapsulate private method fill.
import GenericClass.Companion.fill
class GenericClass<T> {
private var arr : Array<T?>? = null
fun show() {
print(arr?.contentToString())
}
private fun fill(arrayFactory: (size: Int) -> Array<T?>) {
this.arr = arrayFactory(10)
}
#PublishedApi
internal fun `access$fill`(arrayFactory: (size: Int) -> Array<T?>) = fill(arrayFactory)
companion object {
inline fun <reified T>GenericClass<T>.fill() {
`access$fill`(arrayFactory = { size -> arrayOfNulls(size) })
}
}
}
fun main() {
val strs = GenericClass<String>()
strs.fill()
strs.show()
}
You could use a helper function as below:
#Suppress("UNCHECKED_CAST")
fun <T> genericArrayOfNulls(size: Int): Array<T?> {
return arrayOfNulls<Any?>(size) as Array<T?>
}