简体   繁体   English

使用泛型删除未选中的强制转换警告

[英]Removing unchecked cast warnings with generics

I just got into generics with Java, so I set up a little project for myself. 我刚刚使用Java进入泛型,所以我为自己设置了一个小项目。 I wanted to make a Vector / Point where you could specify the Number (eg Double , Integer , Long , etc). 我想制作一个Vector / Point,你可以指定Number (例如DoubleIntegerLong等)。

I ended up getting a decent class object for it, however noticed some issues regarding the methods. 我最终得到了一个体面的类对象,但是注意到有关这些方法的一些问题。

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;
    }
}

The final method, which is the adding numbers method, throws an unchecked cast warning. 最后一个方法,即添加数字方法,抛出未经检查的强制转换警告。 After I did some research, I figured out it was behaving oddly due to generics, which I'm relatively new in and unable to properly troubleshoot. 在我做了一些研究之后,我发现它由于泛型而表现得很奇怪,我是相对较新的并且无法正确排除故障。

What about return (T) bd; return (T) bd;怎么样return (T) bd; creates the warning? 创建警告? T has to be an instance of a Number , so it should be cast-able to a BigDecimal , right? T必须是Number的实例,所以它应该可以转换为BigDecimal ,对吧?

So I created my little testing method, 所以我创建了我的小测试方法,

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());

It works great, printing out 2.0 5.7 and 4.2 9.6 . 效果很好,打印出2.0 5.74.2 9.6

The issue then, is when I use a method from Double , like Double#isNaN() . 那么问题是,当我使用Double的方法时,比如Double#isNaN() It then throws out the ClassCastException, Exception in thread "main" java.lang.ClassCastException: java.base/java.math.BigDecimal cannot be cast to java.base/java.lang.Double . 然后抛出ClassCastException, Exception in thread "main" java.lang.ClassCastException: java.base/java.math.BigDecimal cannot be cast to java.base/java.lang.Double抛出Exception in thread "main" java.lang.ClassCastException: java.base/java.math.BigDecimal cannot be cast to java.base/java.lang.Double

This seemed pretty common with other issues people have had with this, however, despite going over the resources, I don't understand why the error is thrown using the Double methods. 这似乎与人们对此有的其他问题相当普遍,但是,尽管超出了资源,我不明白为什么使用Double方法抛出错误。 The object should be a Double after the cast, right? 演员之后对象应该是Double ,对吧?

You basically can't do things like this in Java. 你基本上不能用Java做这样的事情。 (T) someBigDecimal will work if and only if T is a BigDecimal itself. (T) someBigDecimal当且仅当TBigDecimal本身时才会起作用。 The way erasure works may hide that from you temporarily, but Number has no special magic about being able to add two Numbers or cast one to another. 擦除的工作方式可能会暂时隐藏起来,但是Number 没有特别的魔力可以添加两个数字或相互转换。

In general, there's not really any way to generify in Java over different kinds of numbers and then be able to do numerical things with them. 一般来说,没有任何方法可以在Java中通过不同类型的数字进行生成,然后能够使用它们进行数字化处理。

To solve this, you need to provide some means of adding T s. 要解决这个问题,您需要提供一些添加T s的方法。

For example, a BinaryOperator<T> is something that takes in two T s, and returns a T . 例如, BinaryOperator<T>需要两个T s,并返回一个T So, you can define ones for adding, for example: 因此,您可以定义要添加的内容,例如:

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

Now, you actually need to supply an instance of this to your Vector when you create it, eg as a constructor parameter: 现在,您实际上需要在创建Vector时向其提供此实例,例如作为构造函数参数:

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

And now use the BiFunction to add the numbers: 现在使用BiFunction添加数字:

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

I simplified your addNumbers always to take two parameters, since you only invoke with two parameters. 我简化了你的addNumbers总是带两个参数,因为你只用两个参数调用。 To do it generically, you'd either need to provide a "generic zero", ie a value of type T which is zero for that type, or simply to start from the first element in the varargs array. 要做到这一点,你需要提供一个“通用零”,即类型T的值,对于该类型为零,或者只是从varargs数组中的第一个元素开始。

The object should be a Double after the cast, right? 演员之后对象应该是Double,对吧?

Never because casting (with or without generics) never changes the runtime type. 从不因为强制转换(使用或不使用泛型)从不更改运行时类型。 It just changes the declared type that you manipulate. 它只是更改您操作的声明类型。

In addNumbers() you actually perform an uncheck casts : BigDecimal to T . addNumbers()您实际上执行取消选中转换: BigDecimalT
The compiler warns you of the uncheck cast but accepts it as BigDecimal is compatible with T that has as upper-bounded wildcard : Number . 编译器会警告您取消选中转换,但接受它,因为BigDecimal与具有上限通配符的Number T兼容: Number
The contained elements of the generic class instance : 泛型类实例的包含元素:

private T x;
private T y;

refer now the BigDecimal type and no more Double type. 现在引用BigDecimal类型而不再是Double类型。

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

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