# The POJO (Plain Old Java Object), common methods and local methods :::info This is a part of preparation for Paper 2 in IB Computer Science diploma. The lessons are 1) [Start point with Java](/S3oXtcWLQduOy_R5_03Z2g) 2) [The concept of Object in OOP](/6FsqJbw3SKq5m1NKk56U3Q) 3) [The POJO (Plain Old Java Object) and their common methods](/zJgmWQUbSLqf3JIiVehBNA) 4) [Relationship between objects and UML diagrams](/5rUt1ACuTQGF7nuLhvx7TA) ::: ## The POJO The POJO is a way of how a typical Java Object should look like. As we have discussed in the previous note, objects have attributes (variables) and methods (functions). There are some conventions in how a generic class should look like. The main features are: - Private attributes - Constructor (one or several) - Accessors and mutators to access the attributes And more optional - ToString() method - Equals() method ### The attributes of the Student Let's have 2 different scenarios for students. One would be Duolingo and the other a Class manager (similar to clickedu or iSams) In duolingo: What would be the attributes that we need from the student? * Languages courses -> object * lifes -> int * days of streak -> int * on Streak -> boolean * id -> usually an object but can be a long. There could be others. In the class manager: What would be the attributes that we need from the student? * grades -> object * name -> String * nationality -> String * attendance Record > object * subjects enrolled > object * merits/behaviours record > object * recentArrival -> boolean * date of Birth -> object :::info Dates in programming are _complex_. ::: ### Let's create one of these classes in java We're going to start with the duolingo version without objects, so we have this: :::warning :warning: In this example we're not using yet encapsulation! :warning: ::: ```java= public class Student { long id; int streakDays; //remember, camelCase boolean onStreak; int lifes; String personalMotto; public String sayHi(){ return "Hi!"; } } ``` Now, if we want to add information or retrieve it we need to use the dot operator after the name of the variable like this: ```java= import java.util.*; public class Main { public static void main(String[] args) { Student student = new Student(); System.out.println(student.sayHi()); student.id = 100000; student.streakDays = 50; student.onStreak = true; student.lifes= 5; student.personalMotto = "Artem is great"; System.out.println(student.lifes); student.lifes--; //same as writing students.lifes = students.lifes -1 System.out.println(student.lifes); } } ``` The output of this would be: "Hi 5 4" ### Creating a custom constructor In the previous example we had to write a lot of lines adding all the details of this student. So we're going to create a constructor with the parameters that we need already. This would be the Student class with the constructor: ```java= public class Student { long id; int streakDays; //remember, camelCase boolean onStreak; int lifes; String personalMotto; //Constructor public Student(long id, int streakDays, boolean onStreak, int lifes, String personalMotto ) //remember that in Java we can have line breaks //if we want and it's considered one! { this.id = id; this.streakDays = streakDays; this.onStreak = onStreak; this.lifes = lifes; this.personalMotto = personalMotto; } public String sayHi(){ return "Hi!"; } } ``` And this is the variation on the driver (main) class: ```java= import java.util.*; public class Main { public static void main(String[] args) { Student student = new Student( 100000, 50, true, 5, "Artem is awesome"); System.out.println(student.lifes); student.lifes--; //same as writing students.lifes = students.lifes -1 System.out.println(student.lifes); } } ``` So we can see one of the **main functions of a constructor method** that is **initialize the values of the attributes**. There are other possible functions of a constructor, such as validate those attributes. For example in the case of duolingo we can validate the if we have been sent some attributes and if they are invalid (such as negative number of lifes) then throw an error or just write a valid number. All the constructors have the same name of the class with first in capital, so if you're in the class Room the name of the constructor would be Room. Constructors are usually **public**. There are some reasons to write them private but they are out of the scope of this course. More on that here(https://www.baeldung.com/java-private-constructors) They don't have a return type. Their return is not void, is the instance that we're creating. We can have several constructors in the same class. The difference between them would be how many parameters do they have but they have the same name. This is _method overload_ or one example of _polymorphysm_ For example let's supose that we don't want to ask for _all_ the data each time but we only need the id and then we set some default values. Then we would have a class like this: (line 23 onwards) ```java= public class Student { long id; int streakDays; //remember, camelCase boolean onStreak; int lifes; String personalMotto; //Constructor public Student(long id, int streakDays, boolean onStreak, int lifes, String personalMotto ) //remember that in Java we can have line breaks //if we want and it's considered one! { this.id = id; this.streakDays = streakDays; this.onStreak = onStreak; this.lifes = lifes; this.personalMotto = personalMotto; } //Second constructor public Student(long id){ this.id = id; this.streakDays = 0; this.onStreak = false; this.lifes = 5; this.personalMotto = "Here to learn"; } public String sayHi(){ return "Hi!"; } } ``` #### Exercise Create another constructor that admit as an input the id and the personalMotto and the rest of the values would be initialized as the default values that you saw in the previous example. Solution in the spoiler: :::spoiler You have to write here only the code that would be added to the **Student** class ```java= public Student(long id, String personalMotto){ this.id = id; this.streak = 0; this.onStreak = false; this.lifes = 5; this.personalMotto = personalMotto; } ``` Credit A.D. ::: ### Accessors and mutators The next type of methods that we're going to talk about are accessors and mutators (also known as getters and setters). They are related to the concept of **encapsulation**. #### Encapsulation //TO DO BY THE STUDENTS :::info This is an important concept that they ask in the exam! ::: ### Student (of duolingo) with private attributes ```java= public class Student { private long id; private int streakDays; //remember, camelCase private boolean onStreak; private int lifes; private String personalMotto; //Constructor public Student(long id, int streakDays, boolean onStreak, int lifes, String personalMotto ) //remember that in Java we can have line breaks //if we want and it's considered one! { this.id = id; this.streakDays = streakDays; this.onStreak = onStreak; this.lifes = lifes; this.personalMotto = personalMotto; } //Second constructor public Student(long id){ this.id = id; this.streakDays = 0; this.onStreak = false; this.lifes = 5; this.personalMotto = "Here to learn"; } public String sayHi(){ return "Hi!"; } } ``` So since we want encapsulation to have control over the variables (or methods) using the private modifier, we still need to access (Even with some regulation) to some of those elements. For that we're going to use Accessors (to access data) and mutators (to modify these attributes) #### Accessors (getters) Public methods that allow other classes to acces variables. Their names are usually "get[nameOfTheVariable]", they return the same type of variable and they don't have any parameters. They can be basic or they can add any other validation or debugging system. The basic goes like this ```java= public long getId() { //it's a long return because id is a long return this.id; } ``` :::info With booleans we use the verb to be or to have instead of get ```java= public boolean isOnStreak() { return this.onStreak; } ``` ::: #### Mutators (setters) Public methods that allow other classes to modify variables. Their names are usually "set[nameOfTheVariable]", they return nothing (so the keyword is `void`) and they have one parameter that is the new value of the variable. This means that we need to write the type of the variable also in the signature of the method. For example ```java= public void setId(long id) { //it's a long return because id is a long this.id = id; } ``` In IB they don't usually ask for validation, but this is a good point to have some validation of values. For example: ```java= public void setStreakDays (int StreakDays) { if (StreakDays >= 0) { this.StreakDays = StreakDays; } else { //do something System.out.println("invalid Streak Days value"); } } ``` Also another way of having more control is by not implementing all the acccesors or mutators. #### Exercise Implement all the accessors and mutators of Student Solution here on the spoiler :::spoiler ```java= public class Student { private long id; private int streakDays; //remember, camelCase private boolean onStreak; private int lifes; private String personalMotto; //Constructor public Student(long id, int streakDays, boolean onStreak, int lifes, String personalMotto ) //remember that in Java we can have line breaks //if we want and it's considered one! { this.id = id; this.streakDays = streakDays; this.onStreak = onStreak; this.lifes = lifes; this.personalMotto = personalMotto; } //Second constructor public Student(long id){ this.id = id; this.streakDays = 0; this.onStreak = false; this.lifes = 5; this.personalMotto = "Here to learn"; } //accessors public long getId() { return this.id; } public boolean isOnStreak() { //With booleans we use the verb to be or to have instead of get return this.onStreak; } public int getLifes() { return this.lifes; } public String getPersonalMotto() { return this.personalMotto; } //mutators public void setId(long id) { //it's a long return because id is a long this.id = id; } public void setStreakDays(int StreakDays) { this.StreakDays = StreakDays; } public void setOnStreak(boolean onStreak) { this.onStreak = onStreak; } public void setLifes(int lifes) { this.lifes = lifes; } public void setPersonalMotto(String personalMotto) { this.personalMotto = personalMotto; } public String sayHi(){ return "Hi!"; } } ``` ::: ### Local methods in a class We have already covered the POJO and the main methods (accessors, mutators, constructor) but so far it seems more than we have a way to encapsulate variables rather than a true object that DOES stuff. So let's fix this. Let's create methods that do things. #### 2Dpoint example Let's have a class called "2Dpoint" with just 2 attributes (x and y) and for simplicity let's have them as int. With this you **should be able** to construct the whole class if I say that I want all the accessors, mutators, and the constructor that initializes all variables. Create it and compare it to this: :::spoiler ```java= public class Point2D { private int x; private int y; public Point2D(int x, int y) { this.x = x; this.y = y; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } } ``` ::: Now let's create some methods to move the dot to the left, to the right up or down. Each of them is going to modify the value of x and y but they are not directly mutators. Here you have the example of moveRight(int unitsToTheRight) ```java= public void moveRight(int unitsToTheRight) { this.x = this.x + unitsToTheRight; } ``` :::info :information_source: You can also use `this.x += unitsToTheRight` ::: Implement the other 3 methods. If you're correct with a test in the Main class in the public static void main method: ```java public static void main(String[] args) { Point2D testPoint = new Point2D(3, 1); testPoint.moveLeft(3); testPoint.moveUp(2); System.out.println(testPoint.getX()); System.out.println(testPoint.getY()); testPoint.moveDown(4); testPoint.moveRight(5); System.out.println(testPoint.getX()); System.out.println(testPoint.getY()); } ``` You should get 0, 3, 5 and -1 if I'm not mistaken. #### Example with boundaries Sometimes we want for any reason some boundaries, usually because the thing that we're modeling has those boundaries. For example if we're modeling IB subject grades even if we have an int we need to make sure that they are not over 7 nor less than 1. Let's add those boundaries to our 2DPoint class. We're only going to admit numbers from 0 to 100. First we need to stablish that boundary in the setter, the mutator. They are not going to ask about this level of _finesse_ in the exam, but I think is a good example to explain the usefulness of encapsulation. Let's go to the setY(): ```java= public void setY(int y) { if (y >= 0 && y <= 100){ //&& is the AND operator in JAVA so here we're writing that y has to be bigger or equal than 0 AND lower and equal than 100 this.y = y; } else { System.err.println("y out of bounds, not changed"); //if you try this the colour of the output should be different } } ``` Also we need to implement a change in the Constructor ```java= public Point2D(int x, int y) { if(y >= 0 && y <= 100){ this.y = y; } else { System.err.println("y out of bounds, setting default"); this.y = 0; } if(x >= 0 && x <= 100){ this.x = x; } else { System.err.println("x out of bounds, setting default"); this.x = 0; } } ``` :::success As you have seen here the consecuences of going out of bounds is different in the constructor and in the setter since we actually need a default value. ::: And also we need to set some boundaries in the movements. Here you have one of them: ```java= public void moveRight(int unitsToTheRight) { this.x = this.x + unitsToTheRight; if (this.x > 100) { this.x = 100; } if (this.x < 0) { this.x = 0; } } ``` Implement the other 3 methods and the modified setter. You can use this test in the main class to check if everything is as expected. ```java public static void main(String[] args) { Point2D testPoint = new Point2D(-5,200); System.out.println(testPoint.getX()); System.out.println(testPoint.getY()); testPoint.moveLeft(3); testPoint.moveUp(150); System.out.println(testPoint.getX()); System.out.println(testPoint.getY()); testPoint.moveDown(200); testPoint.moveRight(150); System.out.println(testPoint.getX()); System.out.println(testPoint.getY()); } ``` You should get 2 messages of error, two zeroes, another 2 messages of error, 0 and 100, then two other messages of error and 100 and 0. :::info This use of tests is similar to test plans more detailed that are used in an Internal Assessment. Things like "what it's suposed to happen if I move this too much to the left or to the right" should be considered. ::: #### Account example I'm using this as example in this case "1.6 Ex: The Account Class" https://www3.ntu.edu.sg/home/ehchua/programming/java/J3f_OOPExercises.html#zz-1.3 A class called Account, which models a bank account of a customer, is designed as shown in the following class diagram. The methods credit(amount) and debit(amount) add or subtract the given amount to the balance. The method transferTo(anotherAccount, amount) transfers the given amount from this Account to the given anotherAccount. Write the Account class. ![ClassAccountJava](https://hackmd.io/_uploads/HkcLnQRJel.png) In this case you see that we have 2 constructorse, we have the accessors but we don't have the mutators. Credit and debit should be as simple as moving the 2Dpoint. The toString method is a method that returns a String, here you have it: ```java= public String toString() { return "id="+this.id+",name="+this.name+",balance="+this.balance; } ``` :::info There are fancier ways to implement a toString method, if you have time find them (using stringBuffer for example) but this should work. ::: The transfer to is going to be the most complicated one. Here you have the test in the public static void Main: ```java= public static void main(String[] args) { // Test constructor and toString() Account a1 = new Account("A101", "Tan Ah Teck", 88); System.out.println(a1); // toString(); Account a2 = new Account("A102", "Kumar"); // default balance System.out.println(a2); // Test Getters System.out.println("ID: " + a1.getID()); System.out.println("Name: " + a1.getName()); System.out.println("Balance: " + a1.getBalance()); // Test credit() and debit() a1.credit(100); System.out.println(a1); a1.debit(50); System.out.println(a1); a1.debit(500); // debit() error System.out.println(a1); // Test transfer() a1.transferTo(a2, 100); // toString() System.out.println(a1); System.out.println(a2); } } ``` The expected output is: ``` Account[id=A101,name=Tan Ah Teck,balance=88] Account[id=A102,name=Kumar,balance=0] ID: A101 Name: Tan Ah Teck Balance: 88 Account[id=A101,name=Tan Ah Teck,balance=188] Account[id=A101,name=Tan Ah Teck,balance=138] Amount exceeded balance Account[id=A101,name=Tan Ah Teck,balance=138] Account[id=A101,name=Tan Ah Teck,balance=38] Account[id=A102,name=Kumar,balance=100] ```