Create a Java Class

This page is a continuation of Before Objects. We will continue to modify the app in Bed.

In the Android Studio project view, the folder /app/java/edu.nyu.scps.bed already holds the Java file MainActivity.java. We will add a new .java file named Date.java to this folder. Select the folder /app/java/edu.nyu.scps.bed in the project view and pull down
File → New → Java Class
Create New Class
Name: Date
Kind: Class
OK
In the Android Studio project view, double-click on your new file Date.java. Edit it to contain the following.

package edu.nyu.scps.bed;

public class Date {
    public static final int JANUARY = 1;
    public static final int FEBRUARY = 2;
    public static final int MARCH = 3;
    public static final int APRIL = 4;
    public static final int MAY = 5;
    public static final int JUNE = 6;
    public static final int JULY = 7;
    public static final int AUGUST = 8;
    public static final int SEPTEMBER = 9;
    public static final int OCTOBER = 10;
    public static final int NOVEMBER = 11;
    public static final int DECEMBER = 12;

    private static final int[] a = {
            0,	//dummy
            31,	//January
            29,	//February: can't distinguish between leap and non-leap
            31,	//March
            30,	//April
            31,	//May
            30,	//June
            31,	//July
            31,	//August
            30,	//September
            31,	//October
            30,	//November
            31	//December
    };

    //The three fields (internal organs) inside each Date object.

    private int year;
    private int month;   //in the range 1 to 12 inclusive
    private int day;     //in the range 1 to number of days in month inclusive

    //This method is the constructor for class Date.
    //It creates a new object of this class.
    //We're not bothering to check for errors.

    public Date(int month, int day, int year) {
        this.year = year;	//this.year is the field, year is the parameter
        this.month = month;
        this.day = day;
    }

    //These three methods are "getters".

    public int getYear() {
        return year;
    }

    public int getMonth() {
        return month;
    }

    public int getDay() {
        return day;
    }

    //Return the length of the month that this Date belongs to.

    public int length() {
        return a[month];
    }

    //Return an int in the range 1 to 366 inclusive
    //telling what day of the year this Date is holding.

    public int julian() {
        int sum = day;
        for (int i = 1; i < month; ++i) {
            sum += a[i];   //means sum = sum + a[i];
        }
        return sum;
    }

    // Move this Date object one day into the future.
    // If it is already at the last possible date, do nothing.

    public void next() {
        if (day < length()) {
            ++day;
            return;
        }

        day = 1;
        if (month < 12) {
            ++month;
            return;
        }

        month = 1;
        if (year < Integer.MAX_VALUE) {
            ++year;
            return;
        }

        //Roll back an unsuccessful attempt at a change.
        //Don't leave this Date object in a half-changed state.
        month = monthsInYear();
        day = length();
    }

    // Move this Date object one day into the past.
    // If it is already at the earliest possible date, do nothing.

    public void prev() {
        if (day > 1) {
            --day;
            return;
        }

        if (month > 1) {
            --month;
        } else if (year > Integer.MIN_VALUE) {
            month = 12;
            --year;
        } else {
            return;
        }

        day = length();
    }

    //Move this Date object count days into the future.
    //The parameter must be non-negative, but we're not bothering to check for errors.

    public void next(int count) {
        for (int i = 0; i < count; ++i) {
            next();
        }
    }

    //Return a String in the format 12/31/2015 showing the content of this Date object.

    @Override
    public String toString() {
        return month + "/" + day + "/" + year;
    }

    //Return the number of months in a year.

    public static int monthsInYear() {
        return a.length - 1;
    }

}

Put the following code between the horizontal lines in the onCreate method of class MainActivity in the file MainActivity.java.

        //-----------------------------
        //Demonstrate the class Date we created in the file Data.java.

        //Can use the static methods and fields of a class
        //even when no objects of the class exist.

        String output = "A year has " + Date.monthsInYear() + " months.\n"
            + "January is month number " + Date.JANUARY + ".\n"
            + "December is month number " + Date.DECEMBER + ".\n";

        Date d = new Date(12, 31, 2015);
        //int y = d.year;   //won't compile, because year is private.
        int j = d.julian();
        output += d + " is day number " + j + " of the year " + d.getYear() + ".\n";
        output += "The day after " + d + " is ";
        d.next();
        output += d + ".\n";
        display("Class Date", output);

        for (;;) {
            Date e = new Date(12, 31, 2014);
            int i = getInt("Forward", "How many days forward from " + e + " would you like to go?");
	    output = "The day that is " + i + " days after " + e + " is ";
            e.next(i);   //Move e i days forward.
            output += e + ".\n";
            e.next();    //Move e one additional day forward.
            output += "The day after that is " + e + ".";

            display("Forward", output);
        }
        //-----------------------------

Run the app and tell it you want to go 60 days forward from 12/31/2014. The output will be wrong because our class Date can’t tell the difference between a leap year and a non-leap year.

Class Date
A year has 12 months.
January is month number 1.
December is month number 12.
12/31/2015 is day number 365 of the year 2015.
The day after 12/31/2015 is 1/1/2016.
OK
Forward
How many days forward from 12/31/2014 would you like to go?
Type answer here and press Done.
60
OK
Forward
The day that is 60 days after 12/31/2014 is 2/29/2015.
The day after that is 3/1/2015.
OK

Classes and objects

An object is a big variable that can contain smaller variables inside of it. The smaller variables are called the fields or members of the object. Think of them as the object’s internal organs.

There are many different classes (types) of objects. In Java, the name of each class begins with an uppercase letter. For example, we’re about to create a class named Date. Because of the package statement at the top of the Date.java file, the class’s full name will be edu.nyu.scps.bed.Date. We create this class for demonstration purposes only. Android already has a class Date (its full name is java.util.Date) which is better than the class Date we’re about to create. You can see now why classes have full names: it allows us to have two classes with the same first name (edu.nyu.scps.bed.Date and java.util.Date).

Each object that belongs to a given class has the same fields inside of it. For example, every object of our class Date will contain three int variables named year, month, and day. Different Date objects can have different values in their fields—October 29, 1929 vs. July 20, 1969—but the three fields must be present in each object.

The class declaration

The code in the file Date.java is called a declaration for class Date. It acts as a blueprint for the objects of class Date we’re about to create. (Let’s call these objects “Date objects”.) The variables that are declared in the outermost {curly braces} of the file—year, month, day—will be present as fields (internal organs) in every Date object.

The declaration for each class is usually written in a file named after the class. For example, ghe declaration for class MainActivity is written in the file MainActivity.java, and the declaration for class Date is written in the file Date.java.

Create an object

The most common way to create a new object is with the Java keyword new. In between the horizontal lines in MainActivity.java, you can see it installing the three numbers 12, 31, and 2015 into the newborn Date object.

It looks like we’re storing the new Date object into the variable d. In reality, the Date object is too big to fit into d. What gets stored into d is only the memory address of the Date object. The memory address is a number that tells where the Date object is located in the phone’s memory. The address is called a reference to the Date object, and d is now said to refer to the Date object. The Date object itself actually has no name, but it will be convenient to call it “the object d”. You’ll let me get away with this, won’t you?

Call a method of the object

When we say d.next(), we automatically execute the group of statements inside the {curly braces} after the public void next in Date.java. The name of this group of statements is next. A group of statements with a name, enclosed in curly braces, is called a method.

Recall that our Date object d contains three fields named year, month, and day. When the statements in the body (i.e., within the curly braces) of next are executed because we said d.next(), the year, month, and day mentioned by those statements are the year, month, and day fields inside the object d. When the statements in the body of next are executed because we said e.next(), the year, month, and day mentioned by those statements are the year, month, and day fields inside the object e.

The object to which the method belongs

The object before the dot in the statement that executes the method is called the object to which the method belongs. For example, when we write d.next(), we are executing the next method that belongs to the Date object d. When we write e.next(), we are executing the next method that belongs to the Date object e. But it’s really the same method in both cases. There is only one copy of this method in the device’s memory, but by writing a different object each time in front of the dot, we can make the method manipulate the fields of different objects.

In fact, all the objects of a given class have the same methods, just as they all have the same fields. The class declaration in Date.java lists all the fields and methods that belong to each object of class Date.

In the body of a method, the keyword this stands for the object that the method belongs to. Let’s say that one of the statements in next mentioned this. If next was being executed because we said d.next(), then the this would mean d. If next was being executed because we said e.next(), then the this would mean e.

One method can call another

One method of an object can easily call another method of the same object. For exmple, the one-parameter next calls the no-parameter next during each iteration of the for loop.

Return values

The one-parameter next and the no-parameter next change the content of the Date object to which they belong. But they return no value. This means that we cannot say

        int i = d.next();   //won't compile

Two examples of methods that do return a value are the following. In the body of each method, the keyword return is followed by the expression that is returned by the method.

        int j = d.julian();        //an integer in the range 1 to 366 inclusive
        String s = d.toString();   //a string in the format "12/31/2015"

Create a string that shows the content of an object

An object’s toString method can also be called implicitly by using the + operator to paste the object onto a string.

        //These two statements do the same thing.
        String s = "The date is " + d.toString() + ".\n";
        String s = "The date is " + d + ".\n";

Every object of class Date has a method named next that takes one int parameter and changes the values stored in the three fields inside of the object. If the object contained December 31, 2015, the parameter 60 would change the content of the object to February 29, 2015. (Objects of our present class Date believe that every year is a leap year.) Every Date object also has a no-parameter next method that moves the object one day ahead.

A field vs. a local variable

The variables year, month, and day last as long as the Date object that contains them. We never have to worry about a Date object outliving its own internal organs. These variables can be mentioned in all the methods of the class.

The variables sum and i in the body of the julian method last only as long as the method that created them (julian) is executing. They can be mentioned only within that method.

A static field of a class

We also created an int variable named DECEMBER, holding the number 12. Since we marked it as final, the content of this variable can never change. The Java convention is to give a final variable an all-uppercase name.

There’s another way in which the variable DECEMBER is special: it has a last name. Its last name is Date, the name of our new class. This would allow the app to have another variable named DECEMBER with a different last name. (See George Washington and George Clooney in XML.) A variable whose last name is the name of a class is called a static field of that class.

DECEMBER is a field of class Date because it was declared in Date.java within the curly braces of the declaration for class Date. But it differs from year, month, and day because it was declared with the keyword static. This means that there is only one copy of the variable DECEMBER, no matter how many objects of class Date are created. The single copy is not stored inside of any Date object.

We’re lucky that d is born containing Deceber 31. If it was born with November 11, there would be no way to tell which parameters were the month and year. Change the

            Date d = new Date(12, 31, 2015);
to
            Date d = new Date(Date.DECEMBER, 31, 2015);
In the body of the no-parameter next, change 12 to DECEMBER. The prefix Date. is unnecxessary inside of a method of class Date (at least, as long as there is no local variable named DECEMBER in the method).

A static method of a class

The method monthsInYear returns the number 12. Like DECEMBER, monthsInYear belongs to class Date as a whole, not to any particular Date object. That’s why we wrote the name of the class, not the name of an object, in front of it when we executed it. This kind of method is called a static method or a class method.

A subclass

Add the following file SmarterDate.java to the project. Put it in the folder that’s holding the two existing .java files, MainActivity.java and Date.java.

Class SmarterDate is a subclass (improved version) of class Date. Conversely, class Date is the superclass of class SmarterDate.

The constructor of the subclass always begins by calling the constructor of the superclass. Often the constructor of the subclass passes its parameters unchanged to the constructor of the superclass. That’s what’s happening here.

The subclass constructor is not the only method of the subclass that calls the corresponding method of the superclass. The length method of the subclass will call the length method of the superclass most of the time. But in one special case—February in a non-leap year—it will not call the length method of the superclass. We say that the subclass length overrides the superclass length, and we mark it with the special word @Override.

package edu.nyu.scps.bed;

public class SmarterDate extends Date {

    public SmarterDate(int month, int day, int year) {
        super(month, day, year);
    }

    @Override
    public int length() {
        if (getMonth() == 2 && !isLeap()) {
            return 28;
        } else {
            return super.length();
        }
    }

    //Return true if this SmarterDate belongs to a leap year.

    private boolean isLeap() {
        int year = getYear();
        if (year % 400 == 0) {
            return true;
        } else if (year % 100 == 0) {
            return false;
        } else if (year % 4 == 0) {
            return true;
        } else {
            return false;
        }
    }

}

In the onCreate method of class MainActivity in the file MainActivity.java, change

            Date e = new Date(Date.DECEMBER, 31, 2014);
to
            SmarterDate e = new SmarterDate(Date.DECEMBER, 31, 2014);

Run the app and tell it you want to go 60 days forward from 12/31/2014. The output will be

Class Date
A year has 12 months.
January is month number 1.
December is month number 12.
12/31/2015 is day number 365 of the year 2015.
The day after 12/31/2015 is 1/1/2016.
OK
Forward
How many days forward from 12/31/2014 would you like to go?
Type answer here and press Done.
60
OK
Forward
The day that is 60 days after 12/31/2014 is 2/28/2015.
The day after that is 3/1/2015.
OK

An anonymous subclass

The following example no longer uses class SmarterDate. The file SmarterDate.java can be removed from the project. Select the file in the Android Studio project view, choose Delete…, and press OK.

In place of class SmarterDate, we will use a class that is exactly the same except that it has no name. We do this when there is exactly one statement that creates an object of the class. (In the following example, the object is e.) If there are two or more statements that create objects of the class, we should reinstate class SmarterDate.

Put the following code between the horizontal lines in the onCreate method of class MainActivity in the file MainActivity.java.

        //-----------------------------
        //Demonstrate the class Date we created in the file Data.java.

        //Can use the static methods and fields of a class
        //even when no objects of the class exist.

        String output = "A year has " + Date.monthsInYear() + " months.\n"
            + "January is month number " + Date.JANUARY + ".\n"
            + "December is month number " + Date.DECEMBER + ".\n";

        Date d = new Date(12, 31, 2015);
        int j = d.julian();
        output += d + " is day number " + j + " of the year " + d.getYear() + ".\n";
        output += "The day after " + d + " is ";
        d.next();
        output += d + ".\n";
        display("Class Date", output);

        for (;;) {
            Date e = new Date(12, 31, 2014) {
                @Override
                public int length() {
                    if (getMonth() == 2 && !isLeap()) {
                        return 28;
                    } else {
                        return super.length();
                    }
                }

                private boolean isLeap() {
                    int year = getYear();
                    if (year % 400 == 0) {
                        return true;
                    } else if (year % 100 == 0) {
                        return false;
                    } else if (year % 4 == 0) {
                        return true;
                    } else {
                        return false;
                    }
                }
            };

            int i = getInt("Forward", "How many days forward from " + e + " would you like to go?");
	    output = "The day that is " + i + " days after " + e + " is ";
            e.next(i);   //Move e i days forward.
            output += e + ".\n";
            e.next();    //Move e one additional day forward.
            output += "The day after that is " + e + ".";

            display("Forward", output);
        }
        //-----------------------------

Error checking with try, throw, catch, finally

In Data.java, change the constructor and no-arg next and prev to the following.

public Date(int month, int day, int year) {
        this.year = year;	//this.year is the field, year is the parameter

        if (month < 1 || month > 12) {
            throw new RuntimeException("bad month " + month);
        }
        this.month = month;

        if (day < 1 || day > length()) {
            throw new RuntimeException("bad day " + day + " of month " + month);
        }
        this.day = day;
    }
    public void next() {
        if (day < length()) {
            ++day;
            return;
        }

        day = 1;
        if (month < 12) {
            ++month;
            return;
        }

        month = 1;
        if (year < Integer.MAX_VALUE) {
            ++year;
            return;
        }

        //Roll back an unsuccessful attempt at a change.
        //Don't leave this Date object in a half-changed state.
        month = monthsInYear();
        day = length();
        throw new RuntimeException(this + " is already the last Date.");
    }
    public void prev() {
        if (day > 1) {
            --day;
            return;
        }

        if (month > 1) {
            --month;
        } else if (year > Integer.MIN_VALUE) {
            month = 12;
            --year;
        } else {
            throw new RuntimeException(this + " is already the first Date.");
        }

        day = length();
    }
        //-----------------------------
        String output = "";

        try {
            Date d = new Date(4, 31, 2015); //There is no April 31st.
        } catch (RuntimeException runtimeException) {
            output += runtimeException + "\n";
        }

        try {
            Date d = new Date(12, 31, Integer.MAX_VALUE);
            d.next();
        } catch (RuntimeException runtimeException) {
            output += runtimeException + "\n";
        }

        display("Output", output);
        //-----------------------------
Output
java.lang.RuntimeException: bad day 31 of month 4
java.lang.RuntimeException: 12/31/2147483647 is already the last date.

Will the Date constructor throw an exception for February 29, 2015? Will the SmarterDate constructor throw an exception for February 29, 2015?

A simpler but sillier example of inheritance

//This file is InformalPerson.java.
package edu.nyu.scps.bed;

public class InformalPerson {
    String nickName;

    InformalPerson(String nickName) {
        this.nickName = nickName;
    }

    @Override
    public String toString() {
        return nickName;
    }
}

A double quote inside a double-quoted string must be preceded with a backslash.

//This file is FormalPerson.java.
package edu.nyu.scps.bed;

public class FormalPerson extends InformalPerson {
    String lastName;
    String firstName;

    FormalPerson(String firstName, String nickName, String lastName) {
        super(nickName);
        this.firstName = firstName;
        this.lastName = lastName;
    }

    @Override
    public String toString() {
        return firstName + " \"" + super.toString() + "\" " + lastName;
    }
}
        //-----------------------------
        String output = "";
        FormalPerson roosevelt = new FormalPerson("Theodore", "Teddy", "Roosevelt");
        FormalPerson nixon = new FormalPerson("Richard", "Tricky Dick", "Nixon");
        InformalPerson carter = new InformalPerson("Jimmy");

        //Some of the objects in this array are of class InformalPerson,
        //and some of them are of class FormalPerson.

        InformalPerson[] a = {
            roosevelt,
            nixon,
            carter
        };

        for (int i = 0; i < a.length; ++i) {
            output += a[i] + "\n";   //means output = output + a[i].toString() + "\n";
        }

        //A downcast converts a reference to a superclass object
        //down to a reference to a subclass object.
        FormalPerson n = (FormalPerson)a[1];

        display("Presidents", output);
        //-----------------------------
Presidents
Theodore "Teddy" Roosevelt
Richard "Tricky Dick" Nixon
Jimmy
OK