In Kotlin you can have a generic function like this:
fun <T> singletonList(item: T): List<T> {
// ...
}
I don't understand what the purpose of the <T> after the fun keyword is for. The function returns List<T>, so what is the point of <T>?
To be able to create a generic function the compiler must know that you want to work with diferent types. Kotlin is (like Java or C#) a strongly typed language. so just passing different types into a function will make the compiler mad.
To tell the compiler that a function should be accepting multiple types you need to add a "Type Parameter"
The <T> after fun is the definition of said "Type Parameter".
Which is then used at the item Argument.
Now the compiler knows that you'll specifiy the type of item when you make the call to singletonList(item: T)
Just doing
fun singletonList(item: T) : List<T> {[...]}
would make the compiler unhappy as it does not know T.
(As long as you don't have a class named T)
You also can have multiple "Type Params" when you separate them with commas:
fun <T, U> otherFunction(firstParam: T, secondParam: U): ReturnType
This is a generic function which, as per the language's syntax requirements, needs to provide this part <T>. You can use it to specify T further:
fun <T: Number> singletonList(item: T): List<T> {
// ...
}
It's also common to have multiple generic types:
fun <T: Number, R: Any> singletonList(item: T): R {
// ...
}
Related
I am trying to declare two suspend methods with list of String and PublishRequest Object as parameter. But the IDE is giving error with this.
The error is either make one of the function internal or remove suspend. But i want to use coroutines inside both of them.
override suspend fun publish(publishRequests: List<PublishRequest>) {
///code
}
suspend fun publish(events: List<String>) {
///code
}
The PublishRequest Data class is internal. The issues is only coming when we add the publish(events: List) method. The code is working fine the publish(publishRequests: List)
Can you explain why it is happening ?
The problem you are facing is related to type erasure.
The types List<PublishRequest> and List<String> are erased to List<*>, as consequence, you would have a JVM signature clash.
To solve your problem you have two different solutions.
Change their names and avoid a signature clash:
suspend fun publishRequests(publishRequests: List<PublishRequest>) {}
suspend fun publishEvents(events: List<String>) {}
Use a single function with a reified type and handle the different type classes inside that function:
suspend inline fun <reified T> publish(objects: List<T>) {
when {
PublishRequest::class.java.isAssignableFrom(T::class.java) -> // it's a list of PublishRequest
T::class == String::class -> // it's a list of String
}
}
I want to write a function like this, where T must be the same data-type for both variables, but can be anything as long as they are the same.
fun <T> doSomething(var1: T, var2: T) {}
When I write this, it will work with any two variables, such as
doSomething(5, listOf<Thread>())
The current assumption is it assumes that since int and list are different, that T is now considered Any?
Is there any way to make this work? A coworker is using Swift and it works as expected for him, failing to compile if either item is a different type.
I tried using reified functions as well, but same problem. It only causes a compile error if I explicitly added the reified class at the start, it just assumes it's any otherwise.
E.G.
inline fun <reified T> doSomething(var1: T, var2: T) { }
doSomething(1,"2") <-- unwanted compile
doSomething<String>(1,"2") <-- Will not compile
But I don't want to only have the function work if the person remembers to add the explicit type on it...
Sounds like maybe your coworker is adding associated type constraints. Kotlin doesn't have associated types, but it might be similar to specifying your Kotlin function with something like this:
fun <T> doSomething(var1: Comparable<T>, var2: Comparable<T>)
which in Kotlin would enforce the same type for primitives, for instance doSomething(3, 4). doSomething(4, "x") would fail to compile because the arguments do not both implement a Comparable of the same type.
If you also need to handle collections, you can overload the function:
fun <T> doSomething(var1: Iterable<Comparable<T>>, var2: Iterable<Comparable<T>>)
This probably covers most of the use cases you described in the comments.
I don't like comparable approach because it won't work for all types. I think this will do the trick, although has strange syntax:
fun <T> doSomething(var1: T) = fun(var2: T) {
println("Hello $var1 and $var2")
}
fun te() {
doSomething(42)("Text") // Error
doSomething(42)(78) // OK
}
You can also easily expand it:
fun <T> doSomething(var1: T) = fun(var2: T) = fun(var3: T) {
println("Hello $var1 and $var2 and $var3")
}
Is it possible to add extension function to all classes? I was thinking about adding it to some common base class like Object. Is it possible?
With Kotlin, Any is the super type like Object for Java.
fun Any.myExtensionFunction() {
// ...
}
And if you want to support null-receiver too:
fun Any?.myExtensionFunction() {
// ...
}
It depends on whether you want to use the type of the object in the signature (either in another argument, or in the return type). If not, use Any(?) as Kevin Robatel's answer says; but if you do, you need to use generics, e.g. (from the standard library)
inline fun <T, R> T.run(block: T.() -> R): R
inline fun <T> T.takeIf(predicate: (T) -> Boolean): T?
etc.
I tried to create an alias function to Flowable.flatmap() as follow, but compile error.
fun <T, R> Flowable<T>.then(mapper: Function<T, Publisher<R>>): Flowable<R> {
return flatMap(mapper)
}
The error is : One type argument expected for interface Function<out R> defined in kotlin
Have any idea? Thanks!
The flatMap takes a java.util.function.Function, the actually error is you didn't import the java.util.function.Function in your Kotlin file, but I don't suggest you use the java-8 functions because you can't take advantage of the SAM Conversions to use the lambda directly from the Kotlin code which defined with java-8 functional interface as parameter type.
You should replace Function with Function1, since the Function interface is a Kotlin marker interface only. for example:
// v--- use the `Function1<T,R>` here
fun <T, R> Flowable<T>.then(mapper: Function1<T, Publisher<R>>): Flowable<R> {
return flatMap(mapper)
}
OR use the Kotlin function type as below, for example:
// v--- use the Kotlin function type here
fun <T, R> Flowable<T>.then(mapper: (T) -> Publisher<R>): Flowable<R> {
return flatMap(mapper)
}
I need to be able to tell the generic type of kotlin collection at runtime. How can I do it?
val list1 = listOf("my", "list")
val list2 = listOf(1, 2, 3)
val list3 = listOf<Double>()
/* ... */
when(list.genericType()) {
is String -> handleString(list)
is Int -> handleInt(list)
is Double -> handleDouble(list)
}
Kotlin generics share Java's characteristic of being erased at compile time, so, at run time, those lists no longer carry the necessary information to do what you're asking. The exception to this is if you write an inline function, using reified types. For example this would work:
inline fun <reified T> handleList(l: List<T>) {
when (T::class) {
Int::class -> handleInt(l)
Double::class -> handleDouble(l)
String::class -> handleString(l)
}
}
fun main() {
handleList(mutableListOf(1,2,3))
}
Inline functions get expanded at every call site, though, and mess with your stack traces, so you should use them sparingly.
Depending on what you're trying to achieve, though, there's some alternatives. You can achieve something similar at the element level with sealed classes:
sealed class ElementType {
class DoubleElement(val x: Double) : ElementType()
class StringElement(val s: String) : ElementType()
class IntElement(val i: Int) : ElementType()
}
fun handleList(l: List<ElementType>) {
l.forEach {
when (it) {
is ElementType.DoubleElement -> handleDouble(it.x)
is ElementType.StringElement -> handleString(it.s)
is ElementType.IntElement -> handleInt(it.i)
}
}
}
You can use inline functions with reified type parameters to do that:
inline fun <reified T : Any> classOfList(list: List<T>) = T::class
(runnable demo, including how to check the type in a when statement)
This solution is limited to the cases where the actual type argument for T is known at compile time, because inline functions are transformed at compile time, and the compiler substitutes their reified type parameters with the real type at each call site.
On JVM, the type arguments of generic classes are erased at runtime, and there is basically no way to retrieve them from an arbitrary List<T> (e.g. a list passed into a non-inline function as List<T> -- T is not known at compile-time for each call and is erased at runtime)
If you need more control over the reified type parameter inside the function, you might find this Q&A useful.