Generics
Invariance In Java
FILE : JavaInvariance.java
First let's look at one example written in java to have a base for further experiments. In Java collections are invariant what means that List is not a supertype of List
List<String> strings= new LinkedList<>();
strings.add("aaa");
strings.add("bbb");
List<Object> object=new LinkedList<>();
object.add("aaa");
object.add(2);
// ILLEGAL IN JAVA
// object=strings;
However Java has a mechanism called use site variance which allows programmer to use wildcards in assignment and declare different relation between types.
List<? extends Object> parent=strings;
This mechanism however moves responsibility for defining correct relationship to developer in each assignment thus gives more chances to introduce a bug. Scala uses declaration site variance which solves this problem.
Arrays - bug by design
In Java you can actually assign String[] to Object[] which allow you to store integer in an array of strings. This is by design and results in ArrayStroeException in runtime. (Why this is by design -> search on the internet)
String[] stringsArray={"aaa","bbb"};
Object[] objectsArray=stringsArray;
objectsArray[1]=55;
Generics In Scala
FILE : GenericsDemo.scala
First difference form Java - generics in scala are declared in square brackets.
var map:Map[String,Int]=Map()
def genericToString[T](arg : T) : String = arg.toString
You can add generic directly to a class definition and also another one to method definition so it will be resolved each time a method is called
class Wrapper[A](element:A){
def modify[B](f:A=>B):Wrapper[B] = new Wrapper[B](f(element))
}
Invariance and covariance
Scala has two sets of collections : scala.collections.mutable and scala.collections.immutable. By default you are using immutable collections which are covariant - this means that List[Any] is a supertype of List[String]
val s:List[String] = List("aaa","bbb")
val i:List[Int]=List(1,2)
val o1:List[AnyRef] = s
val o2:List[Any] = i
This is legal because it is impossible to add an element to immutable list so you can not add integer to a collection of strings.
If you want to declare covariant structure you need to add + before generic declaration
class CovariantWrapper[+A](element:A){
def modify[B](f:A=>B):Wrapper[B] = new Wrapper[B](f(element))
}
Nothing
Nothing type becomes really useful when working with covariant types. Becuase Nothing is a subtype of every other type then we can always do a substitution
val v:Type[Whatever] = Type[Nothing]()
so for example when working with either and defining one side we can easily set other side as Nothing
def createRight:Either[Nothing,Int]=Right(500)
Arrays
Arrays are invariant so no "java bugs by design".
val arrayString=Array("aaa","bbb")
// val arrayAny:Array[Any]=arrayString //error
Exercises
FILE TEST : jug.lodz.workshops.starter.generics.exercises. GenericsExercisesSpec
- Create typed Pair[A,B]
- Method which displays patch of any class
- Create Invariant option with proper implementation of getOrElse in None and Some
- Implement Covariant Option - in this exercise uncomment only part of code. Leave getOrElse for exercise 5.