Recently I struggled again with the issue, that Java does not support multiple return values of a function. Usually, this is not a problem, but sometimes it is just not very elegant.
In this case, I was writing a method that computes the bounds of a crop rectangle (in percent of the original image size). Please note, that I was not working in the java.awt.* context, thus I did not want to use Rectangle or any other AWT class to store the return value.
The most simple solution is to use an array.
float[] bounds = computeCrop(...)
While this works in this example, it becomes crude when the return values are not all of the same type. Additionally, you have to guess which index represents which coordinate (top? left? bottom? right? width? …).
An alternative is to use a dedicated value class, such as Rectangle:
public class Rectangle {
public float x;
public float y;
...
}
Rectangle bounds = computeCrop(...);
However while implementing I thought that this approach could be optimized further. Why not let the class’ constructor do the job of computing the values? This has the advantage, that the member fields can be final, and the code for computing the crop bounds is in one place. It would look like this:
public class Crop {
public final float x;
public final float y;
public final float width;
public final float height;
public Crop(float w1, float h1, float w2, float h2) {
float r1 = w1 / h1;
float r2 = w2 / h2;
if (r1 < r2) {
width = 1;
height = r1 / r2;
}
else {
width = r2 / r1;
height = 1;
}
x = (1f - width) / 2;
y = (1f - height) / 2;
}
}
Apart from coupling the logic tightly with the data (logic = constructor, data = member fields), is there any other drawback for using this approach?
I think I would create my own Rectangle class and add a method to it, cropped(float w, float h) which returns a new (immutable) Rectangle. Similar to all those methods on java.lang.String; the original String is immutable and there are a bunch of converters or transformers, if you will, that return a new String. Josh Bloch talks about this in one of his items in Effective Java.
I think it might be Item 15, Minimize Mutability.
The problem here is, that the crop operation transforms the coordinates into a different metric. While the input is pixel-based, the output is percentage. This would lead to misleading code, such as:
That’s why I decided for a special class.
Then make the method name describe what’s happening; cropByPercentage for example.
That’s an idea…
I included a Pair and Triple class in my projects and use them as typed return values. So its easy to return 2 or 3 values and the Generics offer some kind of type safety.