DEV Community

BettyES
BettyES

Posted on

First steps in scala - part 2: objects

This is a very basic attempt of mine to help fellow scala starters kick off their first projects including objects. This has also been published on my personal webpage. In my previous blog I aimed to get everyone started using different approaches to "hello world" in an IntelliJ IDE. This included, simple print statements, functions and variables. In the following post it is all about:

Introducing objects

There are many blogs about object oriented programming and potential advantages or disadvantages. Let me try to summarise in short my take aways from some of these. Contrary to procedural programming (from 1960s: Fortran, C, Pascal) where the focus is set around actions (functions), object oriented programming (from 1995: Java) is all about data stored within objects. The reusability of these objects is what makes object oriented programming (OOP) a great approach for large projects. Personally, I love the clear structure of OOP projects, but please feel free to agree or disagree in the comments section ;-) . Each object is build using a class (a template for objects), and comes with its own variables (data storage), called 'fields' and its very own functions, called 'methods' (the object's behaviour). This terminology makes it also quite easy to distinguish between (i) object linked variables (fields) and functions (methods) and (ii) independent variables and functions.

To put it simply: objects encapsulate data and define functions that can be applied to these data. This is fantastic, cause you will not have to worry about where to apply a function, as they are clearly associated with a specific object/data. Lets look at a very simple example:

My first object: a car

My car object will have the fields: color, make and position and the method: move()
Looking at some simple code to build your first car object below. I named the project "carPark". It contains a main method, the entry point to our program which always starts with main(args:Array[String]). Generally, a standalone application cannot run without a main-method, any logic will start from here. In the main method we will create a car object (based on the template defined in the Car class), named "fiat" and check its fields, color, make and position in a print statement (The print statement has a variable reference embedded (s"$variable"). I mentioned string interpolation already in my last post.). The Car-class defines the features for our car object and enables us to build as many cars as we like, which will all share the fields, color (user defined, immutable), make (user defined, immutable) and position.

object carPark {
  def main(args: Array[String]) {
    val fiat = new Car("red","Fiat");
    println(s"a ${fiat.color} car of make ${fiat.make} 
is now standing at position ${fiat.position.mkString(",")}.")
  }
}

class Car(col: String, mk: String) {
    val color: String = col
    val make: String = mk
    var position = new Array[Int](2);
    position(0) = 0
    position(1) = 0
}
Enter fullscreen mode Exit fullscreen mode

Adding a function move() will actually enable us to access the mutable data stored in car.position within the car class and transform these. If you imagined a grid(x,y) we could now move our object car within that grid starting from position(0,0) to position(2,2).

object carPark {
  def main(args: Array[String]) {
    val fiat = new car("red","Fiat")

    // Move to a new location
    fiat.move(2, 2)
    println(s"a ${fiat.color} car of make ${fiat.make} 
is now standing at position ${fiat.position.mkString(",")}.")
  }
}

  class Car(col: String, mk: String) {
    val color: String = col
    val make: String = mk
    val position = new Array[Int](2)
    position(0) = 0
    position(1) = 1

    def move(dx: Int, dy: Int) {
      position(0) = position(0) + dx
      position(1) = position(0) + dy
    }
  }
Enter fullscreen mode Exit fullscreen mode

***Optional exercise***

In case you would like to experiment building your own objects. You could try to add a second car, stepwise adding complexity:
(1) The car object has the same variables and methods as the first
(2) The cars should not be able to move to the same position
If this happens shift the car one on the y position next to the previous car and send a message that the chosen spot was taken and it has been parked to the right
(3) Imagine you only had 5*5 positions available could use the following positions x in (0,4) and y in (0,4). Make sure the second car does not fall out of the grid.

Of course this is only a suggestion on how to get started. There are numerous ways to add more complexity to this example.
Add visual output


For simple visual solution on the position of your car, you could add a Grid class to display its position (I marked the position of the car with "X"):

// main class with instructions what to do:
// instantiate car, names fiat and move it one position 
// towards x and y
// create a grid and display

object carPark {
  def main(args: Array[String]) {
    val fiat = new Car("red","Fiat")

    // Move to a new location
    fiat.move(2, 2);
// at the moment show method works best for to 5*5
    val streetMap = new Grid(5,5) 
    streetMap.updatePosition(fiat.position(0),fiat.position(1))

    streetMap.show()
  }
}

// build a grid class to visually display position of the car
// field of this class: row, col, streetMap
// methods: updatePosition, show

import Array._
class Grid(r:Int, c: Int) {
  val row: Int = r
  val col: Int = c
  var streetMap = ofDim[String](row, col)
  for (i <- 0 to row-1) {
    for (j <- 0 to col-1) {
      streetMap(i)(j) = " "
    }
  }

  def updatePosition(posX: Int, posY: Int): Unit = {
    streetMap(posX)(posY) = "X"
  }

  def show(): Unit = {
    println("=========================")
    for (i <- 0 to row-1) {
      print("|")
      for (j <- 0 to col-1) {
        print(" " + streetMap(i)(j)+" | ")
      }
      println()
      println("=========================")
    }
  }
}

// car class as shown earlier 
class Car(col: String, mk: String) {      
    val color: String = col     
    val make: String = mk         
    val position = new Array[Int](2);  
    position(0) = 0
    position(1) = 0   

    def move(dx: Int, dy: Int) {       
    position(0) = position(0) + dx
    position(1) = position(1) + dy 
    }   
}
Enter fullscreen mode Exit fullscreen mode

In the Grid class I used a multidimensional Array. A multidimensional array stores data in a matrix. It might be worth having a look at arrays first. In the above example car.position is an array. Arrays in scala are a collection of data, just like arrays in java. Examples of different arrays: (I) var numbers: Array[Int] = Array(1,2,3,4) (II) var strings: Array[String] = Array("hello","goodbye", "servus")

Running the whole program you should see something like this:

output1

Make it user friendly - add Standard input from commandline

Finally, if you would like to make this a bit more interactive, we can let the user input his favoured coordinates, by adding a method to the car class that allows a user input. Instead of fiat.move(1, 1) in the main methods call fiat.readCoordinates(); fiat.move(fiat.position(0),fiat.position(1))

def readCoordinates(carName: String){
  println(s"where do you want to move your $carName?\n Please enter X     position:")
  val dx  = scala.io.StdIn.readInt()
  println("Please enter Y position")
  val dy = scala.io.StdIn.readInt()
  coord = Array(dx,dy)
}
Enter fullscreen mode Exit fullscreen mode

The scala.io.readInt() Standard Inputhttps://www.scala-lang.org/api/2.12.2/scala/io/StdIn$.html command enables user input of integers. To read in a String use scala.io.readLine().

Now if you'd like to run your program from commandline, just go to yourProject/target/scala*/classes/ and type scala yourProject and you should be asked to enter x and y coordinates. Please remember that currently the Grid is set to 5*5 fields, so your coordinates should be between [0,4] .

commandline

You can get access to the full project, including a solution for a second car on github.

Summary on object oriented programming

Now that we have seen how data and associated methods can be stored in objects , the concept of encapsulation shoud be clearer. Generally, object opriented programming follows four major principles including encapsulation, abstraction, inheritence and polymorphism. I will keep a description of these concepts short. Looking at the above example: (i) we have encapsulated data and methods associated with the class car, such as color, make, position and move. (ii) Abstraction means that implementation details are hidden. The method move, for example, takes two numbers and returns a position. We do not see how the actual action (although very simple in this case) takes place. (iii) we have not used the concepts of inheritance and polymorphism in the carPark project. Inheritance in short, if we had a class Vehicles with fields such as color, wheels and method move, we might subset it into classes Cars, Bicycles, and Tricycle. Cars, for example, would inherit the fields color, wheels and move, but might add fields like fuel, motor and methods, like getFuel. If you are keen to know more about these concepts, have a look at this nice blog.

A final remark: Even though I have set focus of this blog on objects, scala is also functional! ...But this will be topic of another blog.

Stay tuned for more scala starter blogs, covering testing, data structures etc.

Top comments (0)