删除带有泛型的未经检查的转换警告



我刚刚开始使用Java泛型,所以我为自己建立了一个小项目。我想制作一个矢量/点,您可以在其中指定Number(例如DoubleIntegerLong等(。

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

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必须是Number的实例,所以它应该是可以投射到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后,对吧?

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

一般来说,实际上没有任何方法可以在Java中对不同类型的数字进行泛化,然后能够用它们做数字的事情。

要解决此问题,您需要提供一些添加T的方法。

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

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

现在,您实际上需要在创建 Vector 时向它提供一个实例,例如作为构造函数参数:

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类型的值,对于该类型为零,要么只是从varargs数组中的第一个元素开始。

投掷后的对象应该是双倍的,对吧?

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

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

private T x;
private T y;

现在参考BigDecimal类型,不再Double类型。

最新更新