views:

159

answers:

5

I have a class with Maps from Ks to Set<V>s, for several different Vs. I'd like to use a generic factory ala:

protected static <T> Set<T> SetFactory(int size) {
     return new HashSet<T>(size);
}

to do something like

for (K key : keySet) map.put(key, SetFactory());

and have the generic part work (I get a compile error, type mismatch between Set<Object> and Set<V>), like it does with Set<V> x = SetFactory();

Are there alternatives to passing a Class<V> argument to SetFactory()?

A: 

You need an explicit assignment:

for (K key : keySet) {
  Set<V> mySet = setFactory(20);
  map.put(key, mySet);
}

Without it, there's no way for compiler to know the type of <V>

ChssPly76
I've specified that it's Map<K,Set<V>> - and it knows enough to tell me no when I try to put things that *aren't* Set<V> in it. Practically, of course you're right - but it seems strange that it can't resolve what what SetFactory is supposed to return, but it can tell me if I try to put the wrong thing into the Map.
Carl
A: 

Apache Commons Collections provides a LazyMap in which you supply a Factory object which creates the values of the map. You can supply a Factory which creates your Set values.

Now, Commons Collections doesn't use generics, but there is a generic port of it here.

I hope I interpreted your question correctly - it was rather confusing.

skaffman
A: 

If you absolutely must have it on one line you could put a baloney assignment in the put:

 Set<V> s = null;
 for (K key : keySet) map.put(key, s = setFactory());

But that answer is more to show another way to do it, not actually suggest it is a better way.

Yishai
Actually, I think that syntax is quite elegant - thanks for the suggestion!
Carl
A: 

Hrm, actually this seems to work:

protected static <K,V> Map<K,Set<V>> mapFactory(Collection<K> keys) {
    Set<V> holder;
    Map<K,Set<V>> result = new HashMap<K,Set<V>>(keys.size());
    for (K key : keys) result.put(key, holder = SetFactory());
    return result;
}

and even gets me out of the annoyance of having to initially allocate the Map as well. I suppose I need to add a factory to the argument list, if I want to actually be able to override the class.

Carl
That's the same kludge suggested by ChssPly76, but it's not necessary. Specify the type argument as shown in my example (it's *not* a cast).
erickson
I've changed my actual code, and I'll agree that it's not a cast, it's just that practically it *looks* a lot like a cast.
Carl
+2  A: 

I don't really understand your problem. Is this what you're looking for?

class MyClass {

  protected static <T> Set<T> SetFactory(int size)
  {
    return new HashSet<T>(size);
  }

  protected static <K, V> Map<K, Set<V>> mapFactory(Collection<K> keys)
  {
    Map<K, Set<V>> result = new HashMap<K, Set<V>>(keys.size());
    for (K key : keys)
      result.put(key, MyClass.<V>SetFactory(20));
    return result;
  }

}

Note the explicit type parameter (<V>) on the generic method invocation.


Update:

I'm probably wrong, but stating there is no cast seems like semantics to me - having to put the MyClass. on front seems pretty close to having to cast. Can you explain the distinction?

A cast would be (Set<V>) SetFactory(20), and it would generate a compiler warning about type safety, because with type erasure there is no way to check that the run-time type of the result of the SetFactory function has a type of V.

Using a type parameter on the method invocation is much like saying new HashSet<V>(). When you invoke a constructor of a generic class, you have to specify the type arguments for the constructor. Whenever you invoke a generic method, the type parameters must be specified. Often, these can be inferred from an assignment of the result of the method, or from the method's parameters, but not always.

However, it's not a cast. A cast always performs a type check at run-time. Specifying the generic type as I show here works at compile-time.

erickson
right, except I'd like type inference automatically for SetFactory() - i.e., without having to cast.
Carl
There is no cast.
erickson
I'm probably wrong, but stating there is no cast seems like semantics to me - having to put the MyClass.<V> on front seems pretty close to having to cast. Can you explain the distinction?
Carl
This answer makes me want a "method<T>()" syntax, so that the "Myclass.<T>" isn't necessary. I read up some, and appreciate the technical distinction, but the "act-of-coding" difference seems pretty small.
Carl
What's the difference between a `<T>method()` syntax and a `method<T>()` syntax? Are you hung up on the `MyClass.` part, or the `<T>` part?
erickson