如何返回与传入的相同具体类型的Number对象?

时间:2018-06-19 08:20:28

标签: java constructor casting

我有以下不完整的课程。它实现了一个方法,该方法将任何Number对象作为参数并将其约束为存储在long中的限制,然后返回原始值或约束值。但是,返回的约束值必须与输入参数具有相同的具体类型。

public class Max implements Constraint {

    long max;

    public Number constrain(Number n) {
        if (n.longValue() <= max) {
            return n;
        }
        // return a number of the type passed in with value max
    }
}

关于创建与另一个类型相同的对象还有其他问题,但答案假设有一个no-arg构造函数可用,而数字类型则不是这样。

我玩过:

n.getClass().getConstructor(new Class<?>[] { n.getClass() }).newInstance(max);

但即使在这里,我仍然有关于传递正确参数的问题。我回到原点。无论如何,它并不优雅。

我知道我可以用很多if语句来做,但我正在寻找更聪明的东西。

4 个答案:

答案 0 :(得分:1)

由于Number的子类的构造函数将primitives作为参数,因此您无法查找具有Wrapper Class作为参数的构造函数

他们都拥有的构造函数是String一个

long max;

public Number constrain(Number n) {
    if (n.longValue() <= max) 
       try{
           return n.getClass()
                .getConstructor(String.class)
                .newInstance(String.valueOf(max));
       }catch(Exception ex){ex.printStackTrace();}
    return n;
}

public static void main(String[]args){
    Max m = new Max();
    m.max = 10;
    System.out.println(m.constrain(new Double(25)).getClass()); // class java.lang.Double
    System.out.println(m.constrain((int) 18).getClass());       // class java.lang.Integer
}

Working DEMO

答案 1 :(得分:0)

编辑 - v3 - 不带字符串。

如果您只讨论允许原始类型作为构造函数输入的Numbers成员,如Float / Double / Integer / Long / ...,则可以使用以下代码:

    //Number n is valid someInput;
    //Note this only works for Classes that take a single numeric value as input
    Class type = (Class) n.getClass().getDeclaredField("TYPE").get(n);
    return n.getClass().getConstructor(type).newInstance(max)

像BigInteger这样的东西不适用于这个。

编辑 - v2

对于原始类型n,似乎“Number n”被不同的签名替换,并且使用Integer.TYPE(或Float.TYPE等),你可以传入'int'作为getConstructor的参数。你可以在这里使用n.TYPE(但提醒TYPE是类成员,它可能会弹出一些警告)。

    n.getClass().getConstructor(n.TYPE).newInstance(max);

然而,正如@azro指出的那样:如果你坚持不使用字符串并希望它变得聪明,你仍然需要分支,它可能更糟糕:你需要考虑Number的所有子类,包括BigInteger。

是的,将它转换为字符串是令人讨厌的,但是否则,您可能需要使用一个额外的类来包装Number的每个子类,该类允许非基本类型作为构造函数的args。我几乎认为if-statements更糟糕。

原始答案

我对Reflection不是很熟悉。 但是这里的一个主要问题是Numbers的构造函数是字符串或原始类型,如'int',你不能真正利用'int'作为getConstructor的输入。

至少,以下可能有效。

    //assume n is some Number object.
    n.getClass().getConstructor(String.class).newInstance(max.toString());

这就像Float / Integer / BigInt这样的类... /具有以String作为输入的构造函数。

答案 2 :(得分:0)

使用Java 5或者hogher,你可以在你的方法中使用泛型

package test;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import org.junit.Assert;

public class Max {

    Number max;

    public <T extends Number> T constrain(T n) {
        if (n.floatValue() <= max.floatValue()) {
            return n;
        } else {
            return castTo(max, n.getClass());
        }

    }

    @SuppressWarnings("unchecked")
    private <T extends Number> T castTo(Number max2, Class<? extends Number> class1) {
        if (class1.equals(AtomicInteger.class)) {
            return (T) new AtomicInteger(max2.intValue());
        } else if (class1.equals(AtomicLong.class)) {
            return (T) new AtomicLong(max2.longValue());

            // these case are dangerous to handle
        } else if (class1.equals(BigDecimal.class)) {
            return (T) BigDecimal.valueOf(max2.doubleValue());
        } else if (class1.equals(BigInteger.class)) {
            return (T) BigInteger.valueOf(max2.longValue());

            // Std Case
        } else if (class1.equals(Byte.class)) {
            return (T) (Byte) max2.byteValue();
        } else if (class1.equals(Double.class)) {
            return (T) (Double) max2.doubleValue();
        } else if (class1.equals(Float.class)) {
            return (T) (Float) max2.floatValue();
        } else if (class1.equals(Integer.class)) {
            return (T) (Integer) max2.intValue();
        } else if (class1.equals(Long.class)) {
            return (T) (Long) max2.longValue();
        } else if (class1.equals(Short.class)) {
            return (T) (Short) max2.shortValue();
        } else {
            throw new IllegalArgumentException("Can't handle this kind of Number : " + class1.getName());
        }
    }

    public static void main(String[] args) {
        Max max = new Max();
        max.max = 32;
        Integer constrain = max.constrain(33);
        Assert.assertEquals(Integer.class, constrain.getClass());
        Assert.assertEquals(max.max, constrain);

        Double constrain2 = max.constrain(33d);
        Assert.assertEquals(Double.class, constrain2.getClass());
        Assert.assertEquals(max.max.doubleValue(), constrain2, 0);

        Float constrain3 = max.constrain(33f);
        Assert.assertEquals(Float.class, constrain3.getClass());
        Assert.assertEquals(max.max.floatValue(), constrain3, 0);

        Short constrain4 = max.constrain((short) 33);
        Assert.assertEquals(Short.class, constrain4.getClass());
        Assert.assertEquals(max.max.shortValue(), constrain4, 0);

        Byte constrain5 = max.constrain((byte) 33);
        Assert.assertEquals(Byte.class, constrain5.getClass());
        Assert.assertEquals(max.max.byteValue(), constrain5, 0);

        Long constrain6 = max.constrain(33l);
        Assert.assertEquals(Long.class, constrain6.getClass());
        Assert.assertEquals(max.max.longValue(), constrain6, 0);

        BigDecimal constrain7 = max.constrain(BigDecimal.valueOf(33));
        Assert.assertEquals(BigDecimal.class, constrain7.getClass());
        BigInteger constrain8 = max.constrain(BigInteger.valueOf(33));
        Assert.assertEquals(BigInteger.class, constrain8.getClass());

        AtomicInteger constrain9 = max.constrain(new AtomicInteger(33));
        Assert.assertEquals(AtomicInteger.class, constrain9.getClass());
        AtomicLong constrain10 = max.constrain(new AtomicLong(33));
        Assert.assertEquals(AtomicLong.class, constrain10.getClass());

    }
}

但是在你的代码中构造所有数字的子代,没有常见的构造函数,最安全的方法是减少你想要处理的情况,我没有处理所有的Striped64孩子

答案 3 :(得分:0)

我发现了:

import org.apache.commons.beanutils.ConvertUtils;

...

return (Number)ConvertUtils.convert(max, n.getClass());

我没有看过消息来源,但我怀疑里面是否非常聪明。我相信它只是托管类型转换器的集合。

这是一种替代方法-并不比其他答案更好,但至少是简洁的。