Need For Speed Learning Exercise Rewording/Adjustment Request

I propose a few changes to the Need For Speed exercise. They are all admittedly arguable, but I think they’re at least worth considering.
I’ll present a summary of my proposals first and then elaborate afterwards.

  1. Explicitly introduce the var keyword and/or replace it with the relevant types.
  2. Adjust the language around constructors so as not to call them methods.
  3. Adjust the code a bit to allow for proper encapsulation.

1. Explicitly introduce the var keyword and/or replace it with the relevant types.

If I’m not mistaken this is the first time var is used in the standard path through the exercises. It feels weird to use it without introducing it. I think a short paragraph with a link would probably suffice. If there is support, I could propose a draft.

Notably people coming back to Java from the old times (like myself) might be surprised by its existence and wonder if it’s a mistake in translating from another language.

Additionally, a non-trivial number of learners might be learning Java at least in part for the AP CS A exam which sadly still assumes Java 7.

Finally, my impression is that there is some debate about when its good style to use var instead of the explicit type. I imagine most would be fine with var car = new NeedForSpeed(speed, batteryDrain); but perhaps less happy with var car = NeedForSpeed.nitro();.

2. Adjust the language around constructors so as not to call them methods

This might be pedantic but for a certain type of learner, precise language can be helpful.

Formally speaking, constructors are considered by the JLS to be distinct from methods.

Evidence:

  • " The body of a class declares members (fields, methods, classes, and interfaces), instance and static initializers, and constructors ([§8.1.7](https://docs.oracle.com/javase/specs/jls/se23/html/jls-8.html#jls-8.1.7))."
  • " Constructors ([§8.8](https://docs.oracle.com/javase/specs/jls/se23/html/jls-8.html#jls-8.8)) are similar to methods, but cannot be invoked directly by a method call; they are used to initialize new class instances. Like methods, they may be overloaded ([§8.8.8](https://docs.oracle.com/javase/specs/jls/se23/html/jls-8.html#jls-8.8.8))."

Argument

I find it helpful to make a distinction between constructors and methods because:

  1. The JLS makes the distinction
  2. With this distinction we can say “method declarations always have a return type” and similar things
  3. Using a method and using a constructor look different
  4. constructors are not inherited while methods can be
  5. the this() and super() usage makes more sense when constructors are called out as separate entities
  6. It makes more sense to say a sentence like “Traditionally, the name of a method is written in camelCase”

Proposal

A constructor is a special type of method whose goal is to initialize a newly created instance. Constructors look like regular methods, but without a return type and with a name that matches the class’s name.

would become something like

A constructor is a special bundle of code whose goal is to initialize a newly created instance. Constructors look like regular methods, but without a return type and with a name that matches the class’s name.

and

Like regular methods, constructors can have parameters. Constructor parameters are usually stored as (private) fields to be accessed later, or else used in some one-off calculation. Arguments can be passed to constructors just like passing arguments to regular methods.

would become something like:

Like regular methods, constructors can have parameters. Constructor parameters are usually stored as (private) fields to be accessed later, or else used in some one-off calculation. Arguments can be passed to constructors just like passing arguments to regular methods.

3. Adjust the code a bit to allow for proper encapsulation

This is the thorniest issue and would require the most amount of work, although still not that much.

As it currently stands, Racetrack has a method boolean canFinishRace(NeedForSpeed car) which requires one of two options:

  1. accessing the battery drain and speed instance variables from the passed car, necessitating non-private instance variables. This violates good principles of encapsulation.
  2. Actually driving the car and seeing if it can complete the race. This irrevocably modifies the data of the car which is not good practice and a violation of the implied contract of the method.

Questions

I notice from looking at a few Community Solutions that it seems an older version of this exercise required writing Racetrack’s boolean tryFinishTrack(NeedForSpeed car) which more strongly implies that the car should be driven and we’d expect data modification of the passed car.

Is there a reason for this change? Can I find discussion about it somewhere?

Are we tied to the new version? It seems like switching back to the old version might be the least work while reducing showing bad coding style.

Alternatives

A few other fixes come to mind.

Adding getters

The next easiest fix would require the learner to write public getters/accessors for speed and battery drain in the NeedForSpeed class. This allows encapsulation by using private instance variables while still allowing for completion of the boolean canFinishRace(NeedForSpeed car) .

Moving the method to the NeedForSpeed class

One other idea that occurs to me is that it doesn’t really feel like canFinishRace should be Racetrack’s job at all. It’s primarily involving the details of the car with a single number required of the Racetrack.

If we moved the method to NeedForSpeed and made it public boolean canFinishRace(Racetrack track), we’d just need to require the learner to write a single getter for the track distance. Granted, we’d also need to update the description and test cases, too.

Postscript

Thank you for your consideration and all the work you do! Also, I apologize for garbling some of the links in markdown code snippets. The forum said that as a new user my post could have at most 2 links.

2 Likes