2

I have an issue where I want to use a generic type, and the type has overloaded methods with a Collection and varargs. When I use Any as type parameter, I cant pass in a List as argument.

    val productType = ComboBox<Any>("Product type")
//    public void setItems(Collection<T> items) {
//    public void setItems(T... items) {
    productType.setItems(listOf("1","2")) // Not possible

Is there any better solution besides converting the list to an array via: productType.setItems(*listOf("1","2").toTypedArray())

Edit: Working example to try out:

   class Foo<T>() {
        fun setItems(items: Collection<T>) {}
        fun setItems(vararg items: T) {}
    }
        val f = Foo<Any>()
        f.setItems(listOf("1", "2"))
PhilBa
  • 732
  • 4
  • 16
  • What if you use `ComboBox`? – EpicPandaForce Feb 27 '20 at 13:18
  • How about `ComboBox<*>` ? – Neo Feb 27 '20 at 13:48
  • `ComboBox` works of course. The issue seems to be the `Any`, since it could also mean `ComboBox>`, thus leading to the ambiguity. `ComboBox<*>` is not possible. – PhilBa Feb 27 '20 at 14:30
  • Can you show your ComboBox class declaration and the declaration of `setItems(Collection)`? I tried creating my own, and it worked fine. – Tenfour04 Feb 27 '20 at 15:12
  • Hi @Tenfour04, the class is from Vaadin and the the methods are as posted in the comment. Maybe the issue is related to the class bein a java class? Edit: no it's not. Added Working example to description. – PhilBa Feb 27 '20 at 15:28
  • How is the class declared? What are the bounds of the generic type? – Tenfour04 Feb 27 '20 at 16:39

3 Answers3

1

You can add an extension method calling the desired overload without ambiguity:

fun <T> ComboBox<T>.setCollection(items: Collection<T>) { this.setItems(items) }

and then

productType.setCollection(listOf("1","2"))
Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
0

If you really need to call the overload that accepts a Collection<T> (for example, it has some desired side effects that the other one doesn't have), then there's a solution that tells the compiler to do that.

This is similar to: How to deal with an overload resolution ambiguity of functions with generics?

Basically, you can make an unchecked cast that would limit the type argument of ComboBox so that List<String> no longer matches the signature with a vararg of the argument type and there's only the signature accepting the Collection that matches.

For that, you need to choose a new type argument, say R, that satisfies both:

  • List<R> is not a subtype of R
  • List<R> is still a subtype of Collection<R>

One can note that a type that we can use as R is String, giving this:

@Suppress("UNCHECKED_CAST")
(productType as ComboBox<String>).setItems(listOf("1","2"))

However, if the behavior of the two overloads is identical, it's simpler to pass an array to the vararg overload using *list.toTypedArray(), just as you mentioned.

hotkey
  • 140,743
  • 39
  • 371
  • 326
  • I ended up with a solution utilizing both answers: ``` fun setProductTypeItems(productTypes: List) { (productType as ComboBox).setItems(productTypes) } ``` – PhilBa Feb 28 '20 at 08:14
0

The following should work

fun <T>setProductTypeItems(productTypes: List<T>) {
    (productType as ComboBox<T>).setItems(productTypes)
}
KristofMols
  • 3,487
  • 2
  • 38
  • 48
PhilBa
  • 732
  • 4
  • 16