简体   繁体   中英

Java Generics in Function

I've got a little but annoying problem with use of Generics in a Function.

The Function has to convert into a Double any value that could be BigDecimal or BigInteger . That's why I designed it with a <T> type for incoming argument.

The problem is that when I'm using it, I have to cast the given argument with <T> ...

Here is the code of the Function:

private Function<T, Double> bigToDouble = value -> {
    BigDecimal bigDec = null;
    if (value instanceof BigInteger) {
        BigInteger bigInt = (BigInteger) value;
        bigDec = new BigDecimal(bigInt);
    }
    if (value instanceof BigDecimal) {
        bigDec = (BigDecimal) value;
    }
    return NumberUtils.toDouble(bigDec, NumberUtils.DOUBLE_ZERO);
};

When I test it, I've got an error if I do not cast the given argument with <T> :

BigDecimal bigDec = new BigDecimal("2.5");      
BigInteger bigInt = new BigInteger("150000");
System.out.println("FUNCTION TEST = " + bigToDouble.apply((T) bigInt));
System.out.println("FUNCTION TEST = " + bigToDouble.apply((T) bigDec));

What I expect is to call it this way, simply:

bigToDouble.apply(bigInt)

How should I design it to avoid such behaviour?

Did you try using the parent class of BigInteger and BigDecimal , Number ?

Instead of using generics, try replacing T with Number , which accepts both BigInteger and BigDecimal . This would look like the following code:

  private Function<Number, Double> bigToDouble = value -> {
    BigDecimal bigDec = null;
    if (value instanceof BigInteger) {
      BigInteger bigInt = (BigInteger) value;
      bigDec = new BigDecimal(bigInt);
    }
    if (value instanceof BigDecimal) {
      bigDec = (BigDecimal) value;
    }
    return NumberUtils.toDouble(bigDec, NumberUtils.DOUBLE_ZERO);
  };


  public void test(){
    BigDecimal bigDec = new BigDecimal("2.5");
    BigInteger bigInt = new BigInteger("150000");
    System.out.println("FUNCTION TEST = " + bigToDouble.apply(bigInt));
    System.out.println("FUNCTION TEST = " + bigToDouble.apply(bigDec));
  }

EDIT

In case you want to use your own <T> type parameter, without having the limitations that you will have when using <T> now, you should declare a generic method. (see: https://docs.oracle.com/javase/tutorial/java/generics/methods.html ). The code for that would look like the following then:

  private <T extends Number> Function<T, Double> bigToDouble() {
    return value -> {
      BigDecimal bigDec = null;
      if (value instanceof BigInteger) {
        BigInteger bigInt = (BigInteger) value;
        bigDec = new BigDecimal(bigInt);
      }
      if (value instanceof BigDecimal) {
        bigDec = (BigDecimal) value;
      }
      return NumberUtils.toDouble(bigDec, NumberUtils.DOUBLE_ZERO);
    };
  }

  public void test() {
    BigDecimal bigDec = new BigDecimal("2.5");
    BigInteger bigInt = new BigInteger("150000");
    System.out.println("FUNCTION TEST = " + bigToDouble().apply(bigInt));
    System.out.println("FUNCTION TEST = " + bigToDouble().apply(bigDec));
  }

如果您明确声明了T的界限(<T extends Number>它会改变吗?

The reason that you need a cast from BigInteger / BigDecimal to T is that at compile time, the compiler doesn't know what exactly the type T is, so the compiler is not sure if it can cast the BigInteger / BigDecimal to the type T . Thus, you need to do a force cast.

To solve this, one solution is to replace T by the parent class of your parameters classes. Thus the compiler is sure that it can do a downcasting.

Otherwise, you have to tell the compiler explicitly that you know at runtime, when you declare the type T , you are sure that BigInteger or BigDecimal can be cast to this type. Which means that you have to write a force cast every time you invoke the apply method.

Below the code :

public static void main(String[] args) {
        Function<Number, Double> bigToDouble = value -> {
            BigDecimal bigDec = null;
            if (value instanceof BigInteger) {
                BigInteger bigInt = (BigInteger) value;
                bigDec = new BigDecimal(bigInt);
            }
            if (value instanceof BigDecimal) {
                bigDec = (BigDecimal) value;
            }
            return bigDec.doubleValue();
        };

        BigDecimal bigDec = new BigDecimal("2.5");      
        BigInteger bigInt = new BigInteger("150000");
        System.out.println("FUNCTION TEST = " + bigToDouble.apply(bigInt));
        System.out.println("FUNCTION TEST = " + bigToDouble.apply(bigDec));
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM