Case Study: A Point Class
Java Records were added to Java in version 14. They are a time-saving
method for creating objects whose primary purpose is representing data.
Let's remake the Point
class we created in the
equals page.
Let's make one and inspect it in jshell
.
public record Point(int x, int y)
{
}
Ooh, lookie! See what we got for free!
jshell> /open Point.java
jshell> Point p = new Point(3,4);
p ==> Point[x=3, y=4]
jshell> Point q = new Point(3,4);
q ==> Point[x=3, y=4]
jshell> Point r = new Point(0,0);
r ==> Point[x=0, y=0]
jshell> p.toString()
$5 ==> "Point[x=3, y=4]"
jshell> p.equals(q)
$6 ==> true
jshell> p.equals(r)
$7 ==> false
jshell> p.equals("Hey, I am a string!");
$8 ==> false
We get an equals()
method that meets the Java standard. We also
get a toString()
method as well.
But there's more!
With a nod to Ron Popeil...
jshell> p.x()
$9 ==> 3
jshell> p.y()
$10 ==> 4
Notice the simple getter methods we got for free.
But what if I don't like the toString() method?
Override it.
public record Point(int x, int y)
{
@Override
public String toString()
{
return String.format("(%s, %s)", x, y);
}
}
Here it is in action.
jshell> /open Point.java
jshell> Point p = new Point(3,4);
p ==> (3, 4)
jshell> p.toString();
$3 ==> "(3, 4)"
But what if I want another constructor?
Overload it.
public record Point(int x, int y)
{
public Point()
{
this(0, 0);
}
@Override
public String toString()
{
return String.format("(%s, %s)", x, y);
}
}
Let's inspect our creation.
jshell> /open Point.java
jshell> Point p = new Point();
p ==> (0, 0)
jshell> Point q = new Point(3,4);
q ==> (3, 4)
jshell> p.equals(q)
$4 ==> false
Can I have other methods in a record?
Yes.
public record Point(int x, int y)
{
public Point()
{
this(0, 0);
}
@Override
public String toString()
{
return String.format("(%s, %s)", x, y);
}
public double distanceTo(Point other)
{
return Math.hypot(x - other.x, y - other.y);
}
}
Now inspect your breathtaking creation.
jshell> /open Point.java
jshell> Point p = new Point();
p ==> (0, 0)
jshell> Point q = new Point(5,12);
q ==> (5, 12)
jshell> p.distanceTo(q)
$4 ==> 13.0
A Final Word
The state in a record is automatically private
and
final
. Since our Point
class's state is
primitives, our Point
objects are immutable.
Records are final
classes; this means you cannot
subclass them. This forces you to use composition, which is not
entirely a bad thing. They are really a tool for representing a particular
type of data. Any methods you create should focus on that function.
All Java records are subclasses of java.lang.Record
.
It's worth a look at this
API page.
What not to do Do not put mutable state in a record. The design contract is that records should produce immutable objects. All state should be primitive or immutable.
It is a compiler error to place state variables in a record beyond those declared in the record's header.
You can download the Point
class from the navigation area.