Acabo de recibir en los genéricos con Java, por lo que he creado un proyecto pequeño para mí. Yo quería hacer un Vector / punto donde se puede especificar el Number
(por ejemplo Double
, Integer
, Long
, etc.).
Que terminó siendo un objeto de clase decente para ella, sin embargo se dio cuenta de algunas cuestiones relacionadas con los métodos.
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;
}
}
El último método, que es el método de añadir números, lanza una advertencia fundido sin marcar. Después de que hice un poco de investigación, me di cuenta de que se estaba comportando de manera extraña, debido a los genéricos, que estoy relativamente nuevo en e incapaz de solucionar problemas adecuadamente.
¿Qué pasa con return (T) bd;
crea la advertencia? T
tiene que ser una instancia de una Number
, por lo que debe ser capaz de fundición de una BigDecimal
, ¿verdad?
Así que creé mi método de prueba,
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());
Funciona muy bien, imprimir 2.0 5.7
y 4.2 9.6
.
El problema, entonces, es cuando se utiliza un método de Double
, al igual que Double#isNaN()
. A continuación, lanza el ClassCastException, Exception in thread "main" java.lang.ClassCastException: java.base/java.math.BigDecimal cannot be cast to java.base/java.lang.Double
.
Esto parecía bastante común con otras cuestiones gente ha tenido con esto, sin embargo, a pesar de ir a través de los recursos, que no entiendo por qué el error es lanzado utilizando los Double
métodos. El objeto debe ser una Double
después de la fundición, ¿verdad?
Para solucionar esto, es necesario proporcionar algún medio de la adición de T
s.
Por ejemplo, una BinaryOperator<T>
es algo que se lleva en dos T
s, y devuelve una T
. Por lo tanto, se puede definir aquellos para añadir, por ejemplo:
BinaryOperator<Double> addDoubles = (a, b) -> a+b;
BinaryOperator<BigDecimal> addBigDecimals = (a, b) -> a.add(b);
Ahora, en realidad se necesita suministrar una instancia de este a su vector cuando se crea, por ejemplo, como un parámetro de constructor:
public Vector(BinaryOperator<T> adder) {
this.adder = adder; // define a field, too.
}
Y ahora utilizar el BiFunction añadir los números:
private T addNumbers(T a, T b) {
return adder.apply(a, b); // or you could just invoke this directly.
}
Que simplifica su addNumbers
siempre tomar dos parámetros, ya que sólo de invocación con dos parámetros. Para hacerlo de forma genérica, más te sea necesario para proporcionar un "cero genérico", es decir, un valor de tipo T, que es cero para ese tipo, o simplemente para iniciar desde el primer elemento de la matriz varargs.