Click here to Skip to main content
15,881,204 members
Articles / Programming Languages / Scala

Scala Generics

Rate me:
Please Sign up or sign in to vote.
5.00/5 (8 votes)
5 Nov 2015CPOL3 min read 14.1K   5  
So continuing on from the Scala for .NET series of posts. This time we will look at using Generics in Scala.

So continuing on from the Scala for .NET series of posts. This time we will look at using Generics in Scala.

The basic usage for generics is not that far removed from usage in .NET, where in Scala you may have generic methods/classes.

Generic Methods

Here is a simple example of a generic method

Scala
object ClassesDemo {
 
  def genericPrinter[A](theStuff : A) : Unit = {
    System.out.println(s"theStuff =$theStuff")
  }
 
  def main(args: Array[String]) =
  {
    genericPrinter("A String")
    genericPrinter(12)
    genericPrinter(Some(12L))
    System.in.read()
    ()
  }
 
}

Which when run will give the following results:

image

Generic Classes

It is also possible to create generic classes. Here is an example of creating a generic class, and its usage

Scala
class printer[A](theStuff : A) {
  def printIt() : Unit = {
    System.out.println(s"theStuff =$theStuff")
  }
}
 
 
object ClassesDemo {
 
  def main(args: Array[String]) =
  {
    new printer[String]("A String").printIt()
    new printer[Int](12).printIt()
    new printer[Tuple2[String,Int]]("A String",12).printIt()
    System.in.read()
    ()
  }
}

Which when run will give the following results:

image

View Bounds

In .NET we have generic constraints that we can apply such as this

Scala
public class MyGenericClass<t> where T : IComparable
{
 
}

In Scala this is accomplished by using “View Bounds”

A view bound was a mechanism introduced in Scala to enable the use of some type A as if it were some type B.

The typical syntax is this:

def f[A <% B](a: A) = a.bMethod 

In other words, A should have an implicit conversion to B available, so that one can call B methods on an object of type A. The most common usage of view bounds in the standard library (before Scala 2.8.0, anyway), is with Ordered, like this:

def f[A <% Ordered[A]](a: A, b: A) = if (a < b) a else b

Because one can convert A into an Ordered[A], and because Ordered[A] defines the method <(other: A): Boolean, I can use the expression a < b.

Taken from http://docs.scala-lang.org/tutorials/FAQ/context-and-view-bounds.html up on date 05/11/15

Context Bounds

Context bounds were introduced in Scala 2.8.0, and are typically used with the so-called type class pattern, a pattern of code that emulates the functionality provided by Haskell type classes, though in a more verbose manner.

While a view bound can be used with simple types (for example, A <% String), a context bound requires a parameterized type, such as Ordered[A] above, but unlike String.

A context bound describes an implicit value, instead of view bound’s implicit conversion. It is used to declare that for some type A, there is an implicit value of type B[A] available. The syntax goes like this:

def f[A : B](a: A) = g(a) // where g requires an implicit value of type B[A] 

This is more confusing than the view bound because it is not immediately clear how to use it. The common example of usage in Scala is this:

def f[A : ClassManifest](n: Int) = new Array[A](n)

An Array initialization on a parameterized type requires a ClassManifest to be available, for arcane reasons related to type erasure and the non-erasure nature of arrays.

Another very common example in the library is a bit more complex:

def f[A : Ordering](a: A, b: A) = implicitly[Ordering[A]].compare(a, b) 

Here, implicitly is used to retrive the implicit value we want, one of type Ordering[A], which class defines the method compare(a: A, b: A): Int.

Taken from http://docs.scala-lang.org/tutorials/FAQ/context-and-view-bounds.html up on date 05/11/15

Type Erasure

Unlike .NET generics are not baked into the JVM, as they are in .NET where they are actually part of the CLR.

Scala’s types are erased at compile time. This means that if you were to inspect the runtime type of some instance, you might not have access to all type information that the Scala compiler has available at compile time.

Like scala.reflect.Manifest, TypeTags can be thought of as objects which carry along all type information available at compile time, to runtime. For example, TypeTag[T] encapsulates the runtime type representation of some compile-time type T. Note however, that TypeTags should be considered to be a richer replacement of the pre-2.10 notion of a Manifest, that are additionally fully integrated with Scala reflection.

ClassTag / TypeTag / Manifest

These 3 classes are the most useful ones to use to maintain type information.

Let’s consider this bit of code:

Scala
import MyExtensions._
import scala.reflect.runtime.universe._
import scala.reflect._
 
object ClassesDemo {
 
 
  def genericMeth[A](xs: List[A]) = xs match {
    case _: List[String] => "list of strings"
    case _: List[Foo] => "list of foos"
  }
 
  def main(args: Array[String]) =
  {
    val x = System.out.print(genericMeth(List("string")))
 
    System.in.read()
    ()
  }
}

Which when compiled will give the following errors:

image

To solve this problem Manifests were introduced to Scala. But they have the problem not being able to represent a lot of useful types.

TypeTag(s)/ClassTag(s) are the preferred mechanism to use. Here is the above code written again use a TypeTag, this time the code compiles fine

Scala
import MyExtensions._
import scala.reflect.runtime.universe._
import scala.reflect._
 
object ClassesDemo {
 
  def genericMeth[A : TypeTag](xs: List[A]) = typeOf[A] match {
    case t if t =:= typeOf[String] => "list of strings"
    case t if t <:< typeOf[Foo] => "list of foos"
  }
 
  def main(args: Array[String]) =
  {
    val x =
    System.out.print(genericMeth(List("string")))
 
 
    System.in.read()
    ()
  }
}

Another thing I have personally found of use is to use TypeTag/ClassTag to help me create an instance of the correct type.

For example:

Scala
import scala.reflect.runtime.universe._
import scala.reflect._
 
trait Logger {
  def log() : Unit
}
 
class LoggerA() extends Logger {
  override def log(): Unit = {
    println("LoggerA.log() called")
  }
}
 
object ClassesDemo {
 
  def doTheLoggingClassTag[T <: Logger]()(implicit tag: scala.reflect.ClassTag[T]) = {
 
      val theObject = tag.runtimeClass.newInstance.asInstanceOf[T]
      theObject.log()
      println(s"theObject $theObject")
      ()
    }
 
  def main(args: Array[String]) =
  {
    doTheLoggingClassTag[LoggerA]()
 
    System.in.read()
    ()
  }
}

Which will give this output:

image

There are some great sources of information on this, here are a couple of links:

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)

- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence

Both of these at Sussex University UK.

Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2016
  • Codeproject MVP 2016
  • Microsoft C# MVP 2015
  • Codeproject MVP 2015
  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions

 
-- There are no messages in this forum --