简体   繁体   中英

Generic method that returns subclass of explicitly declared superclass

The Goal

The title is long and confusing... I want a method which can be used like this:

Double d = getAsNumber(Double.MAX_VALUE);
Short s = getAsNumber(Short.MAX_VALUE);
  • The method is guaranteed to only accept a subclass of java.lang.Number
  • The method will only return an instance of the exact same class that was passed in

The parameter being passed in is actually a default value in case the getAsNumber method doesn't actually have a value.

What I have so far

This is actually working but it's ugly and I don't understand why the heck I need to explicitly cast with (T). The enum feels like an ugly workaround, and (T) shouldn't be necessary, the compiler KNOWS that Double or Long is a subclass of Number...

 private enum NUMBER_CLASS {
        Double, Float, Integer, Long, Short
    }

    private static final <T extends Number> T  getAsNumber( T defaultVal ) {
        final String string = "2";//would normally get from data source
        if (string==null) {
            return defaultVal;
        }

        NUMBER_CLASS numberClass = NUMBER_CLASS.Double.valueOf(defaultVal.getClass().getSimpleName());
        switch (numberClass) {
            case Double:
                return (T) Double.valueOf(string); // WHY is explicit cast necessary!
            case Long:
                return (T) Long.valueOf(string);
            default:
                throw new IllegalArgumentException("Must give a java.lang.Number");
        }
    }

There must be a better way?

** UPDATE **

Multiple questions are posed here. The main question I'm actually after is why I needed to explicitly cast with (T). It seems to me the compiler has all of the information it needs in order to guarantee that Double will meet the method signature's return type.

The compiler is unable to find what is your function internal reasoning. Imagine the code:

switch (numberClass) {
    case Double:
        return Long.valueOf(string); // If there is no cast it will broke type system
    case Long:
        return Double.valueOf(string); // The same
    default:
        throw new IllegalArgumentException("Must give a java.lang.Number");
}

The return value isn't formally connected to the type T so without the cast it's possible to return Long with Double argument, etc. That's why the cast is needed.

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