3

我刚接触 Java 的泛型,所以我为自己建立了一个小项目。我想制作一个向量/点,您可以在其中指定Number(例如Double,、、IntegerLong)。

我最终得到了一个像样的类对象,但是注意到有关方法的一些问题。

import java.math.BigDecimal;

@SuppressWarnings("WeakerAccess") // Suppresses weaker access warnings
public class Vector<T extends Number> {

    private T x;
    private T y;

    public Vector() {}

    public Vector(T x, T y) {
        this.x = x;
        this.y = y;
    }

    public T getX() {
        return x;
    }

    public void setX(T x) {
        this.x = x;
    }

    public T getY() {
        return y;
    }

    public void setY(T y) {
        this.y = y;
    }

    public void dislocate(T offsetX, T offsetY) {
        this.setX(addNumbers(getX(), offsetX));
        this.setY(addNumbers(getY(), offsetY));
    }

    public void dislocate(Vector vector) {
        this.setX(addNumbers(getX(), vector.getX()));
        this.setY(addNumbers(getY(), vector.getY()));
    }

    @SuppressWarnings("unchecked") // Suppresses cast unchecked warnings
    private T addNumbers(Number... numbers) {
        BigDecimal bd = new BigDecimal(0);

        for(Number number : numbers) {
            bd = bd.add(new BigDecimal(number.toString()));
        }

        return (T) bd;
    }
}

最后一个方法,即加数方法,会引发未经检查的强制转换警告。在我做了一些研究之后,我发现它的行为很奇怪,因为我对泛型比较陌生,无法正确排除故障。

如何return (T) bd;创建警告?T必须是 a 的一个实例Number,所以它应该可以转换为 a BigDecimal,对吧?

所以我创建了我的小测试方法,

Vector<Double> vec = new Vector<>(1.0, 3.0);
Vector<Double> vec2 = new Vector<>(2.2, 3.9);
vec.dislocate(1.0, 2.7);
System.out.println(vec.getX() + " " + vec.getY());
vec.dislocate(vec2);
System.out.println(vec.getX() + " " + vec.getY());

它工作得很好,打印出来2.0 5.74.2 9.6.

那么问题是当我使用来自 的方法时Double,例如Double#isNaN(). 然后它抛出 ClassCastException, Exception in thread "main" java.lang.ClassCastException: java.base/java.math.BigDecimal cannot be cast to java.base/java.lang.Double

这似乎与人们遇到的其他问题很常见,但是,尽管查看了资源,但我不明白为什么使用这些Double方法会引发错误。对象应该是Double演员后的,对吧?

4

3 回答 3

3

你基本上不能在Java中做这样的事情。 (T) someBigDecimal当且仅当T是 aBigDecimal本身时才会起作用。擦除的工作方式可能会暂时将其隐藏起来,但对于能够添加两个数字或将一个数字相加Number没有什么特别的魔力。

一般来说,在 Java 中没有任何方法可以对不同类型的数字进行泛型化,然后能够对它们进行数字处理。

于 2018-08-25T17:59:17.140 回答
2

为了解决这个问题,您需要提供一些添加Ts 的方法。

例如, aBinaryOperator<T>是接受两个Ts 并返回 a 的东西T。因此,您可以定义要添加的内容,例如:

BinaryOperator<Double> addDoubles = (a, b) -> a+b;
BinaryOperator<BigDecimal> addBigDecimals = (a, b) -> a.add(b);

现在,您实际上需要在创建 Vector 时为其提供 this 的实例,例如作为构造函数参数:

public Vector(BinaryOperator<T> adder) {
  this.adder = adder; // define a field, too.
}

现在使用 BiFunction 来添加数字:

private T addNumbers(T a, T b) {
  return adder.apply(a, b); // or you could just invoke this directly.
}

我简化了你addNumbers总是采用两个参数,因为你只用两个参数调用。一般而言,您需要提供一个“通用零”,即类型 T 的值,该类型为零,或者只是从可变参数数组中的第一个元素开始。

于 2018-08-25T19:22:42.560 回答
1

施法后对象应该是 Double ,对吧?

永远不会,因为强制转换(有或没有泛型)永远不会改变运行时类型。它只是更改您操作的声明类型。

addNumbers()您实际执行取消选中强制转换:BigDecimalto T
编译器会警告您未检查强制转换,但会接受它,因为它BigDecimalT具有上限通配符 :的兼容Number
泛型类实例的包含元素:

private T x;
private T y;

现在参考BigDecimal类型而不是更多Double类型。

于 2018-08-25T18:24:36.037 回答