0

Is there a way to assign a default value to a derived class of Number, that can be passed through an int?

I am looking for an implementation for:

public <N extends Number> N getNumberWithDefault( N number, int defaultNumber );

I know it would be easy with:

public <N extends Number> N getNumberWithDefault(N number, Class<N> clazz, String defaultNumber) {
    N result = number;
    if (result == null) {
        try {
            result = clazz.cast(clazz.getDeclaredMethod("valueOf", String.class).invoke(null, defaultNumber));
        } catch (Exception ex) {
            LOGGER.error("Error assigning default", ex);
        }
    }
    return result;
}

But it does not seem elegant at all. Is there any way to improve it?

UPDATE

What I wanted to know is if something like this can be done:

public <N extends Number> N getNumberWithDefault( N number ) {
    return getNumberWithDefault( number, 1 );
}

Then, the same function (getNumberWithDefault(number)), could be used for all classes.

Although I think that may be possible that that way to proceed is not possible in Java

  • 1
    IMO, a more elegant solution would be to use `N` as the type of `defaultNumber`. This allows you to get rid of the `clazz` parameter. But I can't think of a more elegant way to do a `valueOf` conversion in a generic context. – Stephen C Jun 03 '21 at 15:02
  • 1
    The signature of this method is impossible to honour. The function returns an unknown N and you want the return value to be int by default? Remove generics and return just `Number`, and also , take `Number`, then you can do this. – Renato Jun 03 '21 at 15:26
  • Thank you for your answers. I have just updated my question to be more specific. May be Renato's answer is the good one (+1). I will keep in mind that it is impossible to create that function. (Generics was needed, as I wanted to return the same type as the input parameter, although with this signature the input parameter was allowed to be just Number, and in that case, I think the function would fail to guess the return type) – Francisco Javier Rojas Jun 04 '21 at 08:47
  • Maybe related: https://stackoverflow.com/questions/2768054 – f_puras Jun 04 '21 at 08:56

2 Answers2

1

The type-safe way, though not elegant, is to use overloading instead of generics.

Advantage is that the default value can be non-int, if needed.

public Byte getNumberWithDefault(Byte number, int defaultNumber) {
    return (number != null ? number : Byte.valueOf((byte) defaultNumber));
}
public Short getNumberWithDefault(Short number, int defaultNumber) {
    return (number != null ? number : Short.valueOf((short) defaultNumber));
}
public Integer getNumberWithDefault(Integer number, int defaultNumber) {
    return (number != null ? number : Integer.valueOf(defaultNumber));
}
public Long getNumberWithDefault(Long number, long defaultNumber) {
    return (number != null ? number : Long.valueOf(defaultNumber));
}
public Float getNumberWithDefault(Float number, float defaultNumber) {
    return (number != null ? number : Float.valueOf(defaultNumber));
}
public Double getNumberWithDefault(Double number, double defaultNumber) {
    return (number != null ? number : Double.valueOf(defaultNumber));
}
public BigInteger getNumberWithDefault(BigInteger number, long defaultNumber) {
    return (number != null ? number : BigInteger.valueOf(defaultNumber));
}
public BigDecimal getNumberWithDefault(BigDecimal number, double defaultNumber) {
    return (number != null ? number : BigDecimal.valueOf(defaultNumber));
}

UPDATE

For the updated question, where the default value is hardcoded as 1, the answer is the same.

public Byte getNumberWithDefault(Byte number) {
    return (number != null ? number : Byte.valueOf((byte) 1));
}
public Short getNumberWithDefault(Short number) {
    return (number != null ? number : Short.valueOf((short) 1));
}
public Integer getNumberWithDefault(Integer number) {
    return (number != null ? number : Integer.valueOf(1));
}
public Long getNumberWithDefault(Long number) {
    return (number != null ? number : Long.valueOf(1L));
}
public Float getNumberWithDefault(Float number) {
    return (number != null ? number : Float.valueOf(1f));
}
public Double getNumberWithDefault(Double number) {
    return (number != null ? number : Double.valueOf(1d));
}
public BigInteger getNumberWithDefault(BigInteger number) {
    return (number != null ? number : BigInteger.ONE);
}
public BigDecimal getNumberWithDefault(BigDecimal number) {
    return (number != null ? number : BigDecimal.ONE);
}
Andreas
  • 154,647
  • 11
  • 152
  • 247
  • that's actually pretty neat. I was thinking to do something with Method Handles, but this is much nicer, to me. – Eugene Jun 03 '21 at 15:53
  • Thank you for your answer. Nice solution, but this is not exactly what I was looking for. I have just updated my question to be more specific. – Francisco Javier Rojas Jun 04 '21 at 08:51
1

I think the only way to solve this is to change the signature of your method to

public <N extends Number> N getNumberWithDefault( N number, N defaultNumber )

That is, the defaultNumber has to be of the same type as number.

Of course, then its simple:

public <N extends Number> N getNumberWithDefault( N number, N defaultNumber ){
   return number != null ? number : defaultNumber;
}