Modern Java: var, records & Optional
Newer Java adds var for less typing, records for tiny data classes, and Optional to handle missing values without null bugs.
What you will learn
- Use var to let Java infer a variable type
- Replace a boilerplate data class with a record
- Avoid null errors with Optional
Why these features exist
Java keeps improving. The features in this lesson all come from modern Java (version 10 and later, with records arriving in Java 16). They do not add brand-new powers so much as let you write the same thing with far less clutter — and, in the case of Optional, dodge one of the most common bugs in all of programming.
var: let Java work out the type
When you declare a variable, the type on the left is often obvious from the value on the right. var tells Java to infer (work out) the type for you. The variable is still strictly typed — Java just figures the type out from the value, so you type less.
var name = "Asha"; // inferred as String
var age = 25; // inferred as int
var prices = new java.util.ArrayList<Double>(); // inferred as ArrayList<Double>
System.out.println(name + " is " + age);
prices.add(19.99);
System.out.println(prices);Note: Output:
Asha is 25
[19.99]
Java read "Asha" and made name a String, read 25 and made age an int, and read the new ArrayList<Double>() and matched that type exactly. var saved repeating long type names. The types are fixed and checked as always — name can never hold a number.
Watch out: var only works for local variables with a value on the right (Java needs something to infer from). You cannot use it for fields, method parameters, or a bare var x; with no value. And do not overuse it — var total = compute(); hides the type, so prefer var where the type is already obvious from the line.
records: a data class in one line
You often need a small class that just holds data — a point, a coordinate, a person with a name and age. Written the old way that is a lot of boilerplate: private fields, a constructor, a getter for each field, plus equals, hashCode and toString. A record generates all of that for you from a single line.
public record Point(int x, int y) { }Note: Output:
(No output yet — but this one line gives you a constructor, read methods x() and y(), a sensible toString, and value-based equals/hashCode, all generated automatically. Writing the same class by hand would take 30+ lines.)
Now watch how little code it takes to use it. The record made the constructor and the accessor methods for free:
public class Main {
record Point(int x, int y) { }
public static void main(String[] args) {
Point a = new Point(2, 3);
Point b = new Point(2, 3);
System.out.println("x is " + a.x()); // generated reader
System.out.println(a); // generated toString
System.out.println("Equal? " + a.equals(b)); // generated equals
}
}Note: Output:
x is 2
Point[x=2, y=3]
Equal? true
We never wrote a constructor, a getter, a toString or an equals — the record generated them all. a.x() read the first value, printing a gave the tidy Point[x=2, y=3], and equals returned true because the two points hold the same values. Records are perfect for plain data carriers.
Optional: a safe box for a maybe-missing value
The single most common crash in Java is the NullPointerException — calling a method on a variable that is null (holds nothing). It usually happens when a lookup finds no result and returns null, which the caller forgets to check. Optional is a small box that either contains a value or is empty, forcing you to handle the empty case on purpose.
import java.util.Optional;
public class Main {
// pretend this looks a user up by id and might find nobody
static Optional<String> findName(int id) {
if (id == 1) {
return Optional.of("Asha"); // found
}
return Optional.empty(); // not found
}
public static void main(String[] args) {
Optional<String> found = findName(1);
Optional<String> missing = findName(99);
System.out.println(found.orElse("Unknown"));
System.out.println(missing.orElse("Unknown"));
}
}Note: Output:
Asha
Unknown
findName(1) returned an Optional holding "Asha"; findName(99) returned an empty Optional. orElse("Unknown") unwraps the value if present, or gives the fallback if empty. Because the return type is Optional<String>, the caller is reminded right there that the value might be missing — so the null crash never happens.
The two methods you will reach for most are isPresent() (is there a value?) and orElse(fallback) (give me the value or this default). There is also ifPresent(...) to run code only when a value exists.
Tip: A good rule: return an Optional from methods that might genuinely find nothing (a search, a lookup), so callers cannot forget to handle the empty case. Do not use Optional for fields or method parameters — that is not what it is designed for.
Q. What problem does Optional help you avoid?
✍️ Practice
- Rewrite a small
Bookdata class (title and pages) as arecord, then create one and print it. - Write a method
findPrice(String item)that returns anOptional<Double>— a value for "coffee", empty for anything else — and print the result withorElse(0.0).
🏠 Homework
- Take any small data-holding class you have written and convert it to a record. In 3 to 4 sentences, list what the record generated for you and explain when an Optional return type would be clearer than returning null.