☰ Contents Java Details

A Static Shock

Student: I went and looked up the Px and Colors classes, because you wouldn’t tell me about them. What’s going on with the word static in them? I can kind of read them but I don’t know what that part means. And also what’s going on with throw?
    /** A width, height, or radius: zero is fine (an empty picture), negative is not. */
    static int size(String what, int v) {
        if (v < 0) throw new IllegalArgumentException(what + " is negative: " + v);
        return v;
    }
}

Pict.java, lines 88–93

Prof: Great questions. Let’s take them one at a time. First throw – this is Java’s built in way of reporting errors. When throw ... evaluates, it stops the current methods and all methods on the stack. It will report the error you created, and also the stack trace. This is called throwing an exception.
Student: I’ve seen stack traces before! So you’re telling me if I give a negative number I’ll get an error from size?
Prof: That’s right.
Student: What is IllegalArgumentException?
Prof: Java has many built in exceptions – they are descriptions of errors that can happen. In this case, the size method is just used to check if a number is non-negative. It’s used where we want to make sure a radius or a width is zero or more.
Student: Huh, OK. Wait what’s what?
Prof: What?
Student: The parameter, it’s called what, but it’s only used in the error message?
Prof: That’s right – reporting errors is important! So important that sometimes we have information included just in case an error occurs.
Student: OK, so in this use in Raster it’s just to say whether width or height was wrong?
    Raster(int width, int height) {
        this.width = Px.size("width", width);
        this.height = Px.size("height", height);
        // ...

Pict.java, lines 412–414

Prof: That’s it – it assigns width or height to the value after checking it.
Student: And I’m seeing something now that I look at this code. Can I make a guess about static?
Prof: By all means!
Student: Well, I see that size was declared static. And then we did one of those calls like we do on Math where it’s a capital thing before the dot, which ought to be a class. So is that how we call size because it’s static?
Prof: Nice analysis! You got it. A static method has no receiver. We usually use it for library methods that aren’t associated with any object instance. So a lot of Math and Arrays are like that, because they work directly on numbers or arrays without constructing an object first. And in the Pict library, that’s what Px is doing.
Student: OK now I want to see what it does.
Prof: Try doing a call from another method, called from main, to see the trace.

In the book, the version of Java that runs cannot show line numbers. When running in a full-fledged programming environment, you’d see something like this:

$ javac Throw.java Pict.java
$ java Throw
Should be just 4 4
Exception in thread "main" java.lang.IllegalArgumentException: test is negative: -3
	at Px.size(Pict.java:90)
	at Throw.anotherMethod(Throw.java:3)
	at Throw.main(Throw.java:6)
Student: Neat! So I can see that the stack has Px.size, then anotherMethod, then main. Wait! That’s the same order as the stack in a memory diagram!
Prof: Indeed – the stack trace captures exactly the stack we draw. To draw it the way we usually do:
Names (the stack)
Px.size("test", -3)
what = test
v = -3
throws IllegalArgumentException
anotherMethod()
main()
top level
Heap
Student: Interesting. So those diagrams really are what’s happening.
Prof: They are pretty accurate!

Practice

Each of these is a call we have made somewhere in the book. Is it a static method, or an instance method?
Math.sqrt(2.0) is
"trash".length() is
Integer.parseInt("42") is
dot.beside(square) is
Arrays.equals(a, b) is
raster.colorAt(0, 0) is
Colors.named("red") is
query.matches(img) is

None of these methods is marked static yet. Which ones could be static, because they never use x or y (or this)? Select all that could.

record Point(double x, double y) {
  double distanceTo(Point other) {
    return Math.sqrt((x - other.x) * (x - other.x) + (y - other.y) * (y - other.y));
  }
  Point flipped() { return new Point(y, x); }
  Point origin() { return new Point(0, 0); }
  double average(double a, double b) { return (a + b) / 2; }
  boolean isOrigin() { return x == 0 && y == 0; }
}
  1. origin
    origin makes a new Point out of nothing but numbers, so it doesn’t need a point to be called on: Point.origin().
  2. average
    average only uses its parameters a and b, so it could be static. (It hardly belongs in Point at all.)
  3. distanceTo
    distanceTo uses x and y, the fields of the point it was called on.
  4. flipped
    flipped uses x and y to build the new point.
  5. isOrigin
    isOrigin asks about the fields of the point it was called on.