Lists with Wildcards Cause Generic Voodoo Error

Lists with wildcards cause Generic voodoo error

(I assume here that Bar and Baz are both subtypes of Foo.)

List<? extends Foo> means a list of elements of some type, which is a subtype of Foo, but we don't know which type. Examples of such lists would be a ArrayList<Foo>, a LinkedList<Bar> and a ArrayList<Baz>.

As we don't know which subtype is the type parameter, we can't put Foo objects into it, neither Bar or Baz objects. But we still know that the type parameter is a subtype of Foo, so every element already in the list (and which we can get from the list) must be a Foo object, so we can use Foo f = list.get(0); and similar things.

Such a list can only be used for taking elements out of the list, not to adding elements at all (apart from null, but I don't know if the compiler actually allows this).

A List<Foo> on the other hand allows adding any object which is a Foo object - and as Bar and Baz are subtypes of Foo, all Bar and Baz objects are Foo objects, so they can be added, too.

F# compilation error: Unexpected type application

It looks that providing type parameters when treating method as a first class value isn't supported. I checked the F# specification and here are some important bits:

14.2.2 Item-Qualified Lookup
[If the application expression begins with:]

  • <types> expr, then use <types> as the type arguments and expr as the expression
    argument.
  • expr, then use expr as the expression argument.
  • otherwise use no expression argument or type arguments.
  • If the [method] is labelled with the
    RequiresExplicitTypeArguments attribute then explicit type arguments must have
    been given.

If you specify type arguments and arguments, then the first case applies, but as you can see, the specification requires some actual arguments too. I'm not quite sure what is the motivation behind this, though.

Anyway, if you use the type parameter anywhere in the type signature of the member, then you can specify it using type annotations like this:

type Foo() = 
member this.Bar<´T> (arg0:string) : ´T =
Unchecked.defaultof<´T>

let f = new Foo()
"string" |> (f.Bar : _ -> Int32)

On the other hand, if you don't use the type parameter anywhere in the signature, then I'm not quite sure why you need it in the first place. If you need it just for some runtime processing, then you may be able to take the runtime type representation as an argument:

type Foo() = 
member this.Bar (t:Type) (arg0:string) = ()

let f = new Foo()
"string" |> f.Bar typeof<Int32>

Collection? extends T vs CollectionT

Consider the following

class Animal { }
class Horse extends Animal { }

private static void specific(List<Animal> param) { }
private static void wildcard(List<? extends Animal> param) { }

Without the extends syntax you can only use the exact class in the signature

    specific(new ArrayList<Horse>()); // <== compiler error

With the wildcard extends you can allow any subclasses of Animal

    wildcard(new ArrayList<Horse>());  // <== OK

It's generally better to use the ? extends syntax as it makes your code more reusable and future-proof.

Storing different types of elements in a List in Java

Since the common ancestor of your classes is Object, and because List<? extends Object> does not make things any cleaner (after all, everything extends Object) it looks like List<Object> would be an OK choice.

However, such list would be a mixed bag: you would need to check run-time type of the object inside, and make decisions based on that. This is definitely not a good thing.

A better alternative would be creating your own class that implements operations on elements of the list the uniform way, and make one subclass for each subtype that implements these operations differently. This would let you treat the list in a uniform way, pushing the per-object differentiation into your wrappers.

public interface ItemWrapper {
int calculateSomething();
}

public abstract class IntWrapper implements ItemWrapper {
private int value;

public IntWrapper(int v) {
value=v;
}

public int calculateSomething() {
return value;
}
}

public abstract class DoubleListWrapper implements ItemWrapper {
private List<Double> list;

public DoubleListWrapper (List<Double> lst) {
list = lst;
}

public int calculateSomething() {
int res;
for (Double d : list) {
res += d;
}

return res;
}
}
// ...and so on

Now you can make a list of ItemWrapper objects, and calculateSomething on them without checking their type:

List<ItemWrapper> myList = new ArrayList<ItemWrapper>();

for (ItemWrapper w : myList) {
System.out.println(
w.calculateSomething());
}


Related Topics



Leave a reply



Submit