Thursday 10 September 2009

JDK 7 - Method suggestions

Joe Darcy has opened up a call for methods to add to the core JDK 7 lang/util package. The idea is to add methods that are "commonly-written utility methods".

JDK 7 utility methods

There is lots of prior art in the field of general utility method libraries for Java. Perhaps the best known is Apache Commons Lang. Lets choose some methods (other than string utilities) that might be appropriate for the JDK from ObjectUtils.

1) Null-safe equals check.
boolean Objects.equals(Object object1, Object object2)
Returns true if both are null, or both are non-null and equal.

2) Null-safe hash code.
int Objects.hashCode(Object object)
Returns the hash code of the object, or zero if the object is null.

3) Null-safe toString.
String Objects.toString(Object object)
Returns the toString of the object, or "" if the object is null.

4) Null-safe toString with specified default.
String Objects.toString(Object object, String defaultStr)
Returns the toString of the object, or defaultStr if the object is null.

5) Null-safe object defaulting.
T Objects.defaultNull(T object, T defaultValue)
Returns the object, or defaultValue if the object is null.
(Of course, the Elvis operator is a much clearer approach for this common case...)

6) Get maximum and minimum.
T Objects.max(T comparable1, T comparable2)
T Objects.min(T comparable1, T comparable2)
Returns the maximum/minimum object, returning the non-null object if either is null, or null if both are null.
Generics allow the return type to check the input is also a Comparable.

These would appear to be the obvious methods to add to an Objects utility class.

The following are missing methods on Integer/Long:

7) Compare two primitives.
int Byte.compare(byte value1, byte value2)
int Short.compare(short value1, short value2)
int Integer.compare(int value1, int value2)
int Long.compare(long value1, long value2)
Safely returns the comparison (-1/0/1) indicator for two primitives. (These methods already exist on Float/Double).

The methods from SystemUtils can be very useful in reducing arbitrary strings in code:

8) Get common environment variables.
File System.getJavaIoTempDir()
File System.getJavaHomeDir()
File System.getUserHomeDir()
File System.getUserDir()
Returns the java environment variables. There are lots more in SystemUtils that could be added.

Some more null handling methods for primitives - again, Elvis would be a better solution:

9) Convert wrapper to primitive avoiding NPE.
boolean Boolean.booleanValue(Boolean obj, boolean defaultValue)
char Character.charValue(Character obj, char defaultValue)
byte Byte.byteValue(Byte obj)
byte Byte.byteValue(Byte obj, byte defaultValue)
short Short.shortValue(Short obj)
short Short.shortValue(Short obj, short defaultValue)
int Integer.intValue(Integer obj)
int Integer.intValue(Integer obj, int defaultValue)
long Long.longValue(Long obj)
long Long.longValue(Long obj, long defaultValue)
float Float.floatValue(Float obj)
float Float.floatValue(Float obj, float defaultValue)
double Double.doubleValue(Double obj)
double Double.doubleValue(Double obj, double defaultValue)
Safe ways to convert a wrapper to a primitive.
The numeric ones return zero if the default argument isn't specified.
There is also a case for methods to convert arrays of wrappers to arrays of primitives.

These should probably be on a Booleans utility class as per BooleanUtils.

10) Boolean methods for clearer code.
boolean Booleans.isTrue(Boolean booleanObj)
boolean Booleans.isFalse(Boolean booleanObj)
Return true as per the method name, false if null.
boolean Booleans.isNotTrue(Boolean booleanObj)
boolean Booleans.isNotFalse(Boolean booleanObj)
Return true as per the method name, true if null.

11) Negate handling null.
Boolean Booleans.negate(Boolean booleanObj)
TRUE returns FALSE, FALSE returns TRUE, null returns null.

11) Boolean arithmetic.
boolean Booleans.and(boolean[] array)
boolean Booleans.or(boolean[] array)
boolean Booleans.xor(boolean[] array)
boolean Booleans.and(Boolean[] array)
boolean Booleans.or(Boolean[] array)
boolean Booleans.xor(Boolean[] array)
Performs the stated binary maths.

12) Character comparison.
boolean Character.equalsIgnoreCase(char ch1, char ch2)
Compares two characters ignoring case.

These should be constants for empty arrays on common classes:

13) Empty array constants.
Boolean[] Boolean.EMPTY_ARRAY
boolean[] Boolean.EMPTY_PRIMITIVE_ARRAY
Character[] Character.EMPTY_ARRAY
char[] Character.EMPTY_PRIMITIVE_ARRAY
Byte[] Byte.EMPTY_ARRAY
byte[] Byte.EMPTY_PRIMITIVE_ARRAY
Short[] Short.EMPTY_ARRAY
short[] Short.EMPTY_PRIMITIVE_ARRAY
Integer[] Integer.EMPTY_ARRAY
int[] Integer.EMPTY_PRIMITIVE_ARRAY
Long[] Long.EMPTY_ARRAY
long[] Long.EMPTY_PRIMITIVE_ARRAY
Float[] Float.EMPTY_ARRAY
float[] Float.EMPTY_PRIMITIVE_ARRAY
Double[] Double.EMPTY_ARRAY
double[] Double.EMPTY_PRIMITIVE_ARRAY
String[] String.EMPTY_ARRAY
Class[] Class.EMPTY_ARRAY
Object[] Objects.EMPTY_OBJECT_ARRAY
Return true as per the method name, true if null.

Or a better solution might be a method on Class:
T[] Class.emptyArray();
This allows code like:
Boolean.class.emptyArray();

14) Array size.
boolean Arrays.isEmpty(Object[] array)
int Arrays.size(Object[] array)
Where a null array is empty/size-zero.

15) Collection size.
boolean Collections.isEmpty(Collection coll)
int Collections.size(Collection coll)
boolean Collections.isEmpty(Map map)
int Collections.size(Map map)
Where a null collection is empty/size-zero.

Or, even better, add a new interface Sized with a single method to get the size. This would be retrofitted to Collection, Map, String and arrays (of course, technically, thats a language change...).

And some Class utilities for less NPEs when outputting the class name in debugging:

16) NPE safe way to get class name.
String Class.getName(Class cls)
String Class.getSimpleName(Class cls)
String Class.getPackageName(Class cls)
Returns the class name, or null if the input is null.
Again, the null-safe operators would avoid this kind of specific method.

Locale could do with some love, as per LocaleUtils:

17) Parse a locale string.
Locale parse(String localeStr)
Parses the locale string to a locale.

16) Country/Language lists.
List Locale.countriesByLanguage(String langaugeStr)
List Locale.languagesByCountry(String countryStr)
Extracts just countries or languages.

A better solution would be two new classes Country and Language. These would be useful in other places in the JDK, where a locale is used and it should be a country or language.

And the big one that I've missed - safe maths, as per Joda-Time FieldUtils:

18) Add/subtract/multiply/cast safely.
int Math.safeToInt(long value)
int Math.safeNegate(int value)
long Math.safeNegate(long value)
int Math.safeAdd(int value1, int value2)
long Math.safeAdd(long value1, int value2)
long Math.safeAdd(long value1, long value2)
int Math.safeSubtract(int value1, int value2)
long Math.safeSubtract(long value1, int value2)
long Math.safeSubtract(long value1, long value2)
int Math.safeMultiply(int value1, int value2)
long Math.safeMultiply(long value1, int value2)
long Math.safeMultiply(long value1, long value2)
These perform the specified mathematical operation, but throw ArithmeticException, rather than failing quietly, if overflow occurs.

I should also note that there are a whole heap of BigDecimal, BigInteger and String utilities that could be added.

Finally, of course, there are lots of other utility class libraries apart from Commons-Lang. Its simply that Commons-Lang provides a good point to start the discussion.

Feel free to (a) complain about my choices, and (b) suggest your own ideas. Remember to focus on non-string core classes for now.