Learning Scala Programming
上QQ阅读APP看书,第一时间看更新

Wrapper classes

In Scala, we can create our own universe, apart from the native methods provided, we can add our own implementations, which we call Rich Wrapper classes. This is possible because of Implicit Conversions. First, we'll list out some Wrappers available already:

Rich wrappers

To see how it happens, let's see an example:

scala> val x = 10
x: Int = 10

scala> x.isValidByte
res1: Boolean = true

The preceding expression tries to check if the value of x can be converted into a Byte, and suffices range of a Byte, and finds it to be true:

scala> val x = 260
x: Int = 260

scala> x.isValidByte
res2: Boolean = false

scala> val x = 127
x: Int = 127

scala> x.isValidByte
res3: Boolean = true

As you know, range for a Byte is -128 to 127. If you try to assign it to a value that's out of range of a Byte and expect it to behave like a Byte, it won't work. Thus, the result for the preceding expression is false.

Apart from this isValidByte, there are a number of utility methods present in the class RichByte wrapper class.

These wrappers methods look like they're natively defined for the types existing. One of the examples is a wrapper around a String that is StringOps. A String in Scala is nothing more than an instance of java.lang.String, so it's clear that all methods implemented for java.lang.String are applicable here as well. For example, the charAt method does pretty good here:

scala> val x = "I am a String"
x: String = I am a String
scala> x.charAt(5)
res13: Char = a

Now let's try some methods from StringOps:

scala> x.capitalize
res14: String = I am a String

scala> x.toUpperCase
res15: String = I AM A STRING

scala> x.toLowerCase
res16: String = i am a string

The three methods capitalize, toUpperCase, and toLowerCase are defined in the StringOps class and not in String classes, but still it works the same way as calling a native method for a String type. There are more of these methods that work as a utility method for Strings. This is because of the power of Implicit Conversions. We'll learn how Implicits work in Scala in later chapters.

One of the ways to create a Range class out of Int types can be achieved using a method to. We call these rich methods. It's really simple to use them, and based on the purpose they solve, we can also name them:

scala> val rangeOfNumbers = 1 to 199
rangeOfNumbers: scala.collection.immutable.Range.Inclusive = Range 1 to 199

scala> val rangeOfNumbersUntil = 1 until 199
rangeOfNumbersUntil: scala.collection.immutable.Range = Range 1 until 199

scala> rangeOfNumbers contains 1
res17: Boolean = true

scala> rangeOfNumbersUntil contains 1
res18: Boolean = true

scala> rangeOfNumbersUntil contains 199
res19: Boolean = false

scala> rangeOfNumbers contains 199
res20: Boolean = true

The preceding are few examples of methods from the Range class, which provide rich methods for Int to create a Range with. The Range can contain values inclusive of those. It's built with, and can also exclude, those values. Methods for building these are to and until. The first includes both values we use to build a Range; the latter includes only the beginning value. We've tried all these. As you can see, rangeOfNumbersUntil does not contain 199. We can also create a Range with some step difference:

scala> 1 to 10 by 2 foreach println

The following is the result:

1
3
5
7
9

This is pretty simple; pretty and simple. Especially with the syntax, we are able to write concisely because of Implicit Conversions and Type Inference happening at the backend. Scala Compiler is taking care of all those parts, leaving us with the simple job of writing code in a beautiful way. Another way of utilizing conciseness while writing a String is by using String Interpolators.