In one of the Stack Overflow answers it is quoted that:
Scala is a fully object oriented language, more so than Java, with one of the most advanced type systems available on non-research languages.
In what ways is Scala's type system more advanced than Java's?
Scala's type system can do pretty much everything Java's can (with some warts removed, like covariant arrays). In addition, it has the following features:
An abstract class C
that is generic on T
can be made a subtype of C[U]
where U
is a subtype or a supertype of T
.
class C[+T] // C[T] <: C[U] iff T <: U
class D[-T] // C[T] <: C[U] iff U <: T
This is incredibly useful when passing around immutable data structures parameterised on the type of values they contain. For example, List[String]
is a subtype of List[Any]
.
Java uses wildcards for this, thereby moving taking care of this to the user of the API rather than the definer. This is suboptimal in many cases.
In Java, a value of non-static inner class type stores a pointer to the object of the containing class type. In Scala this is also the case, except it also works this way in the type system. For example:
class P {
class C { }
}
val x = new P
val y = new P
var z = new x.C
z = new y.C // type error; x.C and y.C are distinct types
Java lacks this feature; x
and y
both have type P.C
.
A type cannot only be parameterised on another type but also on a type constructor:
trait Functor[F[_]] {
def map[T, U](function: T => U)(functor: F[T]): F[U]
}
This is most useful in type classes like Functor
and Monad
.
Java lacks this feature.
A type is a subtype of a structural type if it contains all members of the structural type.
type S = { def x: String; def y: Int }
class A { def x = "a"; def y = 1 }
class B { def y = 1 }
Here, A
is a subtype of S
because it defines both def x: String
and def y: Int
which are required by S
. B
is not a subtype of S
because B
does not define def y: Int
. Note that in many cases, reflection is used when accessing members of values of which the static type is a structural type, and therefore structural typing is often discouraged.
They can be used to simulate type lambdas, though, without runtime cost.
Java lacks this feature, and it's arguably not very useful anyway.
Like methods, types can be abstract:
trait T {
type G
def f: G
}
class C extends T {
override type G = Int
override def f = 42
}
Java lacks this feature.
The singleton type of x
contains x
and only x
. This can be useful in particular when one wants to guarantee that a method returns this
:
trait T {
def x: this.type
}
class C extends T {
def x = this
}
val x: T = new C
x.x // has type x.type (which is a subtype of C), not type T
In Java this isn't possible; you either have to parameterise T
and rely on CRTP, or have x
return T
instead of C
.
Scala has the bottom type Nothing
that is a subtype of all types and contains no values. This is the type of throw
expressions and the return type of functions that never return (i.e. always throw an exception or enter an infinite loop).
Java lacks this feature and uses void
instead. Scala has no void
but makes the distinction between functions that return no value and non-returning functions explicit.