Sunday, 31 July 2016

Scala String and it's implicit counterpart StringOps


Introduction

In this post, I'm going to discuss about the relationship between Java's String and Scala's StringOps in detail with some suitable examples.

I'll try to answer the following questions:

  • What is the relationship between Java's String and Scala's StringOps?
  • How Scala uses Java's String class?
  • How Scala extends Java's String final class?
  • How Scala converts from Java's String to Scala's StringOps and vice-versa?
Let us start exploring these concepts one by one now.


Java's String in Scala:

Scala does not have a separate String class. It uses Java's String class to support Immutable and sequence of characters. 

Example:-

scala> val name="Rams"
name: String = Rams

When we execute above expression in Scala REPL, we can see it's type is String inferred by Scala Compiler automatically.

Scala has defined a type for Java's String class in Predef object as shown below:



Example:-

object Predef {
// Other code 
type String = java.lang.String
}

In Scala, this Predef is an Object defined in "scala" package as "scala.Predef".

So when Scala refers "String" type that means "java.lang.String" only.



How Scala extends Java's final String class:

As we know, Java's String class is final that means we cannot extend or inherit this class. Then if we want to add more functions or utilities or methods to String class, what is the solution? 

The solution is using Scala's Implicits.


Yes, Scala has defined some Implicits to extend or add missing functionalities to Java's String class.



Java's String with Scala's StringOps:

When we use some methods like map (which is not available in Java's String class), Scala Compiler will search for available Implicits in Predef object.

Example:-

scala> val name="Rams"name: String = Rams
scala> name.map(s => s.toInt)res0: scala.collection.immutable.IndexedSeq[Int] = Vector(82, 97, 109, 115)

scala> name.reverseres1: String = smaR

Here we can use map method to perform some operations which are not available in Java's String class.

How is it possible? How Scala's Predef has defined Implicits to do these operations?


Scala has defined two Implicits in Predef object as shown below:



object Predef {
   // Other code   
   @inline implicit def augmentString(x: String): StringOps = new StringOps(x)       
   @inline implicit def unaugmentString(x: StringOps): String = x.repr

}

Here, we need to understand 

  • augmentString function is used to convert Java's String class to Scala's StringOps class.
  • unaugmentString function is used  to convert Scala's StringOps class to Java's String class.


When we use some methods like "map", "reverse" etc which are not available in Java's String class, Scala Compiler uses this Implicit function to covert Java's String to Scala's StringOps class.

So Scala has defined all required or missing methods in StringOps class and using them by defining Implicits.


Scala Compiler uses the following StringOps constructor to create StringOps instance using String object.


NOTE:- Scala has defined some classes like WrappedString, StringLike etc to extend Java's String class functionality.

Please refer Scala API for more information about these classes and also go through Predef source code to understand other Implicits.


NOTE:- If you are new to Implicits, please refer my Implicits tutorial. 

final class StringOps(repr:String) {
  val repr: String 

}

Thank you for reading my tutorials.

Please drop me a comment if you like my tutorial or have any issues/suggestions.