If I try to get
BaseResponse<Iterable<User>>::class
I get error error only classes are allowed on the left hand side of class literal. I have searched and still not found how to get generic classes's type in kotlin.
You can't -KClass can only describe the class BaseResponse, and you can get a KClass instance that does that with BaseResponse::class.
What you have however, BaseResponse<Iterable<User>>, is a concrete type, which you can be represented as a KType instance. KType instances can be created, for example, with the createType function. This would look something like this:
// User
val userType = User::class.createType()
// Iterable<User>
val iterableOfUserType = Iterable::class.createType(arguments = listOf(KTypeProjection.invariant(userType)))
// BaseResponse<Iterable<User>>
val responseType = BaseResponse::class.createType(arguments = listOf(KTypeProjection.invariant(iterableOfUserType)))
I made the choice of making the type parameters both invariant, there's also factory methods in KTypeProjection to create covariant or contravariant types, as you need them.
Related
I'm using kotlin sealed class. And I need to retrieve specific subclass. My sealed class:
sealed class Course(
val type: Type
) {
data class ProgrammingCourse(val name: String, val detail: String) : Course(Type.PROGRAMMING)
object LanguageCourse: Course(Type.LANGUAGE)
.....
}
For example I have function which can return Course:
fun getCourse(): Course {
if(...)
return Course.ProgrammingCourse("test", "test")
else
return Course.LanguageCourse
}
In addition, I have a method that can only work with a specific subclass of the Course class. Fox example:
fun workWithCourse(course: Course.ProgrammingCourse) {
// here some logic
}
And now I'm trying to get the course using the method getCourse(), and then pass it to the method workWithCourse()
fun main() {
val course = getCourse()
workWithCourse(course)
}
Error:
Type mismatch.
Required:
Course.ProgrammingCourse
Found:
Course
But I know the course type - Type, parameter that each course has. Can I, knowing this Type, cast the course (which I retrieve from getCourse() method) to a specific subclass ? Is there such a way ?
Please help me
P.S.
I don't need type checks like:
if(course is Course.ProgrammingCourse) {
workWithCourse(course)
}
I need the subclass to be automatically inferred by the Type parameter, if possible.
P.S.2
The need for such a solution is that I have a class that takes a Course, it doesn't know anything about a particular course, at the same time the class takes the Type that I want to use for identification. This class also receives an interface (by DI) for working with courses, a specific implementation of the interface is provided by the dagger(multibinding) by key, where I have the Type as the key. In the same way I want to pass by the same parameter Type specific subclass of my Course to my interface which working with specific courses.
No, there is no way for automatic inference to the best of my knowledge.
You returned a Course, and that's what you have. Being sealed here does not matter at all. Generally what you do here is use the when expression if you want to statically do different things depending on the type, but if it's just one type (ProgrammingCourse) that can be passed to workWithCourse, then an if is probably right, with dispatch using as.
That said, this looks like counter-productive design. If you can only work with one course, why do they even share a top level interface? The way the code is written implies working is a function that can take any course, or should be a method member. Anything else is very confusing. Perhaps workWithCourse should take a Course and use the when expression to dispatch it appropriately?
In kotlin you can specify the class explicitly with as.
val course = getCourse()
if (type == Type.PROGRAMMING) {
workWithCourse(course as Course.ProgrammingCourse)
}
*thanks Joffrey for his comment
What you seem to be asking for is a compile-time guarantee for something that will only be known at runtime. You didn't share the condition used in getCourse(), but in general it could return both types.
Therefore, you need to decide what will happen in both cases - that's not something the compiler can decide for you via any "inference".
If you want the program to throw an exception when getCourse() returns something else than a Course.ProgrammingCourse, you can cast the returned value using as:
val course = getCourse() as Course.ProgrammingCourse
workWithCourse(course)
If you don't want to crash, but you only want to call workWithCourse in some cases, then you need an if or when statement to express that choice. For instance, to call it only when the value is of type Course.ProgrammingCourse, then you would write the code you already know:
if (course is Course.ProgrammingCourse) {
workWithCourse(course)
}
Or with a when statement:
val course = getCourse()
when (course) {
is Course.ProgrammingCourse -> workWithCourse(course)
is Course.LanguageCourse -> TODO("do something with the other value")
}
The when is better IMO because it forces you (or other devs in the team) to take a look at this when whenever you (or they) add a new subclass of the sealed class. It's easy to forget with an if.
You can also decide to not test the actual type, and focus on the type property like in #grigory-panov's answer, but that is brittle because it relies on an implicit relationship between the type property and the actual type of the value:
val course = getCourse()
if (type == Type.PROGRAMMING) {
workWithCourse(course as Course.ProgrammingCourse)
}
The main point of using sealed classes is so you can use their actual type instead of a manually managed type property + casts. So I'd say use only is X and don't set a type property at all. Using a sealed class allows Kotlin to type-check a bunch of things, it's more powerful than using such a property.
I have to store and update the below variables in Kotlin
string name;
Array of Class Objects(5)
Array of Int(5)
C++ format:
struct subject
{
string name;
Array of Class Objects(5)
Array of Int(5)
};
vector<subject> sub;
In other programming languages C/C++ for ex, we use struct and put everything above in that.
Questions:
How to store and update above values with mixture of different types like Array, string, etc., in Kotlin?
Arrays will not get updated in one stretch. Ex: When someone calls AIDL interface with name, I create instance of class and stored the object in array of class obj(0) and integer array(0) as well updated with some value.
When the same AIDL interface is called with same name again, second instance of class will be created and store in **array of class obj(1)**and integer array(1) as well updated. As name is same, there is no need to update it again.
How to check the name and update the other arrays in the run time?
An additional use case, I need to make vector of that struct(according to C++). How I can achieve this in Kotlin?
Instead of a struct you would use a class in Kotlin: https://kotlinlang.org/docs/classes.html. There are several differences between the two that are relevant:
The declaration and class members and there implementation are done in the same place.
The constructor declaration is built into the class declaration.
Kotlin leans towards immutability. While you can reassign fields more often you will see val (like const) and immutable collections.
With that said, you would do something like this to implement your struct in Kotlin. The following isn't a literal 1 for 1 translation, but rather how you might solve your problem with idiomatic Kotlin:
class Subject(val name: String) {
val objects = mutableListOf<NameOfThatClass>()
val numbers = mutableListOf<Int>()
}
What's going on in that code snippet is that we are declaring a class Subject. It has a constructor that takes one argument called name of type String. The val keyword means that the argument will also be kept as a member variable, and that member variable cannot be reassigned. Next, in the class body, we declare and assign two more member variables. objects and numbers will also not be reassignable because of the val keyword, but instead of receiving a constructor argument as a value they receive the result of calling mutableListOf(), which creates more or less the equivalent of a vector. We could also use arrayOfNulls(5) and arrayOfInt(5), but unless you very specifically need fixed-sized arrays it's easier and more common to use lists in Kotlin.
You would then use it like so:
val myName = "foo"
val myFirstObject = ...
val myFirstNumber = 1
val mySubject = Subject(myName)
mySubject.objects += myFirstObject
mySubject.numbers += myFirstNumber
The += you see there isn't an actual reassignment, but an operator overload that acts as Kotlin's equivalent of std::vector's push_back(): https://kotlinlang.org/docs/collection-write.html#adding-elements.
Finally, as mentioned above, Kotlin's lists are what you would normally use in place of vector. However, it sounds like you want to be able to look up a specific entry by name, which is more efficient to do with a map https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-map/. You could do something like this:
val myMap = mutableMapOf<String, Subject>()
// add to the map like this
myMap[name] = Subject(name)
// get from the map like this (returns null if not in the map)
val mySubject = myMap[name]
// check if the subject is already in the map like this
myMap.containsKey(name)
Then, if you need to iterate over all the Subjects like you would with a vector, you can use myMap.values to get just the Subjects.
I need to pass the type of a class as a parameter because of type erasure.
class Abc<T : Any>(private val clazz: KClass<T>)
I can get it to work when T is something like String, but I'm having trouble creating the argument for clazz when the type is KClass<MutableList<Foo<*>>>.
I've tried doing mutableListOf<Foo<*>>(), but then I get KClass<MutableList<out Foo<*>>> instead of KClass<MutableList<Foo<*>>>.
How can I create the KClass instance that I need?
If you need to construct an Abc<MutableList<Foo<*>>> so its methods end up taking and returning MutableList<Foo<*>>, it's enough to cast it:
val abc = Abc(MutableList::class) as Abc<MutableList<Foo<*>>>
(you could cast the argument to KClass<MutableList<Foo<*>>> instead, but this makes no difference).
But as Tenfour04's comment says, there are no different KClass instances for MutableList<Foo<*>>, MutableList<String>, etc. so:
you can't expect actually different behavior for Abc<MutableList<Foo<*>>> and Abc<MutableList<AnythingElse>> except for the casts the compiler inserts;
by using type erasure in this way, you are giving up some type safety, and make possible ClassCastExceptions far from the original cast.
I am running some experiments on Kotlin's reflection.
I am trying to get a reflection object of a generic class with its argument.
In Java, that would be a ParameterizedType.
The way to get such a thing using Java's reflection API is a bit convoluted: create an anonymous subclass of a generic class, then get its super-type first parameter.
Here's an example:
#Suppress("unused") #PublishedApi
internal abstract class TypeReference<T> {}
inline fun <reified T> jGeneric() =
((object : TypeReference<T>() {}).javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0]
When I println(jGeneric<List<String?>>()), it prints java.util.List<? extends java.lang.String>, which is logical considering that Kotlin's List uses declaration-site out variance and that Java types have no notion of nullability.
Now, I would like to achieve the same kind of result, but with the Kotlin reflection API (that would, of course, contain nullability information).
Of course, List<String>::class cannot work since it yields a KClass. and I am looking for a KType.
However, when I try this:
inline fun <reified T> kGeneric() =
(object : TypeReference<T>() {})::class.supertypes[0].arguments[0].type
When I println(kGeneric<List<String?>>()), it prints [ERROR : Unknown type parameter 0], which is quite... well, anticlimactic ;)
How can I get, in Kotlin, a KType reflecting List<String> ?
To create a KType instance in Kotlin 1.1, you have two options:
To create a simple non-nullable type out of a KClass, where the class is either not generic or you can substitute all its type parameters with star projections (*), use the starProjectedType property. For example, the following creates a KType representing a non-nullable type String:
val nonNullStringType = String::class.starProjectedType
Or, the following creates a KType representing a non-nullable type List<*>:
val nonNullListOfSmth = List::class.starProjectedType
For more complex cases, use the createType function. It takes the class, type arguments and whether or not the type should be nullable. Type arguments are a list of KTypeProjection which is simply a type + variance (in/out/none). For example, the following code creates a KType instance representing List<String>:
val nonNullStringType = String::class.starProjectedType
val projection = KTypeProjection.invariant(nonNullStringType)
val listOfStrings = listClass.createType(listOf(projection))
Or, the following creates the type List<String>?:
val listOfStrings = listClass.createType(listOf(projection), nullable = true)
Both starProjectedType and createType are defined in package kotlin.reflect.full.
We're planning to introduce the possibility of getting a KType instance simply from a reified type parameter of an inline function which would help in some cases where the needed type is known statically, however currently it's not entirely clear if that's possible without major overhead. So, until that's implemented, please use the declarations explained above.
Here is my code snippet, and the newer "constructParameterizedType" doesn't match my needs (unless I am missing something, which I assume I am). I have a genericized class called Result where T is any basic class that extends my "Inflatable" base class. represents the data records coming back from Salesforce REST API... so here is example of code that is working:
Class c = Class.forName("sfshare.UserRecord" );
JavaType type = mapper.getTypeFactory().constructParametricType(Result.class, c);
Result<T> res = mapper.readValue(rspData, type);
But if I use the newer (non-deprecated) "constructParameterizedType()" method, this same code will not compile because it isn't matching the parameters of constructParameterizedType. But constructParameterizedType isn't in use much yet and there are no examples to use... only the Javadoc - which doesn't make sense for my use-case.
If you look at arguments and specifically Javadocs, you will note that there is a new type: 2nd argument is the intended 'target' for parameters.
To give an example of meaning is that if you want to construct equivalent of:
ArrayList<String>
what you want to pass as arguments are:
constructParameterizedType(ArrayList.class, List.class, String.class)
or, possibly, Collection.class for second argument.
Think of it as the underlying relevant type you are trying to provide parameters for.
The underlying reason for this change is somewhat complicated and has to do with handling of "add-on" interfaces like Iterable<T>: for those cases it is necessary to provide different classes.
But in most end-user use cases you will just need to pass the same class as first and second argument.
Try this:
Class c = Class.forName("sfshare.UserRecord");
TypeFactory typeFactory = mapper.getTypeFactory();
JavaType type = typeFactory.constructParametrizedType(Result.class, Result.class, c);
Result<T> res = mapper.readValue(rspData, type);
or if your Result<T> class implements an interface:
JavaType type = typeFactory.constructParametrizedType(Result.class, ResultInterface.class, c);