Mastering Java Classes, Objects, and Lambda Expressions
Creating and Using Objects
A typical Java application operates by creating objects, which interact by invoking methods. These interactions allow the program to perform tasks such as rendering GUIs, processing calculations, or handling network traffic. Once an object has fulfilled its purpose, its resources are reclaimed for reuse.
Consider the following example, GeometryDemo, which instantiates a Coordinate object and two Box objects.
public class GeometryDemo {
public static void main(String[] args) {
// Declare and create a coordinate and two box objects.
Coordinate startPoint = new Coordinate(23, 94);
Box containerOne = new Box(startPoint, 100, 200);
Box containerTwo = new Box(50, 100);
// Display containerOne's dimensions
System.out.println("Width of containerOne: " + containerOne.width);
System.out.println("Height of containerOne: " + containerOne.height);
System.out.println("Area of containerOne: " + containerOne.calculateArea());
// Set containerTwo's position
containerTwo.origin = startPoint;
// Display containerTwo's position
System.out.println("X Position of containerTwo: " + containerTwo.origin.x);
System.out.println("Y Position of containerTwo: " + containerTwo.origin.y);
// Move containerTwo and display the new position
containerTwo.move(40, 72);
System.out.println("X Position of containerTwo: " + containerTwo.origin.x);
System.out.println("Y Position of containerTwo: " + containerTwo.origin.y);
}
}
The Lifecycle of an Object
Creating an object involves three distinct steps:
- Declaration: Associating a variable name with a specific object type.
- Instantiation: Using the
newkeyword to allocate memory. - Initialization: Calling a constructor to initialize the new object.
Declaration
Declaring a variable does not create an object. It simply notifies the compiler that you will use a specific name to refer to data of a specific type.
Coordinate startPoint; // Declaration only
Instantiation and Initialization
The new operator instantiates a class by allocating memory for a new object and returning a reference to that memory. The operator is followed by a constructor call, which initializes the object.
Coordinate startPoint = new Coordinate(23, 94);
Using Objects
Once an object is created, you can interact with its fields and methods.
Accessing Fields
You access an object's fields using a dot operator (.). If the code is outside the class where the field is defined, you must use a qualified name:
objectReference.fieldName
Invoking Methods
Methods are invoked similarly, by appending the method name to the object reference, followed by arguments in parentheses.
objectReference.methodName(argumentList);
Garbage Collection
Java runtime environments manage memory automatically via garbage collection. An object becomes eligible for garbage collection when it is no longer referenced by any variable. You may explicitly drop a reference by setting the variable to null, but this is rarely necessary.
Classes and Class Members
Returning Values from Methods
A method returns to the code that invoked it when it completes all statements, reaches a return statement, or throws an exception. Methods declared with a return type other than void must use the return statement with a value of the corresponding type.
The this Keyword
Inside an instance method or constructor, this refers to the current object. It is commonly used to distinguish between instance variables and parameters that have the same name (shadowing).
public class Coordinate {
public int x;
public int y;
public Coordinate(int x, int y) {
this.x = x;
this.y = y;
}
}
Access Control Modifiers
Access modifiers determine whether other classes can use a particular field or invoke a specific method.
| Modifier | Class | Package | Subclass | World |
|---|---|---|---|---|
public |
Y | Y | Y | Y |
protected |
Y | Y | Y | N |
| (default) | Y | Y | N | N |
private |
Y | N | N | N |
Static vs. Instance Members
The static keyword creates fields or methods that belong to the class itself rather than to any specific instance. All instances share the same static variable.
public class Device {
private int id;
private static int deviceCount = 0;
public Device() {
id = ++deviceCount;
}
public static int getDeviceCount() {
return deviceCount;
}
}
Nested Classes
Java allows you to define a class within another class. These are categorized as:
- Static Nested Classes: Behave like top-level classes but are nested for packaging convenience. They cannot access instance members of the outer class.
- Inner Classes: Associated with an instance of the enclosing class and can access its private members.
Local and Anonymous Classes
You can also define classes inside method bodies. These are known as local classes. Anonymous classes are similar but do not have a name; they are used for one-time instantiation and implementation.
Lambda Expressions
Lambda expressions provide a concise way to represent functional interfaces (interfaces with a single abstract method). They allow you to treat functionality as a method argument, or code as data.
Syntax
A lambda expression consists of a parameter list, an arrow token (->), and a body.
(parameterList) -> { expression; }
Example: Filtering Data
Imagine you have a list of Product objects and want to filter them based on price.
Approach 1: Local Class
class PriceFilter implements Filter {
public boolean test(Product p) {
return p.getPrice() > 50;
}
}
filterInventory(inventory, new PriceFilter());
Approach 2: Anonymous Class
filterInventory(inventory, new Filter() {
public boolean test(Product p) {
return p.getPrice() > 50;
}
});
Approach 3: Lambda Expression
filterInventory(inventory, p -> p.getPrice() > 50);
Approach 4: Using Standard Functional Interfaces and Streams
Java provides standard functional interfaces like Predicate<T> in java.util.function.
inventory.stream()
.filter(p -> p.getPrice() > 50)
.forEach(p -> System.out.println(p.getName()));
Accessing Enclosing Scope
Like local and anonymous classes, lambda expressions can access local variables and parameters of the enclosing block. However, these variables must be final or effectively final (not modified after initialization).