The booking code was small.
A list of open tables.
When a table is booked,
take its number out of the list.
openTables.remove(tableNumber).
It read like English.
It passed the tests,
because the test booked table one
in a list that started with table one.
Then Friday night.
Table four was booked twice.
Table five
was marked as taken
with nobody sitting at it.
And once in a while,
booking table eighteen
threw an IndexOutOfBoundsException
and the waiter tried again.
Here is the mechanism.
A Java List has two methods
called remove.
remove(int index)
takes out whatever is at that position.
remove(Object o)
looks for that value
and takes it out.
Your list held Integers.
Your table number was an int,
straight from getTable().
When the compiler picks an overload,
it first looks for a method
that fits without boxing.
remove(int) fits exactly.
So it wins,
every time,
and nothing warns you.
remove(4)
did not remove the table numbered four.
It removed the fifth element,
which, with tables one to twenty open,
was table five.
Table four stayed open,
and somebody booked it again.
The exception
was the only honest part.
Once enough tables were gone,
the list was down to eighteen tables or fewer,
and there was no position eighteen
to remove.
The bug showed itself
only when it ran out of room
to hide.
Kotlin noticed this one.
On a MutableList,
remove takes the element,
and the index version
has its own name,
removeAt.
In Java,
say which one you mean.
remove(Integer.valueOf(tableNumber))
removes the value.
Better still,
ask whether it should be a list at all.
Open tables have no order
and no duplicates.
That is a Set,
and a Set only has one remove.
And write the test
with a list that does not start at one,
so position and value
cannot agree by accident.
When two methods share a name,
the compiler chooses for you.
Make sure it is choosing
what you meant.
– Serguey Asael Shinder
Top comments (0)