Some languages (Java in particular) blur the line between types and classes. You may even see them used interchangeably in tutorials, documentation, etc. While they are related concepts, they are not synonymous.
Here's a 30-second explanation.
Consider the Vector
class
jshell> var a = new Vector<Integer>(List.of(1, 2, 3))
a ==> [1, 2, 3]
Vector a
is an instance of the Vector
class. But its type is not Vector
, but rather Vector<Integer>
. A Vector<Integer>
and (for example) a Vector<String>
may be instances of the same class (Vector
), but they are clearly not the same type -- they hold totally different kinds of data!
In addition, Vector
cannot be a type, because it takes a type parameter itself. When we write Vector
, we actually mean Vector<E>
, where E
is a type parameter.
We can draw a parallel with constructor arguments. Consider this simple class
jshell> public class Dog {
...> private String _name;
...> public Dog(String name) {
...> _name = name;
...> }
...> public String name() { return _name; }
...> }
| modified class Dog
jshell> var Fido = new Dog("Fido")
Fido ==> Dog@68de145
jshell> var Spot = new Dog("Spot")
Spot ==> Dog@46f7f36a
You can think of class Dog
as a sort of parameterized object constructor. It takes a single parameter (or "argument"), name
, and uses that name
to construct an object, a Dog
.
Similarly, generic classes like Vector<E>
can be thought of as parameterized type constructors. They take a single parameter, which in this case is another type, and use that type to construct a new (parameterized) type.
So why is this so confusing in Java?
Because Java uses raw types, which you may occasionally see in compiler or jshell
warnings
jshell> var b = new Vector(List.of(1, 2, 3))
| Warning:
| unchecked call to Vector(java.util.Collection<? extends E>) as a member of the raw type java.util.Vector
| var b = new Vector(List.of(1, 2, 3));
| ^--------------------------^
b ==> [1, 2, 3]
Java will grudgingly compile this code, but since you didn't tell it what type E
is, it assumes it to be Object
. Look what happens when I try to add a String
to b
jshell> b.add("hey")
| Warning:
| unchecked call to add(E) as a member of the raw type java.util.Vector
| b.add("hey")
| ^----------^
$23 ==> true
jshell> b
b ==> [1, 2, 3, hey]
Java can't narrow the type of the elements within the Vector
at all. All it knows is that they must be some kind of Object
s. Since String
is an Object
, it has no problem adding it to this Vector
.
If you try the same with a
, you'll get an error
jshell> a.add("hey")
| Error:
| incompatible types: java.lang.String cannot be converted to java.lang.Integer
| a.add("hey")
| ^---^
Raw types are a holdover from a time before Java had these type parameterizations (nearly two decades ago now). But they still regularly cause confusion among the uninitiated as to the difference between a type and a class in Java.
Remember that a Vector<String>
and a Vector<Integer>
are both Vector
s (they are instances of the same class), but they do not hold the same kind of data, so they do not have the same type.