public abstract class Vector, K extends VectorKind> { public abstract K kind(); public abstract V sub(V that); } abstract class VectorKind { public abstract V zero(); } class Vector2d extends Vector { private final double x; private final double y; public Vector2d(double x, double y) { this.x = x; this.y = y; } public TwoDimensionalVector kind() { return TwoDimensionalVector.INSTANCE; } public Vector2d sub(Vector2d that) { return new Vector2d(this.x - that.x, this.y - that.y); } public String toString() { return "(" + x + ", " + y + ")"; } } class TwoDimensionalVector extends VectorKind { public static final TwoDimensionalVector INSTANCE = new TwoDimensionalVector(); public static final Vector2d ZERO = new Vector2d(0.0, 0.0); private TwoDimensionalVector() {} // singleton public Vector2d zero() { return ZERO; } } class Vector3d extends Vector { private final double x; private final double y; private final double z; public Vector3d(double x, double y, double z) { this.x = x; this.y = y; this.z = z; } public ThreeDimensionalVector kind() { return ThreeDimensionalVector.INSTANCE; } public Vector3d sub(Vector3d that) { return new Vector3d(this.x - that.x, this.y - that.y, this.z - that.z); } public String toString() { return "(" + x + ", " + y + ", " + z + ")"; } } class ThreeDimensionalVector extends VectorKind { public static final ThreeDimensionalVector INSTANCE = new ThreeDimensionalVector(); public static final Vector3d ZERO = new Vector3d(0.0, 0.0, 0.0); private ThreeDimensionalVector() {} // singleton public Vector3d zero() { return ZERO; } } class VectorTest { public static void main(String... args) { System.out.println(invert(new Vector2d(1.41421, 1.41421))); System.out.println(invert(new Vector3d(1.25992, 1.25992, 1.25992))); } private static , K extends VectorKind> V invert(V plus) { return plus.kind().zero().sub(plus); } }