What is the difference between a class and a type in Scala (and Java)?

Solution 1:

When you say "type" I'm going to assume you mean static type mostly. But I'll talk about dynamic types shortly.

A static type is a property of a portion of a program that can be statically proven (static means "without running it"). In a statically typed language, every expression has a type whether you write it or not. For instance, in the Cish "int x = a * b + c - d", a,b,c,and d have types, a * b has a type, a * b + c has a type and a * b + c -d has a type. But we've only annotated x with a type. In other languages, such as Scala, C#, Haskell, SML, and F#, even that wouldn't be necessary.

Exactly what properties are provable depends on the type checker.

A Scala style class, on the other hand, is just the specification for a set of objects. That specification includes some type information and includes a lot of implementation and representation details such as method bodies and private fields, etc. In Scala a class also specifies some module boundaries.

Many languages have types but don't have classes and many languages have classes but don't have (static) types.

There are several observable differences between types and classes. List[String] is a type but not a class. In Scala List is class but normally not a type (it's actually a higher kinded type). In C# List isn't a type of any sort and in Java it's a "raw type".

Scala offers structural types. {def foo : Bar} means any object that provably has a foo method that returns a Bar, regardless of class. It's a type, but not a class.

Types can be abstracted using type parameters. When you write def foo[T](x : T) = ..., then inside the body of foo T is a type. But T is not a class.

Types can be virtual in Scala (i.e. "abstract type members"), but classes can't be virtual with Scala today (although there's a boilerplate heavy way to encode virtual classes https://wiki.scala-lang.org/display/SIW/VirtualClassesDesign)

Now, dynamic types. Dynamic types are properties of objects that the runtime automatically checks before performing certain operations. In dynamically typed class-based OO languages there's a strong correlation between types and classes. The same thing happens on JVM languages such as Scala and Java which have operations that can only be checked dynamically such as reflection and casting. In those languages, "type erasure" more or less means that the dynamic type of most objects is the same as their class. More or less. That's not true of, e.g., arrays which aren't typically erased so that the runtime can tell the difference between Array[Int] and Array[String]. But remember my broad definition "dynamic types are properties of objects that the runtime automatically checks." When you use reflection it is possible to send any message to any object. If the object supports that message then everything works out. Thus it makes sense to talk of all objects that can quack like a duck as a dynamic type, even though it's not a class. That's the essence of what the Python and Ruby communities call "duck typing." Also, by my broad definition even "zeroness" is a dynamic type in the sense that, in most languages, the runtime automatically checks numbers to make sure you don't divide by zero. There are a very, very few languages that can prove that statically by making zero (or not-zero) a static type.

Finally, as other's have mentioned, there are types like int which don't have a class as an implementation detail, types like Null and Any which are a bit special but COULD have classes and don't, and types like Nothing which doesn't even have any values let alone a class.

Solution 2:

Okay, I'll bite... James has a good answer, so I'm going to try a different tact and give a more down-to-earth viewpoint.

Broadly speaking, a class is something that can be instantiated. singleton objects (scala) traits (Scala) and interfaces (Scala) are also commonly considered to be classes. This makes sense, as singletons are still instantiated (via compiler-generated code) and an interface can be instantiated as part of a subclass.

Which brings us to the second point. classes are the primary unit of design in most object-oriented languages (though not the prototype-based ones like javascript). Polymorphism and subclassing are both defined in terms of classes. classes also provide a namespace and visibility controls.


types are a very different beast, every possible value that the system can express will have one or more types, and these can sometimes be equated to classes, for example:

(Int) => String // both the type and class are Function1[Int,String]
"hello world" // class and type are String    

You also get some interesting differences between Scala and Java:

7 // both the class and type are Int in Scala
  // in Java there's no class and the type is Integer.TYPE

println("hello world") // the return type is Unit, of class Unit
                       // Java has void as a type, but no corresponding class

error("oops") // the type and class are both "Nothing"

and the really fun types that aren't classes at all. For example, this.type always refers to the unique type of this. It's unique to a single instance and isn't even compatible with other instances of the same class.

There are also abstract types and type parameters. For example:

type A // 'A' is an undetermined abstract type
       // to be made concrete in a subclass

class Seq[T] { ... } // T is a type, but not a class

Seq is interesting as it's a class, but not a type. More accurately, it's a "type constructor"; something that will construct a valid type when supplied with the necessary type parameter. Another term for type constructors is "higher kinded types", I personally don't like this term, as "type constructor" encourages me to think in terms of supplying types like any other form of argument - a mental model that has served me well for Scala.

"higher-kinded" rightly implies that Seq has a "kind", which is * => *, this notation states that Seq will take a single type and yield a single type (this is similar to curried notation for describing functions). By way of comparison, the kind of Map is * => * => * because it takes two type parameters.

Solution 3:

A type can be useful by itself, without any instances. One example for this is called "phantom type". Here is an example for Java: http://michid.wordpress.com/2008/08/13/type-safe-builder-pattern-in-java/

In that example we have public static class Initializer<HA, HB>, where HA and HB take some types (represented by the abstract classes TRUE and FALSE), without ever beeing instantiated.

I hope this shows that types and classes are something different, and that types can be useful by itself.

Solution 4:

(Java only) I'd say, a type is a set of objects. Object o is type X, if o is a member of set X. Type X is a subtype of Y, if set X is a subset of Y.

For every class C (not interface) there is a set of objects, created from new C(...). Interestingly, we rarely cares about this set. (but every object does belong to a set like this, a fact that may be useful)

For every class C, there is a type t(C), generally refered to as "the type C", which is the set of all objects that can be created from new S(...) where S is C or a subclass of C.

Similarly, for every interface I, there is a type t(I), "the type I", which is the set of all objects that can be created from new S(...) where S implements I.

Obviously, if class S is a subclass of C, type S is a subtype of type C. Similar for interface I

There is a null type, which is the empty set. The null type is a subtype of every type.

There is a set of all objects, which is the type Object. It's a super type of every type.

So far, this formalism is pretty useless. A type is basically the same as a class or an interface, and the subtype relation is basically the subclass/subinterface relation. The triviality is a good thing, the language was understandable! But entering generics, there are more complicated types, and operations like unions and intersections of types. Types are no longer only classes and interfaces, and subtype relations are much richer and harder to understand.