What is the different between the following Generics definitions in Scala:
class Foo[T <: List[_]]
and
class Bar[T <: List[Any]]
My gut tells me they are about the same but that the latter is more explicit. I am finding cases where the former compiles but the latter doesn't, but can't put my finger on the exact difference.
Thanks!
Edit:
Can I throw another into the mix?
class Baz[T <: List[_ <: Any]]
The former is a shorthand for an existential type when the code doesn't need to know what the type is or constrain it:
This form says that the element type of
List
is unknown toclass Foo
rather than your second form, which says specifically that theList
's element type isAny
.Check out this brief explanatory blog article on Existential Types in Scala.
OK, I figured I should have my take on it, instead of just posting comments. Sorry, this is going to be long, if you want the TLDR skip to the end.
As Randall Schulz said, here
_
is a shorthand for an existential type. Namely,is a shorthand for
Note that contrary to what Randall Shulz's answer mentions (full disclosure: I got it wrong too in an earlier version fo this post, thanks to Jesper Nordenberg for pointing it out) this not the same as:
nor is it the same as:
Beware, it is easy to get it wrong (as my earlier goof shows): the author of the article referenced by Randall Shulz's answer got it wrong himself (see comments), and fixed it later. My main problem with this article is that in the example shown, the use of existentials is supposed to save us from a typing problem, but it does not. Go check the code, and try to compile
compileAndRun(helloWorldVM("Test"))
orcompileAndRun(intVM(42))
. Yep, does not compile. Simply makingcompileAndRun
generic inA
would make the code compile, and it would be much simpler. In short, that's probably not the best article to learn about existentials and what they are good for (the author himself acknowledge in a comment that the article "needs tidying up").So I would rather recommend reading this article: http://www.artima.com/scalazine/articles/scalas_type_system.html, in particular the sections named "Existential types" and "Variance in Java and Scala".
The important point that you you should get from this article is that existentials are useful (apart from being able to deal with generic java classes) when dealing with non covariant types. Here is an example.
This class is generic (note also that is is invariant), but we can see that
hello
really don't make use of the type parameter (unlikegetName
), so if I get an instance ofGreets
I should always be able to call it, whateverT
is. If I want to define a method that takes aGreets
instance and just calls itshello
method, I could try this:Sure enough, this does not compile, as
T
comes out of nowhere here.OK then, let's make the method generic:
Great, this works. We could also use existentials here:
Works too. So all in all, there is no real benefit here from using an existential (as in
sayHi3
) over type parameter (as insayHi2
).However, this changes if
Greets
appears itself as a type parameter to another generic class. Say by example that we want to store several instances ofGreets
(with differentT
) in a list. Let's try it:The last line does not compile because
Greets
is invariant, so aGreets[String]
andGreets[Symbol]
cannot be treated as aGreets[Any]
even thoughString
andSymbol
both extendsAny
.OK, let's try with an existential, using the shorthand notation
_
:This compiles fine, and you can do, as expected:
Now, remember that the reason we had a type checking problem in the first place was because
Greets
is invariant. If it was turned into a covariant class (class Greets[+T]
) then everything would have worked out of the box and we would never have needed existentials.So to sum up, existentials are usefull to deal with generic invariant classes, but if the generic class does not need to appear itself as a type parameter to another generic class, chances are that you don't need existentials and simply adding a type parameter to your method will work
Now come back(at last, I know!) to your specific question, regarding
Because
List
is covariant, this is for all intents and purpose the same as just saying:So in this case, using either notation is really just a matter of style.
However, if you replace
List
withSet
, things change:Set
is invariant and thus we are in the same situation as with theGreets
class from my example. Thus the above really is very different from