4

I have written this interface as part of a framework.

public interface CollectionFactory {

    public <T> Collection<T> newCollection();
}

But I want the implementer to be able to define the returned type of the collection so they won’t have to cast like:

public interface CollectionFactory<C extends Collection> {
    public C newCollection();
}

The problem is that I then lose typesafety on T. I would like it to be

public interface CollectionFactory<C extends Collection> {
     public <T> C<T> newCollection();
}

And I don’t want to specify T in advance like so:

public interface CollectionFactory<T, C extends Collection<T>> {
    public C newCollection();
}

To the best of my knowledge this is not possible.
Would someone like to surprise me?

Also, just as an appetizer, does anyone know if something similar to this is possible in say… Scala?

orshachar
  • 4,837
  • 14
  • 45
  • 68
  • 1
    What error are you getting with that last one? – durron597 Nov 14 '12 at 13:26
  • Can you share some of the context with us? Maybe you don't need C at all and simple inheritance will cut it. – Adam Arold Nov 14 '12 at 13:29
  • @durron597 - Not getting an error, it’s just missing the use case. I don’t want to do it that way :) it binds my CollectionFactory instance to a specific T type at initialization, and that means a different factory per type, which is missing the point. – orshachar Nov 14 '12 at 16:25
  • @AdamArold It’s more of a theoretical exercise, it’s just a bit of old code I remembered and wanted to make typesafe when I came upon this issue. Every now and then I encounter problems with java’s generic model and I wonder if it’s only me… – orshachar Nov 14 '12 at 16:25
  • Maybe you ain't using the right tool for the task. Java generics is not a swiss army knife. – Adam Arold Nov 14 '12 at 16:26

2 Answers2

4

In Scala, you can use higher-kinded types if you want to parametrize over both C and T in this way:

// means that C has a single type parameter and C[T] extends Seq[T] whatever T is
// or C[_] <: Seq[_] which means C[T] must extend Seq[Something] but not necessarily Seq[T]
trait SeqFactory[C[T] <: Seq[T]] { 
  def newSeq[T]: C[T]
}

Example implementation:

object ListFactory extends SeqFactory[List] { 
  def newSeq[T] = List() 
}

You are correct that this can't be done in Java, but depending on the purpose, @Dylan's or @CostiCiudatu's solution may be good enough even if they are less typesafe.

Community
  • 1
  • 1
Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
  • I will learn to read that scala piece more in depth. Thanks. I will keep the question open so that java buffs can still give ideas though, if you don’t mind. – orshachar Nov 14 '12 at 16:23
3

On a per-method-invocation basis, you can try something like:

public interface CollectionFactory {
    public <T, C extends Collection<T>> C newCollection();
}
Costi Ciudatu
  • 37,042
  • 7
  • 56
  • 92
  • Thanks for your answer, it’s a good try - but it is missing the use case. For the CollectionFactory to be typesafe, C must be determined on the CollectionFactory interface. consider a HashSetCollectionFactory and an ArrayListCollectionFactory that will each return their respective types. Your solution will allow ArrayListCollectionFactory to potentially return a HashSet. – orshachar Nov 14 '12 at 16:22
  • Actually I got the point; but I'm afraid that's as close as you can get to it in Java. Anyway, a more detailed description of the usecase may help if you are also interested in alternative approaches for reaching this goal. – Costi Ciudatu Nov 14 '12 at 23:36