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.
Section 2.2 First look at classes
Subsection 2.2.1 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.2 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 start 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.
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.1.
Checkpoint 2.2.2.
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.