Is it possible to write a generic +1 method for numeric box types in Java?

后端 未结 7 2034
旧巷少年郎
旧巷少年郎 2021-01-18 15:56

This is NOT homework.

Part 1

Is it possible to write a generic method, something like this:



        
相关标签:
7条回答
  • 2021-01-18 16:15

    FWIW this isn't really a limitation of generics.

    The + operator only works on primitives. The reason it works for Integer or Long is because of autoboxing/unboxing with their primitive types. Not all Number subclasses have a matching primitive type, but more importantly Number doesn't have a matching primitive type. So taking generics out of it completely, the following code would still be wrong:

    public Number plusOne(Number num) {
        return num + 1;
    }
    
    0 讨论(0)
  • 2021-01-18 16:16

    Arithmetic operations in Java work only on primitives. You here are combining generics and autoboxing unboxing etc.

    For such a simple case as yours I'll suggest use only primitives.

    0 讨论(0)
  • 2021-01-18 16:17

    Not all of the subclasses of Number can be autounboxed. BigDecimal, for instance, can't be autounboxed. Therefore the "+" operator won't work for it.

    0 讨论(0)
  • 2021-01-18 16:17

    The problem here is that your code must unbox the object, operate on the primitive, and then rebox it. Java really can't do that because by the time the code is compiled, it doesn't know what the type is any more, so it doesn't know how to unbox.

    The value of Java generics is really to preserve type safety, i.e. the compiler knows the real class and will prevent illegal assignments. The compiler will NOT generate different code depending on the type: it WON'T say "oh, that's an integer, so I need to generate an integer add here, versus that one's a String, so the plus sign really means string concatenation". It's really quite different from C++ templates, if that's what you're thinking of.

    The only way you could make this work would be if there was a plusOne function defined for Number, which there isn't.

    0 讨论(0)
  • 2021-01-18 16:26

    Part 1:

    Doesn't num + 1 work without the need to create such method? The + operator is overloaded just for that. That is, why call:

    Integer n = plusOne(anotherInt);
    

    when you can do:

    Integer n = anotherInt + 1;
    

    The bottomline is - you can't combine autoboxing with generics.

    0 讨论(0)
  • 2021-01-18 16:30

    Not the prettiest solution ever, but if you rely in the following properties of every known implementation of Number (in the JDK):

    • They can all be created from their String representation via a one-argument constructor
    • None of them has numbers that can't be represented by BigDecimal

    You can implement it using reflection and using Generics to avoid having to cast the result:

    public class Test {
    
        @SuppressWarnings("unchecked")
        public static <T extends Number> T plusOne(T num) {
            try {
                Class<?> c = num.getClass();
                Constructor<?> constr = c.getConstructor(String.class);
                BigDecimal b = new BigDecimal(num.toString());
                b = b.add(java.math.BigDecimal.ONE);
                return (T) constr.newInstance(b.toString());
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
        public static void main(String[] args) {
            System.out.println(plusOne(1));
            System.out.println(plusOne(2.3));
            System.out.println(plusOne(2.4E+120));
            System.out.println(plusOne(2L));
            System.out.println(plusOne(4.5f));
            System.out.println(plusOne(new BigInteger("129481092470147019409174091790")));
            System.out.println(plusOne(new BigDecimal("12948109247014701940917.4091790")));
        }
    
    }
    

    The return is done using an apparently unsafe cast but given that you're using a constructor of the class of some T or child of T you can assure that it will always be a safe cast.

    0 讨论(0)
提交回复
热议问题