-
These courses in the schedule would be another kind of object. What information do you think will be in them? What functions would they support?
-
The schedule is also an object, containing a list of courses in it. what functions might it support?
Section 2.2 First look at classes
Subsection 2.2.1 Classes and Objects
In many languages, including Java, there are two concepts that are closely related: classes and objects. Letβs start with objects. An object represents an entity that contains possibly a lot of information but can be treated as a single value and passed around to different parts of the code. In an object-oriented approach to development we build programs by creating objects that represent the behaviors we want, then having those objects "talk" to each other. We will explore these ideas in further sections, so for now just keep in mind the idea of an object as an entity that has information inside it and offers us functions/methods to interact with that information.
As an example, imagine an object that represents a particular student, Joe. This object might have in it information like Joeβs first and last name, possibly a middle name, their email, a student id, and so on. It may also hold as part of it Joeβs "schedule" of courses, which may itself be another kind of object. This object may have in it a function for how to print its full name, a function to look for schedule conflicts, a function to find available free times during the day, and so on.
Checkpoint 2.2.1.
Surely Joe is not the only student. So we will likely end up having many of these "student" objects. They all share similarities: They wonβt all have the same email of course, but they will all have an email, and all have first and last name entries which will be strings, and so on. So we can think of objects as having some sort of type, and creating such an object includes some very repetitive tasks. One of our goals in programming is to only do things once and then reuse the work, and we want to do the same here. This leads us to the concept of a class: A class is a blueprint for creating objects of a certain type. It specifies:
-
The kind of information that objects of this type contain (i.e. the fields like email, first name etc)
-
The function code for what operations are permitted on such objects (methods)
-
The process for creating such objects (constructor/initializer)
While having these class blueprints is not the only way we could arrange to have object creation, it is one of the most popular approaches and most languages, including Java, have embraced it.
Subsection 2.2.2 Key parts of a class
So here is an example of a very simple "letter grade" class:
public class LetterGrade {
public String letter;
public double points;
public LetterGrade(String letter, double points) {
this.letter = letter;
this.points = points;
}
public boolean countsForCredit() {
return letter.equals("W");
}
}
This is really not a great class, but it will do as a first example. So what all do we have here?
-
A class is declared with the keyword
class
. It is followed by the name for the class, which is customarily in "upper camel case" form. Then the customary curly braces demarcate the contents of the class definition. -
Within the class definitionβs body we encounter three kinds of information: variable declarations, function (method) declarations or definitions, and inner class definitions.
-
Typically the first part of the class definition contains declarations of the variables we call fields. They come in two flavors, non-static and static, depending on whether they are specific to each object of the class or whether they are specific to the class. The ones in our example are non-static, or object-specific. We often call these member variables or instance variables. These are variables that are visible to all methods of the object and last for the lifetime of the object.
-
In most cases what follows is the constructor, on line 5 in our example. The constructor is a special method that is called when a new object is created. Its job is to initialize the object, typically by assigning values to the objectβs fields and doing any other needed prep work. In our example, this constructor is provided with two parameters in the form of a
letter
string and apoints
value. It then assigns these values to the fields of the object, via assignments likethis.letter = letter
. In this assignment, the left-hand-side refers to the fieldletter
declared in line 2, while the right-hand-side refers to the parameterletter
that was passed in to the constructor in line 5. Parameters are short-lived variables that cease to exist the moment the function exits. By storing the values in the fields instead we can ensure that other object methods will have access to them later. -
Lastly, we have methods, an example of which we can see in line 10. It is a public method (we will discuss this later), that returns a
boolean
value, it is calledcountsForCredit
, and takes no parameters. This method calls a method of theletter
String
object, or to be more precise theString
object stored in theletter
field. That method,equals
, checks to see if the two strings are the same string.
Letβs take a brief look at some code that uses this class:
LetterGrade g1 = new LetterGrade("A", 4.00);
LetterGrade g2 = new LetterGrade("W", 0.00);
g1.letter; // This is "A"
g2.letter; // This is "W"
g1.points; // This is 4.00
g1.countsForCredit(); // This is true
g2.countsForCredit(); // This is false
We start by creating two objects of this class, and store them in the variables
g1
and g2
. We called the constructor to accomplish that, using the new
keyword.
Then we access the fields of those objects, via the "dot notation"
object.property
. Get used to this notation, because you will be using it a lot. If you are coming from a C or C++ background, you should think of this more like the arrow notation in those languages.
Notice that each object has its own value for
letter
(and also for points
for that matter). This is the essential feature of the member/instance variables: The values of those fields travel along with the object instance, wherever it goes. When we call an objectβs method, like the countsForCredit
in our example, the body of that method executes and it can reference these field values.
Subsection 2.2.3 Class example: Linked Lists
Letβs take a look at another and slightly more complicated example, that of a "singly" linked list. If you are not familiar with this structure, you should read up on it before continuing here, as my explanation below will probably not do it justice.
A linked list is comprised of nodes. Each node
-
contains a value, but also
-
points to the next node on the list.
That way the nodes are chained to each other, from some initial node towards a final node, which points to nothing; we represent that "nothing" in Java with the keyword
null
.null
can be used in any place where an object is expected. Calling a method on this "null" object will result in aNullPointerException
.
Null values are a bit of a pain, alright a huge pain, and weβll see later ways to avoid them when possible. But for now weβll just learn to live with them.
Whatβs so bad about null values? The problem is that they can appear in place of any object at any moment, unless you take great pains to avoid it. But they donβt respond to any of the things such an object would. So what is a program supposed to do when you ask it to print the first name of a student object, but that student object is actually null? What is the first name of the null object? What is its last name?So we often find ourselves having to constantly check if an object is null before doing something with it. And if we forget to do so then our program might fail in mysterious and unexpected ways.
So here is how our linked list class might look like. Iβm calling it
LList
instead of the more appropriate name of LinkedList
because there is a built-in linked list class in Java, and the names would conflict. Weβll discuss the implementation after the code.
public class LList {
private Node head = null;
public void insertFront(int value) {
head = new Node(value, head);
}
public boolean contains(int value) {
Node curr = head;
while (curr != null) {
if (curr.value == value) return true;
curr = curr.next;
}
return false;
}
private static class Node {
private int value;
private Node next;
Node(int value, Node next) {
this.value = value;
this.next = next;
}
Node(int value) {
this(value, null);
}
}
}
This class starts innocently enough, with line 2 declaring the initial
head
node to be null.
What is
Node
you ask? Well, that is a private inner class, that only LList
knows about, defined starting on line 17. The keyword static
there means that it is the same class for all LList
objects. Note that it has two fields, one to hold an integer value
and one to point to the next
Node in the list. And two constructors, one that takes both a value
and a next
node, and one that takes only a value
. This second constructor is a bit unusual. It uses the funky this(...)
syntax to call the other constructor.
When possible, make sure all your constructors eventually call the same constructor, the one with the most parameters. This way there is a single "point of entry" into the class, and we can do any needed checks there, and only once.
Our
LList
class has only two methods, to keep the exposition simple. the insertFront
method inserts a new value at the front. It has a simple job, it creates a new Node
from the provided value, makes that node point to the old head
, then sets it as the new head
. You should draw a picture to make sure you understand how that worked out.
The other method is perhaps more interesting: It will search through the list for a provided
value
, and if it finds it returns true
, otherwise returns false
. It uses a local variable curr
to point to the currently examined node. Then it simply checks that node and if needed advances to the next, until it reaches null
.
You might like to take some time here and write other methods for the
LList
for practice. For example:-
A method that returns the size of the list.
-
A method that inserts a new value at the end.
-
A method that inserts a new value at index
i
. -
A method that removes the node at index
i
and returns its value.
Checkpoint 2.2.2.
Choose and correctly order some of the following, to produce a function that computes the size of a list. You may not need to use all the provided blocks.
Checkpoint 2.2.3.
Choose and reorder the blocks below, to form a function that inserts a new value at the end of a list. You may not need to use all the provided blocks.