Eu só entrou em genéricos com Java, então eu configurar um projeto pouco para mim. Eu queria fazer um vetor / Point, onde você pode especificar o Number
(por exemplo Double
, Integer
, Long
, etc).
Eu acabei ficando um objeto de classe decente para ele, no entanto notado alguns problemas quanto aos 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;
}
}
O último método, que é o método a adição de números, lança um aviso fundido não verificada. Depois que eu fiz alguma pesquisa, descobri que estava se comportando estranhamente, devido aos genéricos, o que eu sou relativamente novo e incapaz de adequadamente solucionar problemas.
Que tal return (T) bd;
cria o aviso? T
Tem que haver uma instância de um Number
, por isso deve ser cast-capaz de um BigDecimal
, certo?
Então, eu criei o meu método de teste pouco,
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());
Ele funciona muito bem, imprimir 2.0 5.7
e 4.2 9.6
.
O problema, então, é quando eu uso um método de Double
, como Double#isNaN()
. Em seguida, ele joga fora o ClassCastException, Exception in thread "main" java.lang.ClassCastException: java.base/java.math.BigDecimal cannot be cast to java.base/java.lang.Double
.
Este comum muito parecia com outras questões pessoas tiveram com este, no entanto, apesar de ir sobre os recursos, eu não entendo por que o erro é lançado utilizando os Double
métodos. O objeto deve ser um Double
após o elenco, certo?
Para resolver isso, você precisa fornecer alguns meios de adição de T
s.
Por exemplo, um BinaryOperator<T>
é algo que leva em dois T
s, e retorna um T
. Assim, você pode definir os para adicionar, por exemplo:
BinaryOperator<Double> addDoubles = (a, b) -> a+b;
BinaryOperator<BigDecimal> addBigDecimals = (a, b) -> a.add(b);
Agora, você realmente precisa fornecer uma instância desse ao seu Vector ao criá-lo, por exemplo, como um parâmetro de construtor:
public Vector(BinaryOperator<T> adder) {
this.adder = adder; // define a field, too.
}
E agora usar o BiFunction para adicionar os números:
private T addNumbers(T a, T b) {
return adder.apply(a, b); // or you could just invoke this directly.
}
I simplificou sua addNumbers
sempre tomar dois parâmetros, já que você só invocar com dois parâmetros. Para fazê-lo genericamente, você quer necessidade de fornecer um "zero genérica", ou seja, um valor do tipo T que é zero para esse tipo, ou simplesmente para começar a partir do primeiro elemento na matriz varargs.