I've noticed something that appears to me as an inconsistency in the generic TArray syntax (and drives me crazy...)
The "constructor" function requires to be called by specifying the type before the function name.
MyArray := TArray<Integer>.Create(3, 2, 1);
The other class functions requires to be called by specifying the type after the function name
TArray.Sort<Integer>(MyArray);
Is there a pratical reason why they did that?
The generic array is defined like this
TArray<T> = array of T;
This an alias for a dynamic array. Dynamic arrays have an intrinsic Create method. Even non-generic ones.
type
TMyDynArr = array of Integer;
....
arr := TMyDynArr.Create(0, 1, 2);
This Create method is a compiler intrinsic. Notice how it accepts arbitrary numbers of arguments.
On the other hand there is the class TArray, with its generic Sort method. This is implemented in the RTL. Remember that TArray is never instantiated, it's just a home for generic class methods.
My guess is that adding methods to the dynamic array type requires compiler support because they are intrinsic functions. But adding to TArray is simpler because it is done at the RTL layer, not requiring compiler support.
There's nothing you can do to change anything here, so there is little to be gained by fretting about this. You can't change it. Just get used to it.
The first TArray is a system type definition of array of T. The creation could be written this way as well:
MyArray := [3,2,1];
The second TArray is a class defined in Generics.Collections.
They have nothing to do with each other.
Note also that the TArray class way of using generics is called Parameterized Methods.
Type
TArray = class
...
class procedure Sort<T>(var Values: array of T); overload; static;
...
end;
That is a way to reduce code duplication.
Related
How can I check if a Generic is an Array using an inline function?
I tried with the following code:
class Mediator {
inline fun <reified C> mediate(string: String): Container<C> {
if (C::class == Int::class) {
//It works
}
else if (C::class == Array::class) {
//It doesn't work!
}
throw IllegalStateException("Yopta")
}
}
But it doesn't work. Maybe because it can be Array<Whatever>?
How can I do it?
Contrary to collections where for example List<String> and List<Int> are internally represented by the same class List, in arrays the type parameter is a part of the type itself. That means Array<String> and Array<Int> are internally represented as different types and as far as I know, they don't have a common super type.
I don't know a pure Kotlin solution to check if a class is an array. It seems to me like an overlook in the design of the reflection API. If you don't mind using the Java reflection, you can do it like this:
else if (C::class.java.isArray) {
Update
There is one interesting fact here. In the Kotlin type system we could consider Array<out Any?> to be a supertype of all arrays. For example, we can upcast to it without an explicit cast operator:
val intArray = arrayOf(1, 2, 3)
val arr: Array<out Any?> = intArray
However, for the reflection API these two types are entirely different:
// false
println(Array<Int>::class.isSubclassOf(Array<out Any?>::class))
I assume this is due to how arrays where implemented in Java. I'm not even sure if it would be technically possible to return true in the code above. Still, it is concerning it provides a different result than the type system at a compile time and it doesn't even produce a warning.
Actual answer that solves the issue here.
Since broot added an actual answer I'll just leave this here as a note as to how we can see that he is right basically.
If we make the call like this:
Mediator().mediate<Array<Int>>("")
Adding a simple check inside the function like this makes it a bit confusing as to why they are not equal.
println(C::class) //class Kotlin.Array
println(Array:class) //class Kotlin.Array
But doing the same for the underlying java class shows that they are not really the same object.
println(C::class.java) //class [Ljava.lang.Integer
println(Array:class.java) //class [Ljava.lang.Object
So changing the statement to:
if(C::class.java == Array<Int>::class.java)
Will make the example work ... for Int only. All other "infinite" possibilities will have to be added manually. Not an issue if you just want to check Array<X> only, but definitely not generic.
Is there a way (I'm sure there is out of runtime check...) to specify that a parameter or a variable in general conforms to multiple types? to avoid doing something such as
work (a_printer: PRINTER; a_scanner: SCANNER)
do
a_printer.print
a_scanner.scan
-- OR without second parameter
if attached {SCANNER} a_printer as l_scanner then
l_scanner.scan
else
throw RuntimeError
end
end
If feature work belongs to a class that may have formal generic parameters, it could be defined as taking one argument of the corresponding formal generic type:
class X [D -> {PRINTER, SCANNER}] feature
work (device: D)
do
device.scan
device.print
end
end
Then, at the caller site, one could make the call
x.work (multi_function_device)
where x has an appropriate type, e.g. X [MULTI_FUNCTION_PRINTER].
If work could also be declared and implemented as a class feature, the temporary variable could be avoided:
{X [like multi_function_device]}.work (multi_function_device)
If the auxiliary class X is not an option, the current version of the language provides no means to declare an argument as conforming to more than 1 type (e.g., work (d: {PRINTER, SCANNER})), so you would have to resort to preconditions like
work (p: PRINTER)
require
attached {SCANNER} p
do
check
from_precondition: attached {SCANNER} p as s
then
s.scan
end
p.print
end
I think that, if possible, you should use a common ancestor to your multiple types. If you cannot (if you are using library types), you can create descendant classes (MY_PRINTER inherit from PRINTER and DEVICE and MY_SCANNER inherit from SCANNER and DEVICE). Another way is to use ANY as the type, but it is not the best solution.
I am trying to deserialize an array of JSON objects with GSON. So the simple call:
val arrayOfFoo = gson.fromJson(source, Array<Foo<*>>::class.java>)
should do the trick. But type erasure tells us, that Foo<*> does not exist at runtime, so the error "Only class literals are allowed on the left hand side of a class literal" pops up. Well, so the solution must be:
val arrayOfFoo = gson.fromJson<Array<Foo<*>>>(source, Array::class.java)
Unfortunatelly, now the Kotlin compiler magic - that turns arrays of Wrapper types into primitive arrays - can not be sure what to do and tells us:
"Array class literals require a class type. Please specify one in angle brackets".
But, wait: This is, what did not work a second ago. Using
Array<Foo>::class.java
does not work, too, since now the compiler tells us: "One type argument is expected for Foo".
I personally can not see a way to solve that. Is it impossible to give a class literal of a typed array, which's type also expects a type parameter?
You can get the array class from an array instance, for example either one of
arrayOf<Foo<*>>()::class.java
java.lang.reflect.Array.newInstance(Foo::class.java, 0)::class.java
The basic problem: You need to specify the type of your array. This is done using a TypeToken in Gson.
I hope this helps:
val listType = object : TypeToken<Array<String>>() {}.type
val json = """["1"]"""
val yourClassList :Array<String> = Gson().fromJson(json, listType)
print(yourClassList)
Note that for primitives, it is simpler: Gson().fromJson(json, IntArray::class.java)
I have read multiple times the answers in this question about the TArray<T> and the array of T. From what I have understood the use of the first is more versatile than the latter because for a dynamic array I should declare a type like...
type
TMyFlexibleArray = array of Integer;
... that is needed (in certain cases) because I cannot return an array of Integer for example. Instead, of course, I can return a generic type. Dynamic arrays don't have a fixed length and memory for them is reallocated with the SetLength procedure. TArray is a generic class with static methods; the documentation about it states:
You should not create instances of this class, because its only
purpose is to provide sort and search static methods.
They have two different natures/functions but do they have the same result (for example when passed as parameter or when I just need a flexible container)? I see that TArray has also some useful method.
Is is correct if I say that TArray<T> is a dynamic array built with generics and type K = array of T is an own dynamic array (a custom one)? In my question I assume that they are equivalent in their function of being dynamic arrays (and I prefer the generic way just for comfort).
Generic dynamic arrays and non generic dynamic arrays are identical in every way, apart from their generic, or otherwise, nature. That is the single difference.
That difference drives decision making in the few scenarios where one can be used but not the other. For instance:
For reasons outlined in your question, generic arrays are sometimes necessary when working with generic types.
On the other hand, when writing code that you wish to compile on old compilers that pre-date generics, then you cannot use generic arrays.
If this seems obvious, that's because it is. There really is just this one single difference between generic and non-generic arrays.
You also mention the class TArray from System.Generics.Collections. That's a static class containing methods to search and sort arrays. It's completely different from any dynamic array types and something of a distraction here. Although the names are similar, TArray<T> and TArray these are quite different things. Ignore TArray for the purpose of this question.
I'm trying to define a generic class that takes a parameterized type T and then use the type in an Array definition in the class. I wrote the following which I thought seemed like it should work
class MyClass[T] {
val myarr:Array[T] = new Array[T](10)
}
But the compiler complains with the following
can't find the class manifest for element type T
value newArray is not a member of Null
Anyone know whats going on here and what its not happy about?
The compiler needs to know how to instantiate things of type T. In the traditional Java way of handling generics through type erasure, this cannot reasonably be done; the compiler just says, "Hey, I don't know what T is, so I don't feel so great about allowing you to instantiate a T like that." In Scala, however, there is a word-around for this: manifests. In order to include the manifest for T, just change the first line of that code to
class MyClass[T : Manifest] {
That's it.