Writing Classes in Java

There are two meanings of “class”. A class could be:

  • a code library (a collection of methods). For example, java.lang.Math contains methods for performing basic numeric operations.
  • an object data type (a description of a concept). For example, java.lang.String represents character strings.

Classes, Objects, Attributes, Operations

A class represents a concept; an object is a realization of the concept. A class is the blueprint of an object; multiple objects can be created from the same class.

There are two kinds of members inside a class:

  • attributes (data/properties, variables)
  • operations (methods, functions)

Attributes keeps the “state” of an object. Attributes are also called data members, properties, or simply variables. These variables are visible to all methods within the class.

Operations, also called methods, functions, provide controls over the “state” (attributes) of an object (initialize, set, get, update, …).

The following defines a class Account.

public class Account
{
	private static int	count = 0;
	private String		owner;
	private double		balance;

	public Account(String _owner)
	{
		owner = _owner;
		balance = 0;
		count++;
	}

	public double getBalance()
	{
		return balance;
	}

	public void setBalance(double newBalance)
	{
		balance = newBalance;
	}

	public String getOwner()
	{
		return owner;
	}

	public void setOwnerBalance(String _owner)
	{
		owner = _owner;
	}

	public double deposit(double amount)
	{
		balance += amount;
		return balance;
	}

	public double withdraw(double amount)
	{
		balance -= amount;
		return balance;
	}

	public static int getCount()
	{
		return count;
	}
}

Visibility modifiers. Visibility modifiers control the access to the member (attribute or operation) of a class.
public: can be referenced from outside of the object
private: can be used inside the class definition but not externally

Accessor and mutator. An accessor method (getter) provides read-only access to an attribute. A mutator method (setter) changes an attribute. Accessors and mutators are usually named getX() or setX(), where X denotes the attribute name.

Constructor. A constructor is a special method to initialize a new object when it is created. It has the same name as the class; it has no return type.

Class attributes vs. local variables. A class attribute, declared at the class level, has a scope of the entire class. Any method of the class can refer to it. A local variable, declared inside a method, has scope limited the declaring method. It simply does not exist outside of the method. The parameters in a method header serve as local variables.

Static variables and static methods. These static members are declared using the static modifier. For example,
private static int count = 0;
A static variable is shared among all instances of a class. There is only one copy of a static variable for all objects of a class. A local variable in a method cannot be static.

Static methods can be called through the class name (instead of an object). A static method cannot access non-static attributes of a class; it can, however, access static variables. Static variables and methods are independent of specific objects.

The following class shows how we can use the Account class defined above.

public class AccountManage
{
	public static void main (String[] args)
	{
		Account alice = new Account("Alice");
		Account bob = new Account("Bob");

		alice.deposit(1000);
		System.out.println(alice.getOwner() + "'s balance: $" + alice.getBalance());

		bob.deposit(200);
		System.out.println(bob.getOwner() + "'s balance: $" + bob.getBalance());
		System.out.println("----------------------------------");

		alice.withdraw(100);
		System.out.println(alice.getOwner() + "'s balance: $" + alice.getBalance());
		bob.withdraw(100);
		System.out.println(bob.getOwner() + "'s balance: $" + bob.getBalance());
		System.out.println("----------------------------------");

		System.out.println("There are totally " + Account.getCount() + " accounts");

	}
}

Class Design

Solving a problem by programming is more than just coding. It usually involves the following steps:

  • Requirement analysis. This is mainly about what the finished program will do, what are the inputs, outputs, or the efficiency requirement. Requirement analysis will not specify “how to do”. Requirements are often incomplete, not accurate, and provided by domain experts (but not computer experts).
  • Designing. This is about the ideas to program to meet the requirements, including the designing of classes, methods, and major algorithms.
  • Implementation. This is on the technical details of the solution — writing code by following the design. Note that, important decision should have been made in the design process. Plan before you try, not after you fail.
  • Test. This is to make sure that the program meet the requirements. Mistakes are inevitable, so we need to build test cases to identify mistakes automatically.

Consider the following problem: Finding the distance between two addresses.

The inputs of this case consists of two addresses, for example
515 W Hastings Street, Vancouver
8888 University Drive, Burnaby
The output should be a distance, such as 14.0 km. While the major requirement is clear, there are many details that we may need to clarify or make certain assumption. For example,
How accurate should the distance be?
What address formats are acceptable?

The idea of a solution is as follows. Each address has a geographical position (latitude, longitude). The distance between two addresses can be computed from the two positions. Thus we would like to obtain the geographical position of the two addresses and then compute the distance.

We would like to design two classes:

  • Location, represents a location, with attributes (address, latitude, longitude) and a method to compute the distance).
  • LocationTool, contains a method to build a Location object by finding the latitude and longitude of an address.

The Location class is implemented as below. It has three attributes and one method:

  • address: String
  • latitude: double
  • longitude: double
  • distanceTo(): distance to another location
public class Location
{
	private String		address;
	private double		latitude;
	private double		longitude;

	public Location(String _address, double _latitude, double _longitude)
	{
		address = _address;
		latitude = _latitude;
		longitude = _longitude;
	}

	// Compute the distance in meters
	// Reference http://stackoverflow.com/a/837957
	public double distanceTo(Location loc)
	{
	    double earthRadius = 3958.75;
	    double dLat = Math.toRadians(latitude - loc.latitude);
	    double dLng = Math.toRadians(longitude - loc.longitude);
	    double a = Math.sin(dLat/2) * Math.sin(dLat/2) +
	               Math.cos(Math.toRadians(latitude)) * Math.cos(Math.toRadians(latitude)) *
	               Math.sin(dLng/2) * Math.sin(dLng/2);
	    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
	    double dist = earthRadius * c;

	    int meterConversion = 1609;

	    return dist * meterConversion;
	}

	public String toString()
	{
		return address + " (" + latitude + ", " + longitude + ")";
	}

}

The LocationTool class is implemented as below. It has a method makeLocation() which finds the latitude and longitude of an address and builds a Location object. Here we use the Google geocoding service (https://developers.google.com/maps/documentation/geocoding/), for example:
http://maps.google.com/maps/api/geocode/json?sensor=false&address=515+W+Hastings+St+Vancouver

import java.io.IOException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Scanner;

public class LocationTool
{
	// Read from URL and return the content in a String
	public static String readURLContent(String urlString) throws IOException
	{
		URL url = new URL(urlString);
		Scanner scan = new Scanner(url.openStream());

		String content = new String();
		while (scan.hasNext())
		{
			content += scan.nextLine();
		}
		scan.close();
		return content;
	}

	// Extract the middle part of a string from "open" to "close"
	public static String extractMiddle(String str, String open, String close)
	{
		int	begin = str.indexOf(open) + open.length();
		int end = str.indexOf(close, begin);
		return str.substring(begin, end);
	}

	// Extract the middle part of a string from "open" to the end
	public static String extractMiddle(String str, String open)
	{
		int	begin = str.indexOf(open) + open.length();
		return str.substring(begin);
	}

	// Make a Location object from a string address
	public static Location makeLocation(String addr) throws IOException
	{
		String url = "http://maps.google.com/maps/api/geocode/json?sensor=false&address=";
		url += URLEncoder.encode(addr, "UTF-8");
		String content = readURLContent(url);

		String formatted_address = extractMiddle(content, "\"formatted_address\" : \"", "\",");
		String latlng = extractMiddle(content, "\"location\" : {", "},");
		double lat = Double.parseDouble(extractMiddle(latlng, "\"lat\" :", ","));
		double lng = Double.parseDouble(extractMiddle(latlng, "\"lng\" :"));

		return new Location(formatted_address, lat, lng);
	}
}

Now we are ready to write the main program which uses Location and LocationTool to build the solution. We build a class Distance with a main() method which
asks the user for two addresses,
builds two Location objects using LocationTool.makeLocation(),
computes the distance

Here we separate different tasks into different classes:

  • Location has the information of a location
  • LocationTool uses some external tool to build a Location object

Method Design

After establishing high-level design issues, it’s important to address low-level issues such as the design of key methods. Method design involves the interaction of the methods with the rest of the system: such as parameters, returns, and the control flow.

For example, the method charAt() of the String class is defined as below.
public char charAt(int index)

  • Returns the char value at the specified index. An index ranges from 0 to length() – 1. The first char value of the sequence is at index 0
  • Parameters: index – the index of the char value.
  • Returns: the char value at the specified index of this string. http://docs.oracle.com/javase/7/docs/api/java/lang/String.html#charAt(int)

The parameters of a method are local variables. Parameter passing is like an assignment statement. When we pass an object to a method, we are actually passing a reference to the object.

Method decomposition is to divide a complex (big) task into multiple methods. For example, consider the problem of extracting the title from the HTML file specified by a URL. We may split the task into two steps: (1) read the HTML file using the URL (2) search for the “title” element.

Building reusable codes is an important skill for method design. An example is the “progressive tax” calculation. The calculation is common for any government; the only difference is about the threshold numbers. The following method abstracts out the common logic of tax caculation.

public static double calcTax(double[]base, double[]rate, double income)
{
	double tax = 0;   int i;
	for (i = 0; i < base.length; i++)
	{
		if (income <= base[i]) { tax += income*rate[i]; return tax; }
		tax += base[i] * rate[i];  income -= base[i];
	}
	return tax + income * rate[i];
}

public static double fedTax2(double a)
{	double[] base = {42707, 42707, 46992};
	double[] rate = {0.15, 0.22, 0.26, 0.29};
	return calcTax(base, rate, a);
}

Method overloading is to have multiple methods using the same name but different parameter lists (such as different the number of parameters, the types of those parameters, the order of the types). But two overloaded methods cannot differ only by their return type.

For example, the substring method of the String class is overloaded.
String substring(int beginIndex, int endIndex)
Returns a substring which begins at beginIndex and extends to endIndex – 1

String substring(int beginIndex)
Returns a substring which begins at beginIndex and extends to the end of this string
When we call the method, the system will decide which method to be invoked based on the parameters.
“smiles”.substring(1, 5) returns “mile”
“unhappy”.substring(2) returns “happy”

Comments

comments