Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to combine similar Java functions into one (In C++ I'd use templates)

In one of my Java classes I have these 2 very similar functions. Is there a way in Java to combine them into one function so I don't have to maintain 2 functions?

public static boolean areValuesValid( double [] values, int numElements ) {
    if( values == null || values.length != numElements ) {
        return false;
    }
    for( int i = 0; i < numElements; ++i ) {
        if( Double.isNaN( values[i] ) ) {
            return false;
        }
    }
    return true;
}

public static boolean areValuesValid( float [] values, int numElements ) {
    if( values == null || values.length != numElements ) {
        return false;
    }
    for( int i = 0; i < numElements; ++i ) {
        if( Float.isNaN( values[i] ) ) {
            return false;
        }
    }
    return true;
}
like image 986
Marek Krzeminski Avatar asked Jun 23 '16 21:06

Marek Krzeminski


2 Answers

Your question is tricky in some ways for Java:

  1. double and float are primitive types, and as such they are not part of a class hierarchy. The wrapper Double and Float classes extends Number, which extends Object, but
  2. An array of primitive types is not the same as an array of objects, and Java does not autobox a float[] to Float[], for example.
  3. There is no isNan(Number n) or isNan(Object o) method in the Java API, but the ones above that you used, that expect a double or a float. However, you can do Double.isNan(n.doubleValue()) for any Number n.

TL;DR In Java the common practice for primitive types is to have separate implementations for each one of them, like you did.

EDIT: As @azurefrog suggested:

public static boolean areValuesValid(Number[] values, int numElements) {
    if (values == null || values.length != numElements) {
        return false;
    }
    for (Number value : values) {
        if (Double.isNaN(value.doubleValue())) {
            return false;
        }
    }
    return true;
}

And then you'd have to use Apache Commons ArrayUtils:

public static boolean areValuesValid(double[] values, int numElements) {
    return areValuesValid(ArrayUtils.toObject(values), numElements);
}

public static boolean areValuesValid(float[] values, int numElements) {
    return areValuesValid(ArrayUtils.toObject(values), numElements);
}

EDIT2: @shmosel's solution passes the array as an Object and from there avoids the conversion of the whole array to a boxed type. A solution worth considering to avoid that overhead.

like image 169
ericbn Avatar answered Oct 12 '22 13:10

ericbn


It's not an ideal scenario, for the reasons @ericbn explained. But here's one way to avoid most of the duplication using a lambda:

public static boolean areValuesValid( double [] values, int numElements ) {
    return areValuesValid(values, numElements, i -> !Double.isNaN(values[i]));
}

public static boolean areValuesValid( float[] values, int numElements ) {
    return areValuesValid(values, numElements, i -> !Float.isNaN(values[i]));
}

private static boolean areValuesValid( Object values, int numElements, IntPredicate tester ) {
    if( values == null || Array.getLength(values) != numElements ) {
        return false;
    }
    for( int i = 0; i < numElements; ++i ) {
        if( !tester.test(i) ) {
            return false;
        }
    }
    return true;
}
like image 33
shmosel Avatar answered Oct 12 '22 12:10

shmosel