I've been working on a Java-based mathematics library focusing on vectors and matrices. I plan to use it for an important upcoming project, so the classes are analogous to data types available in GLSL (e.g. Vector2 for GLSL's vec2, Matrix4 for GLSL's mat4, and so on).
I'm trying to only expose interface to clients (e.g. Vector2
) and keep implementation classes package-private (e.g. Vector2f
). I'm trying to use the static factory methods and, since I'm using Java 1.8, I'm trying to take advantage of the new option that allows static methods to be added to interfaces.
However, there's something I currently don't like too much and I'm wondering if there's a way to improve the code design to address it.
Here's what the interface looks like (I've omitted the non-static methods for brevity because they're not relevant to the question):
public interface Vector2 extends Measurable, Bufferable {
static Vector2 createZeroVector() {
return Vector2f.createZeroVector();
}
static Vector2 createFrom(float x, float y) {
return Vector2f.createFrom(x, y);
}
static Vector2 createFrom(double x, double y) {
return Vector2f.createFrom(x, y);
}
static Vector2 createFrom(final float[] values) {
return Vector2f.createFrom(values);
}
static Vector2 createFrom(final double[] values) {
return Vector2f.createFrom(values);
}
static Vector2 createNormalizedFrom(float x, float y) {
return Vector2f.createNormalizedFrom(x, y);
}
static Vector2 createNormalizedFrom(double x, double y) {
return Vector2f.createNormalizedFrom(x, y);
}
static Vector2 createNormalizedFrom(final float[] values) {
return Vector2f.createNormalizedFrom(values);
}
static Vector2 createNormalizedFrom(final double[] values) {
return Vector2f.createNormalizedFrom(values);
}
// ... non-static methods omitted ...
}
I'll briefly note that:
final class Vector2f implements Vector2 {
// details omitted
}
Since the interface methods are static, they must be implemented in the interface itself. Classes implementing the interface don't get overrides because this can be resolved at compile time.
However, if a client goes ahead and does something like this:
public class Vector2d implements Vector2 {
// a double-precision implementation
}
and then tries to use the interface as intended:
// ...
Vector2 vec = Vector2.createFrom(5f, 3f);
Then there seem to be at least 2 problems:
the interface knows about a specific implementation and can only return Vector2f instances, and
a client expecting a Vector2d implementation class gets a Vector2f instead!
Since the methods in the interface are static, there seems to be no way for the client to actually get an instance of the hypothetical Vector2d implementation without changing the interface itself.
How can I improve the code's design in order to resolve this problem for clients?
I've read other articles and I'm not aware of something else that might be helpful here, though I might be wrong.