Удаление непроверенных кастовых предупреждений с помощью дженериков - PullRequest
0 голосов
/ 25 августа 2018

Я только начал изучать дженерики с Java, поэтому я создал небольшой проект для себя. Я хотел создать Вектор / Точку, где вы могли бы указать Number (например, Double, Integer, Long и т. Д.).

В итоге я получил достойный объект класса, но заметил некоторые проблемы, связанные с методами.

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.7 и 4.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 после приведения, верно?

Ответы [ 3 ]

0 голосов
/ 25 августа 2018

Объект должен быть двойным после приведения, верно?

Никогда, потому что приведение (с или без дженериков) никогда не меняет тип времени выполнения.Это просто изменяет объявленный тип, которым вы манипулируете.

В addNumbers() вы фактически выполняете отмены проверки: BigDecimal до T.
Компилятор предупреждает вас о снятии проверки, но принимает его, поскольку BigDecimal совместимо с T, чтоимеет подстановочный знак с ограничением сверху: Number.
Содержащиеся элементы экземпляра универсального класса:

private T x;
private T y;

теперь относятся к типу BigDecimal, а не к типу Double.

0 голосов
/ 25 августа 2018

Чтобы решить эту проблему, вам нужно предоставить некоторые способы добавления T с.

Например, BinaryOperator<T> - это то, что занимает две T с и возвращает T,Таким образом, вы можете определить их для добавления, например:

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

Теперь вам действительно нужно предоставить экземпляр этого в ваш Вектор при его создании, например, в качестве параметра конструктора:

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.

0 голосов
/ 25 августа 2018

Вы не можете делать такие вещи в Java. (T) someBigDecimal будет работать тогда и только тогда, когда T само по себе BigDecimal. То, как работает стирание, может временно скрыть это от вас, но Number имеет нет особой магии, заключающейся в возможности добавлять два номера или накладывать одно на другое.

В общем, на самом деле нет какого-либо способа генерировать в Java различные типы чисел, а затем иметь возможность делать числовые вещи с ними.

...