☰ Contents Java Details

Sweet Little Lies

Student: Professor, I’ve been reading Pict.java, and there’s a lot you haven’t been telling me.
Prof: Oh?
Student: Just look at like, the rotate method. Sometimes the method header says rotate(int) and sometimes it says rotate(double), but both seem to work. I remember default from earlier, but how can there be two defaults of the same method?
default Pict rotate(int degrees) { return new Rotate(this, degrees); }
default Pict rotate(double degrees) { return new Rotate(this, degrees); }

Pict.java, lines 71–72

Prof: Ah, I see what you mean. Yeah, there are a number of conveniences in Pict.java that we haven’t talked through. We can address some!
Student: OK, so let’s do this one first. How are there two rotates?
Prof: This is called overloading. We can write the same method name, in the same class, with different parameters. Java will use the arguments and types in the call to figure out which one to call. Here’s a very simple example:
Prof: It will work with most types, and a lot of built-in libraries use it in another way: for the same method taking different numbers of arguments. For example, substring can take just one argument, and then it takes the rest of the string starting from that index, or it can take an explicit end index:
Student: Oh that explains why sometimes documentation for Java lists the same method twice.
Prof: That’s just it! So it is an incredibly handy convenience. I use it for both of these cases.
Student: Wait, are there other cases?
Prof: Yeah, so there are a ton of complex rules for what happens with overloading, mostly I don’t try to remember them all and look them up when needed.
Student: You don’t remember them all?
Prof: Honestly, yeah. Well, OK here’s a complex example:
Student: What the heck? It’s the same object stored in r and rr, right?
Prof: Yup, good noticing. r and rr would store the same reference, but different code is called for whatType on r and rr!
Student: That’s so weird because I thought when you call methods the receiver decides it.
Prof: Right, the receiver decides which class to look in. Here these are top-level methods so that detail isn’t involved. But once we figure out which class to find the methods in, Java does another round of selection based on the arguments. And here’s the tricky part – it does it based on the types of the arguments, not based on the argument values. So this is one place in Java where the type you use for a variable can change what code runs! If you change the type of r to RectRegion instead of Region, it will call the other method.
Student: Hm. I’m not sure I like that.
Prof: Frankly, me either. I definitely like overloading for simple cases, like taking a number or a string, or for different lengths of argument lists. But I find the interaction of overloading with interfaces and implements to be really tricky to think through. It has uses, so you should be aware it’s possible. But I mostly just use it for convenience. If you want to read more you can see both tutorials and somewhat more dense references.
Student: Oh interesting. So it just makes it so you can rotate with either an int or a double? But the body is exactly the same for both! How can that work?
Prof: Well, it turns out the Rotate constructor is also overloaded.
Student: You can overload a constructor?
Prof: Hold that thought :-)

Practice

slice builds a new array out of part of another one, from index start up to (but not including) index end, just like substring does for a String. Write an overload of slice that takes only arr and start, and slices all the way to the end of the array. It can call the slice that’s already there.

int[] slice(int[] arr, int start, int end) {
int[] result = new int[end - start];
int index = 0;
for (int i = start; i < end; i += 1) {
result[index] = arr[i];
index += 1;
}
return result;
}

{

}

int[] nums = { 1, 2, 3, 4, 5, 6 };
int[] middle = slice(nums, 1, 3);
int[] rest = slice(nums, 1);