Is it possible to declare generic wildcards in Kotlin like this code in Java:
List<Integer> a = new ArrayList<>();
List<? extends Number> b = a;
The equivalent in Kotlin would be like this:
val a = ArrayList<Int>()
val b: ArrayList<out Number> = a
Kotlin doesn't have wildcards, it uses the concepts of declaration-site variance and type projections instead.
Please check the documentation, covers pretty extensively.
Kotlin provides so called star-projection
val a = ArrayList<Int>()
val b: ArrayList<out Number> = a
Related
I have some Hash Maps of some objects <String, Object>, some goes like this:
val mapA = HashMap<String, A>
val mapB = HashMap<String, B>
val mapC = HashMap<String, C>
I want to create a function that takes any Hash Map to print it out. This is what I mean:
fun printHashMap(hashMap: HashMap<String, (any type here, for example A, B or C)>){
// print each element of hashmap
}
What I've tried so far:
fun printHashMap(hashMap: HashMap<Any, Any>){
// print HashMap here
}
But it throws me Type mismatch, <Any, Any> vs <String, A> for example.
Is this possible in Kotlin?
If you don't need specific print format, you can print hashMap without your own function:
val testMap = HashMap<String, Any>()
testMap["1"] = 1
testMap["2"] = "2"
print(testMap)
Otherwise, you should change your signature of method to
fun printHashMap(map: Map<String, *>)
In functions better to use Interface instead of concrete class. And instead of Any you need to use star-projections. It is the safe way here is to define such a projection of the generic type, that every concrete instantiation of that generic type would be a subtype of that projection.
You can read more about it here and here
I have a val built like this
val qs = hashMapOf<KProperty1<ProfileModel.PersonalInfo, *> ,Question>()
How can I obtain the class of ProfileModel.PersonalInfo from this variable?
In other words what expression(involving qs of course) should replace Any so that this test passes.
#Test
fun obtaionResultTypeFromQuestionList(){
val resultType = Any()
assertEquals(ProfileModel.PersonalInfo::class, resultType)
}
Thank you for your attention
There is no straight way to get such information due to Java type erasure.
To be short - all information about generics (in your case) is unavailable at runtime and HashMap<String, String> becomes HashMap.
But if you do some changes on JVM-level, like defining new class, information about actual type parameters is kept. It gives you ability to do some hacks like this:
val toResolve = object : HashMap<KProperty1<ProfileModel.PersonalInfo, *> ,Question>() {
init {
//fill your data here
}
}
val parameterized = toResolve::class.java.genericSuperclass as ParameterizedType
val property = parameterized.actualTypeArguments[0] as ParameterizedType
print(property.actualTypeArguments[0])
prints ProfileModel.PersonalInfo.
Explanation:
We define new anonymous class which impacts JVM-level, not only runtime, so info about generic is left
We get generic supperclass of our new anonymous class instance what results in HashMap< ... , ... >
We get first type which is passed to HashMap generic brackets. It gives us KProperty1< ... , ... >
Do previous step with KProperty1
Kotlin is tied to the JVM type erasure as well as Java does. You can do a code a bit nice by moving creation of hash map to separate function:
inline fun <reified K, reified V> genericHashMapOf(
vararg pairs: Pair<K, V>
): HashMap<K, V> = object : HashMap<K, V>() {
init {
putAll(pairs)
}
}
...
val hashMap = genericHashMapOf(something to something)
I have interface A and class B: A
val a: Stream<A>
val b: Stream<B> = a.filter { it is B }
.map { it as B }
Is there a way to write this using Kotlin smart casts?
Is there a way to write this using Kotlin smart casts?
No, this is not possible with basic static analysis as there no indicator that a, after being filtered, only contains Bs, as it is still a Stream<A>, so you must check this yourself. Kotlin's smart casting is applied only to the value of a variable and not to a type parameter.
AFAIK there isn't anything that does this in the Java or Kotlin libraries for Streams, but you could convert the stream to a Sequence and use filterIsInstance:
a.asSequence().filterIsInstance<B>().asStream()
Of course, you could also implement this functionality directly on streams with an extension method:
inline fun <reified B> Stream<*>.filterIsInstance() = a.filter { it is B }.map { it as B }
...
val a: Stream<A>
val b: Stream<B> = a.filterIsInstance<B>()
Just a comment: do you need to use Streams? I would consider using Kotlin's Sequences from the start instead.
The Java
Consider the following Java:
private void example() {
Optional<String> foo = ...
Optional<String> bar =
foo.map(this::transform1)
.map(this::transform2)
}
private String transform1(String s) {
return s + "!";
}
private String transform2(String s) {
return "#" + s;
}
Note that the transform methods accept non-Optional values.
Question
Does Kotlin have a built-in mechanism to achieve the same thing when using its nullable/non-nullabe types?
The first attempt
I managed to achieve this effect with the following:
fun <A, B> nullMap(a: A?, mapper: (a: A) -> B?): B? =
if (a != null) mapper.invoke(a) else null
fun example() {
val foo: String? = "Space cookies"
val bar1: String? = nullMap(foo, Example::transform1)
val bar2: String? = nullMap(bar1, Example::transform2)
}
fun transform(s: String) = s + "!"
fun transform2(s: String) = "#" + s
(Again, we note that for the transform methods, s is a non-nullable String.)
So, my question stated another way is: Does a nullMap equivalent already exist in Kotlin's standard library? And/or: is there a different standard way of doing this?
Finally, can the visually cleaner chaining approach be achieved in Kotlin, rather than either separate assignments (above) or nested function calls? For a moment I considered that you could add nullMap as an extension method on Any, but sometimes the receiver you're trying to invoke a method on is null (so that won't work).
We use safe call operator:
val foo: Foo? = ...
foo?.toA()?.toB()
Or
foo?.let(this::transform1)?.let(this::transform2)
Kotlin emphasis on null safety. There is an entire chapter in its manual describing related technics.
This works fine:
class Wrapped<out T>(val value: T)
open class Wrapper<T> {
fun wrap(map: T): Wrapped<T> = Wrapped(map)
}
class Wrapper2 : Wrapper<Map<String, String>>()
val wrapped: Wrapped<Map<String, String>> = Wrapper2().wrap(mapOf())
But, when I try to access Wrapper2.wrap from Java, the Map comes back with a wildcard type:
Map<String, String> toWrap = new HashMap<>();
Wrapped<Map<String, String>> result;
result = new Wrapper<Map<String, String>>().wrap(toWrap); // ok
result = new Wrapper2().wrap(toWrap); // NOT ok, returns Wrapped<Map<String, ? extends String>>
I can work around this by overriding wrap in Wrapper2 with the explicit type.
Why does Wrapper2.wrap return a different type than Wrapper.wrap?
You can suppress Kotlin using wildcards in generics as described in the Kotlin reference where it describes the #JvmSuppressWildcards annotation (or the reverse of that #JvmWildcard annotation).
From the docs:
On the other hand, if we don't need wildcards where they are generated, we can use #JvmSuppressWildcards:
fun unboxBase(box: Box<#JvmSuppressWildcards Base>): Base = box.value
// is translated to
// Base unboxBase(Box<Base> box) { ... }
NOTE: #JvmSuppressWildcards can be used not only on individual type arguments, but on entire declarations, such as functions or classes, causing all wildcards inside them to be suppressed.
Change
class Wrapper2 : Wrapper<Map<String, String>>()
to
class Wrapper2 : Wrapper<MutableMap<String, String>>()
You'll see in the Kotlin source,
public interface Map<K, out V> {
whereas:
public interface MutableMap<K, V> : Map<K, V> {
I believe out V is the reason you're getting ? extends String, see Covariance under the generics docs for Kotlin and a quick search on Google should give you some more insight into covariance and contravariance in Java .