对于Integer
,Float
,Double
等包装类型,我想对每个操作使用一个通用方法执行加法,减法,乘法和除法等基本算术运算。不包括BigDecimal
和BigInteger
)。
我尝试使用泛型类做以下内容(用于添加)。
public final class GenericClass<E extends Number> {
public E add(E x, E y) {
return x + y; // Compile-time error
}
}
它发出编译时错误,
operator +无法应用于E,E
有没有办法使用这样的通用版本来实现这样的操作?
答案 0 :(得分:10)
不,没有办法做到这一点,否则它将被内置到Java中。类型系统不够强大,无法表达这类事情。
答案 1 :(得分:6)
不,你不能这样做,因为+运算符不是Number类的一部分。你可以做的是创建一个抽象基类并从中扩展:
static void test() {
MyInteger my = new MyInteger();
Integer i = 1, j = 2, k;
k = my.add(i, j);
System.out.println(k);
}
static public abstract class GenericClass<E extends Number> {
public abstract E add(E x, E y);
}
static public class MyInteger extends GenericClass<Integer> {
@Override
public Integer add(Integer x, Integer y) {
return x + y;
}
}
(我将这些类设为静态以便于测试,但您可以删除此修饰符。)
您还可以添加一个抽象函数,该函数将获取并返回参数并返回Number类型的值并覆盖它和子类,但返回值所需的强制转换将失去其实用性。
答案 2 :(得分:2)
我能想到的只是将类型作为未知类型接收并使用实例。
类似的东西:
public class GenericClass<? extends Number>{
public Integer add(? x, ? y){
if(x instance of Integer && y instance of Integer){
//Do your math with Integer class methods help
//Return here
}
return (Interger)null;
}
}
但不确定:/
答案 3 :(得分:1)
通常你不需要这个,因为使用像double或BigDecimal这样的“超级”类型更简单,更有效,它可以代表任何类型的任何值。
注意:double
使用的Integer
空间不到{{1}}的一半,并提及它。
答案 4 :(得分:1)
Number是类类型,因此原始类型运算符不适用。此外,Java don't support基元类型中的泛型,因此您将无法绑定E以仅允许基本类型。
解决这个问题的一种方法是在Add方法中单独处理Number支持的每个基本类型,但我认为这会击败你想要完成的任务。
答案 5 :(得分:1)
正如Afonso所建议的,另一种可能性是创建一个函数并记下所有必需的可能性。以下是一些变体:
// Examples with static functions:
Integer i = addNumbers (1, 2); // OK
Double d = addNumbers (3.0, 4.0); // OK
String s = addObjects ("a", "b"); // OK
//
// Example with a raw type for a class with both method
// (or member function) and static functions:
GenericClass gc = new GenericClass(); // Raw type
//
// Note the error if we don't add a cast:
// i = gc.add(1, 2); // Error: Cannot convert from Number to Integer
//
// Now OK with a cast but with a Type safety warning:
i = (Integer) gc.add (1, 2); // Type safety warning.
i = GenericClass.add2 (1, 2); // OK
i = GenericClass.add3 (1, 2); // OK
//
// Example with an instanciated type for the same class:
GenericClass<Integer> gc1 = new GenericClass<Integer>();
//
// Note that we don't need a cast anymore:
i = gc1.add(1, 2); // Now OK even without casting.
//
i = GenericClass2.add2 (1, 2); // OK
s = GenericClass2.add2 ("a", "b"); // OK
i = GenericClass2.<Integer>add2 (1, 2); // OK.
d = GenericClass2.<Double>add2 (1.0, 2.0); // OK
s = GenericClass2.<String>add2 ("a", "b"); // OK
//
public static<T extends Number> T addNumbers(T x, T y) {
if (x instanceof Integer && y instanceof Integer){
return (T) (Integer) ((Integer)x + (Integer)y);
} else if (x instanceof Double && y instanceof Double){
return (T) (Double) ((Double)x + (Double)y);
} else
return (T)null;
}
//
public static<T> T addObjects(T x, T y) {
if (x instanceof Integer && y instanceof Integer) {
//
// We must add an (Integer) cast because the type of the operation
// "((Integer)x + (Integer)y))" is "int" and not "Integer" and we
// cannot directly convert from "int" to "T". Same thing for Double
// but not for the type String:
//
return (T) (Integer) ((Integer)x + (Integer)y);
} else if (x instanceof Double && y instanceof Double) {
return (T) (Double) ((Double)x + (Double)y);
} else if (x instanceof String && y instanceof String) {
return (T) ((String)x + (String)y);
} else
return (T)null;
}
//
static class GenericClass<T extends Number> {
public T add(T x, T y) {
if (x instanceof Integer && y instanceof Integer) {
return (T) (Integer) ((Integer)x + (Integer)y);
} else if (x instanceof Double && y instanceof Double) {
return (T) (Double) ((Double)x + (Double)y);
} else
return (T)null;
}
//
// The type <T> here is NOT the same as the one for the class.
// We should rename it in order to make this clearer. See add3()
// for an example of this.
public static<T> T add2(T x, T y) {
if (x instanceof Integer && y instanceof Integer) {
return (T) (Integer) ((Integer)x + (Integer)y);
} else if (x instanceof Double && y instanceof Double) {
return (T) (Double) ((Double)x + (Double)y);
} else if (x instanceof String && y instanceof String) {
return (T) ((String)x + (String)y);
} else
return (T)null;
}
//
// The type here is not the same as the one for the class
// so we have renamed it from <T> to <N> to make it clearer.
public static<N extends Number> N add3(N x, N y) {
if (x instanceof Integer && y instanceof Integer) {
return (N) (Integer) ((Integer)x + (Integer)y);
} else if (x instanceof Double && y instanceof Double) {
return (N) (Double) ((Double)x + (Double)y);
} else
return (N)null;
}
}
答案 6 :(得分:1)
正如Louis Wasserman指出的那样,在Java中无法做到这一点。但是,可以通过使用一些不那么棘手的编程来呈现解决方案。让我们从一个我喜欢的解决方案开始:SylvainL回答这个问题。但是,我相信我们可以退一步处理 Number
的每个类型。如果查看Java API,您可以注意到Number
的任何子类都必须覆盖几个抽象方法;即intValue()(以及其他人)。使用这些方法,我们可以利用多态性来发挥其真正的潜力。从SylvainL的答案中得到我们的课程,我们可以生成一个新的课程:
public final class EveryNumberClass<E extends Number>
{
public static int add(E x, E y)
{
return x.intValue() + y.intValue();
}
public static int subract(E x, E y)
{
return x.intValue() - y.intValue();
}
}
这些操作可以扩展为乘法和除法,虽然不限于Integer
s,但可以用来接受任何 Number
并表达给定的适当行为一个手术。虽然intValue()
方法的行为可能不会返回Number
的整数表示,但它肯定会这样做(我查看了most numbers的源代码,包括atomic ones和math ones)。当intValue()
返回意外行为时会出现唯一的问题,这可能发生在原子序数,用户定义的Number
或大数字被迫缩小的情况下。如果这些是你的项目(或你的图书馆)的问题,我会考虑使用long
值,或参考SylvainL的回答。
答案 7 :(得分:0)
希望这可能会帮助那些有兴趣使用Generics进行算术运算的人
public < T > T add( T a, T b)
{
return (T) (String.valueOf((Integer.parseInt(a.toString()) + Integer.parseInt(b.toString()))));
}
public < T > T sub(T a, T b)
{
return (T) (String.valueOf((Integer.parseInt(a.toString()) - Integer.parseInt(b.toString()))));
}
public < T> T mul(T a,T b)
{
return (T) (String.valueOf((Integer.parseInt(a.toString()) * Integer.parseInt(b.toString()))));
}
public < T > T div (T a, T b)
{
return (T) (String.valueOf((Integer.parseInt(a.toString()) / Integer.parseInt(b.toString()))));
}
public < T > T mod(T a, T b)
{
return (T) (String.valueOf((Integer.parseInt(a.toString()) % Integer.parseInt(b.toString()))));
}
public < T > T leftShift(T a, T b)
{
return (T) (String.valueOf((Integer.parseInt(a.toString()) << Integer.parseInt(b.toString()))));
}
public < T > T rightShift(T a, T b)
{
return (T) (String.valueOf((Integer.parseInt(a.toString()) >> Integer.parseInt(b.toString()))));
}
答案 8 :(得分:0)
enum Operator {
ADD, SUBTRACT, DIVIDE, MULTIPLY;
}
public class GenericArithmeticOperation {
public GenericArithmeticOperation() {
// default constructor
}
public <E> Integer doArithmeticOperation(E operand1, E operand2, Operator operator) {
if (operand1 instanceof Integer && operand2 instanceof Integer) {
switch (operator) {
case ADD:
return (Integer) ((Integer) operand1 + (Integer) operand2);
case SUBTRACT:
return (Integer) ((Integer) operand1 - (Integer) operand2);
case MULTIPLY:
return (Integer) ((Integer) operand1 * (Integer) operand2);
case DIVIDE:
return (Integer) ((Integer) operand1 / (Integer) operand2);
}
}
throw new RuntimeException(operator + "is unsupported");
}
public static void main(String[] args) {
GenericArithmeticOperation genericArithmeticOperation = new GenericArithmeticOperation();
System.out.println(genericArithmeticOperation.doArithmeticOperation(10, 6, Operator.ADD));
System.out.println(genericArithmeticOperation.doArithmeticOperation(10, 6, Operator.SUBTRACT));
System.out.println(genericArithmeticOperation.doArithmeticOperation(4, 6, Operator.MULTIPLY));
System.out.println(genericArithmeticOperation.doArithmeticOperation(21, 5, Operator.DIVIDE));
}
}
答案 9 :(得分:0)
这可能会对您有所帮助。只需使用您要支持的类型创建不同的操作。而且,您现在可以对泛型使用算术运算。无需昂贵的String
解析。
static <V extends Number> V add(V lhs, V rhs)
{
if (lhs.getClass() == Integer.class && rhs.getClass() == Integer.class)
return (V) Integer.valueOf(((Integer) lhs) + ((Integer) rhs));
if (lhs.getClass() == Double.class && rhs.getClass() == Double.class)
return (V) Double.valueOf(((Double) lhs) + ((Double) rhs));
if (lhs.getClass() == Float.class && rhs.getClass() == Float.class)
return (V) Float.valueOf(((Float) lhs) + ((Float) rhs));
if (lhs.getClass() == Long.class && rhs.getClass() == Long.class)
return (V) Long.valueOf(((Long) lhs) + ((Long) rhs));
throw new IllegalArgumentException("unsupported type: " + lhs.getClass());
}
valueOf()
将结果包装到Integer
对象中,然后可以将其强制转换为V。
要提高性能,请按使用量降序排列。
开关不起作用,因为您必须输入通用或Class<?>
,这对于switch语句是不可接受的。