简体   繁体   English

Java-绑定到特定子类的通用数字方法签名

[英]Java - Generic Number method signature bound to specific subclasses

I would like to write a method which takes a generic Number as an argument and returns another Number , whose type may possibly differ from the first and is passed as a second argument. 我想编写一个将通用Number用作参数并返回另一个Number ,该Number的类型可能与第一个类型不同,并作为第二个参数传递。 Something like this: 像这样:

public <N1 extends Number, N2 extends Number> N2 op(N1 num, Class<N2> retType) {
    //Sample operation, where type conflicts arise.
    return retType.cast(num + 3);
}

Of course, I can't just cast N1 to N2 if they are, say, Integer and Long . 当然,如果它们是IntegerLong ,我不能将N1N2 However, Number offers doubleValue() , intValue() , ... , methods that could allow me to partially work around the issue with a switch/case statement. 但是, Number提供了doubleValue()intValue() ,...等方法,这些方法可以让我通过switch / case语句部分解决此问题。 That would restrict me to the return types of the xxxValue() methods the Number class exposes, thus cutting off AtomicInteger , AtomicLong , BigInteger and BigDecimal . 那将限制我使用Number类公开的xxxValue()方法的返回类型,从而切断AtomicIntegerAtomicLongBigIntegerBigDecimal

This is still acceptable for the application I have in mind, but my methods would not be able to properly handle any custom or future official extensions of the Number class if not with a default statement in the switch/case block which should arbitrarily decide which xxxValue() method to invoke, or throw an exception (which I would like to avoid). 对于我所想到的应用程序,这仍然是可以接受的,但是如果没有在switch / case块中使用default语句来随意决定哪个xxxValue() ,则我的方法将无法正确处理Number类的任何自定义或将来的官方扩展。 xxxValue()方法来调用或引发异常(我想避免这种情况)。 I could use an enumeration to encapsulate parameters by type, but I am afraid my code would get too convoluted and tricky to use. 我可以使用枚举按类型封装参数,但是恐怕我的代码将变得非常复杂和难以使用。

The answer to this question gives further insight as to declaring a single generic type for two parameters (it doesn't enforce that both parameters will be, in fact, of the same type at runtime), which sure is worth mentioning here. 这个问题的答案提供了进一步的洞察力,即可以为两个参数声明一个通用类型(它并没有强制要求两个参数实际上在运行时都属于同一类型),在这里肯定值得一提。

What I want to achieve is: 我想要实现的是:

  • Declare a method with some generic (in a broad sense) Number parameters, possibly more methods with a different number of parameters (eg a method with two, a method with three parameters) but still generic in each of them. 声明一个带有一些通用 (广义上) Number参数的方法,可能会更多带有不同数量参数的方法(例如,带有两个参数的方法,带有三个参数的方法),但在每个参数中仍然是通用的。
  • The parameters must be restricted to some well-known extensions of Number (eg Integer , Double ). 必须将参数限制为一些知名的Number扩展(例如IntegerDouble )。
  • The parameters may come in multiple combinations, potentially any of (Double, Integer) , (Double, Double) , (Integer, Double) , (Integer, Integer) . 参数可以有多种组合,可能是(Double, Integer)(Double, Double)(Integer, Double)(Integer, Integer)

I would like not to define multiple methods with different signatures. 我不想定义多个具有不同签名的方法。 Even while fixing the return type eg to Double , the number of methods would explode as more arguments and types are added. 即使将返回类型固定为例如Double ,随着添加更多参数和类型,方法的数量也会激增。

Of course, I may always resort to specific, ad-hoc implementations for each method, as I am probably not going to need every possible combination of types. 当然,我可能总是对每种方法都采用特定的特定实现,因为我可能不需要每种类型的所有可能组合。 I would still like my design to be flexible enough while enforcing these type constraints. 我仍然希望我的设计在执行这些类型约束时足够灵活。

Here's my best workaround, which calls the constructor that accepts a String as the parameter, and returns a null if it fails (Edited to remove printStackTrace and remove one unnecessary branch) 这是我最好的解决方法,它调用接受String作为参数的构造函数,并在失败时返回null(已编辑以删除printStackTrace并删除一个不必要的分支)

public static <R extends Number> R op(Number num, Class<R> retType) {
    BigDecimal three = BigDecimal.valueOf(3);
    BigDecimal bdNum = new BigDecimal(num.toString());

    //add three
    BigDecimal bdResult = bdNum.add(three);

    String strResult = bdResult.toString();
    Constructor[] cons = retType.getDeclaredConstructors();
    for (Constructor con: cons) {
        if (con.getParameterCount() == 1) {
            if (con.getGenericParameterTypes()[0] == String.class) {
                try {
                    return (R)con.newInstance(strResult);
                } catch (InstantiationException | IllegalAccessException | NumberFormatException e) {
                } catch (InvocationTargetException e) {
                    //if here then either the decimal place is causing a problem
                    // when converting to integral type,
                    // or the value is too large for the target type

                    // so let's try to remove the decimal point by truncating it.
                    strResult = bdResult.toBigInteger().toString();
                    try {
                        return (R)con.newInstance(strResult);
                    } catch (NumberFormatException | IllegalAccessException | InstantiationException | InvocationTargetException e1) {
                    }
                }
                //if here, then the most likely the integral type is too large
                //like trying to put 3,000,000,000 into an int
                // when largest int possible is 2,147,483,647
            }
            //if here, then no constructors with 1 String parameter
        }
    }

    return null;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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