I want to compare to variables, both of type T extends Number. Now I want to know which of the two variables is greater than the other or equal. Unfortunately I don't know the exact type yet, I only know that it will be a subtype of java.lang.Number. How can I do that?
EDIT: I tried another workaround using TreeSets, which actually worked with natural ordering (of course it works, all subclasses of Number implement Comparable except for AtomicInteger and AtomicLong). Thus I'll lose duplicate values. When using Lists, Collection.sort() will not accept my list due to bound mismatchs. Very unsatisfactory.
This should work for all classes that extend Number, and are Comparable to themselves. By adding the & Comparable you allow to remove all the type checks and provides runtime type checks and error throwing for free when compared to Sarmun answer.
class NumberComparator<T extends Number & Comparable> implements Comparator<T> {
public int compare( T a, T b ) throws ClassCastException {
return a.compareTo( b );
}
}
A working (but brittle) solution is something like this:
class NumberComparator implements Comparator<Number> {
public int compare(Number a, Number b){
return new BigDecimal(a.toString()).compareTo(new BigDecimal(b.toString()));
}
}
It's still not great, though, since it counts on toString returning a value parsable by BigDecimal (which the standard Java Number classes do, but which the Number contract doesn't demand).
Edit, seven years later: As pointed out in the comments, there are (at least?) three special cases toString can produce that you need to take into regard:
Infinity, which is greater than everything, except itself to which it is equal
-Infinity, which is less than everything, except itself to which it is equal
NaN, which is extremely hairy/impossible to compare since all comparisons with NaN result in false, including checking equality with itself.
After having asked a similar question and studying the answers here, I came up with the following. I think it is more efficient and more robust than the solution given by gustafc:
public int compare(Number x, Number y) {
if (isSpecial(x) || isSpecial(y))
return Double.compare(x.doubleValue(), y.doubleValue());
else
return toBigDecimal(x).compareTo(toBigDecimal(y));
}
private static boolean isSpecial(Number x) {
var specialDouble = x instanceof Double d
&& (Double.isNaN(d) || Double.isInfinite(d));
var specialFloat = x instanceof Float f
&& (Float.isNaN(f) || Float.isInfinite(f));
return specialDouble || specialFloat;
}
private static BigDecimal toBigDecimal(Number number) {
if (number instanceof BigDecimal d)
return d;
if (number instanceof BigInteger i)
return new BigDecimal(i);
if (number instanceof Byte || number instanceof Short
|| number instanceof Integer || number instanceof Long)
return new BigDecimal(number.longValue());
if (number instanceof Float || number instanceof Double)
return new BigDecimal(number.doubleValue());
try {
return new BigDecimal(number.toString());
} catch(NumberFormatException e) {
throw new RuntimeException("The given number (\"" + number + "\" of class " + number.getClass().getName() + ") does not have a parsable string representation", e);
}
}
One solution that might work for you is to work not with T extends Number but with T extends Number & Comparable. This type means: "T can only be set to types that implements both the interfaces."
That allows you to write code that works with all comparable numbers. Statically typed and elegant.
This is the same solution that BennyBoy proposes, but it works with all kinds of methods, not only with comparator classes.
public static <T extends Number & Comparable<T>> void compfunc(T n1, T n2) {
if (n1.compareTo(n2) > 0) System.out.println("n1 is bigger");
}
public void test() {
compfunc(2, 1); // Works with Integer.
compfunc(2.0, 1.0); // And all other types that are subtypes of both Number and Comparable.
compfunc(2, 1.0); // Compilation error! Different types.
compfunc(new AtomicInteger(1), new AtomicInteger(2)); // Compilation error! Not subtype of Comparable
}
The most "generic" Java primitive number is double, so using simply
a.doubleValue() > b.doubleValue()
should be enough in most cases, but... there are subtle issues here when converting numbers to double. For example the following is possible with BigInteger:
BigInteger a = new BigInteger("9999999999999992");
BigInteger b = new BigInteger("9999999999999991");
System.out.println(a.doubleValue() > b.doubleValue());
System.out.println(a.doubleValue() == b.doubleValue());
results in:
false
true
Although I expect this to be very extreme case this is possible. And no - there is no generic 100% accurate way. Number interface have no method like exactValue() converting to some type able to represent number in perfect way without loosing any information.
Actually having such perfect numbers is impossible in general - for example representing number Pi is impossible using any arithmetic using finite space.
What about this one? Definitely not nice, but it deals with all necessary cases mentioned.
public class SimpleNumberComparator implements Comparator<Number>
{
#Override
public int compare(Number o1, Number o2)
{
if(o1 instanceof Short && o2 instanceof Short)
{
return ((Short) o1).compareTo((Short) o2);
}
else if(o1 instanceof Long && o2 instanceof Long)
{
return ((Long) o1).compareTo((Long) o2);
}
else if(o1 instanceof Integer && o2 instanceof Integer)
{
return ((Integer) o1).compareTo((Integer) o2);
}
else if(o1 instanceof Float && o2 instanceof Float)
{
return ((Float) o1).compareTo((Float) o2);
}
else if(o1 instanceof Double && o2 instanceof Double)
{
return ((Double) o1).compareTo((Double) o2);
}
else if(o1 instanceof Byte && o2 instanceof Byte)
{
return ((Byte) o1).compareTo((Byte) o2);
}
else if(o1 instanceof BigInteger && o2 instanceof BigInteger)
{
return ((BigInteger) o1).compareTo((BigInteger) o2);
}
else if(o1 instanceof BigDecimal && o2 instanceof BigDecimal)
{
return ((BigDecimal) o1).compareTo((BigDecimal) o2);
}
else
{
throw new RuntimeException("Ooopps!");
}
}
}
This should work for all classes that extend Number, and are Comparable to themselves.
class NumberComparator<T extends Number> implements Comparator<T> {
public int compare(T a, T b){
if (a instanceof Comparable)
if (a.getClass().equals(b.getClass()))
return ((Comparable<T>)a).compareTo(b);
throw new UnsupportedOperationException();
}
}
if(yourNumber instanceof Double) {
boolean greaterThanOtherNumber = yourNumber.doubleValue() > otherNumber.doubleValue();
// [...]
}
Note: The instanceof check isn't necessarily needed - depends on how exactly you want to compare them. You could of course simply always use .doubleValue(), as every Number should provide the methods listed here.
Edit: As stated in the comments, you will (always) have to check for BigDecimal and friends. But they provide a .compareTo() method:
if(yourNumber instanceof BigDecimal && otherNumber instanceof BigDecimal) {
boolean greaterThanOtherNumber = ((BigDecimal)yourNumber).compareTo((BigDecimal)otherNumber) > 0;
}
You can simply use Number's doubleValue() method to compare them; however you may find the results are not accurate enough for your needs.
Let's assume that you have some method like:
public <T extends Number> T max (T a, T b) {
...
//return maximum of a and b
}
If you know that there are only integers, longs and doubles can be passed as parameters then you can change method signature to:
public <T extends Number> T max(double a, double b) {
return (T)Math.max (a, b);
}
This will work for byte, short, integer, long and double.
If you presume that BigInteger's or BigDecimal's or mix of floats and doubles can be passed then you cannot create one common method to compare all these types of parameters.
If your Number instances are never Atomic (ie AtomicInteger) then you can do something like:
private Integer compare(Number n1, Number n2) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
Class<? extends Number> n1Class = n1.getClass();
if (n1Class.isInstance(n2)) {
Method compareTo = n1Class.getMethod("compareTo", n1Class);
return (Integer) compareTo.invoke(n1, n2);
}
return -23;
}
This is since all non-Atomic Numbers implement Comparable
EDIT:
This is costly due to reflection: I know
EDIT 2:
This of course does not take of a case in which you want to compare decimals to ints or some such...
EDIT 3:
This assumes that there are no custom-defined descendants of Number that do not implement Comparable (thanks #DJClayworth)
In my use case, I was looking for a general Comparator that works with the autoboxed primitives (64 bit max precision), not arbitrary precision types like BigInteger and BigDecimal. Here's a first shot at it..
public class PrimitiveComparator implements Comparator<Number> {
#Override
public int compare(Number a, Number b) {
if (a == b)
return 0;
double aD = a.doubleValue();
double bD = b.doubleValue();
int comp = Double.compare(aD, bD);
if (comp == 0 && inLongBounds(aD))
comp = Long.compare(a.longValue(), b.longValue());
return comp;
}
private boolean inLongBounds(double value) {
return
Double.compare(value, Long.MAX_VALUE) <= 0 &&
Double.compare(value, Long.MIN_VALUE) >= 0;
}
}
The objective is to be able to compare mixed types (e.g. Floats against Longs). This should also work with those AtomicXxx types (or any hand rolled Number subclass that uses no more than 64 bits).
In this ordering, btw, Double.NaN > Double.POSITVE_INFINITY > { everything else }.
Related
See the tiny method below. The boo1 = ... line goes fine, probably as it does object ID comparison. The second boo2 = ... line gives a compile error "Operator > cannot be applied to T,T". I don't understand why. After all T extends Number (as you can see in the method signature), so comparisons like > should be possible. What am I doing wrong?
public static <T extends Number> int[] where(T[] arr, T val) {
if (arr == null || arr.length == 0) return null;
boolean boo1 = arr[0] == val; //Compiles happily, as does "!="
boolean boo2 = arr[0] > val; //Doesn't compile (nor does ">=", "<", "<="
return null;
}
What am I doing wrong?
You're assuming that the relational operators support Number operands; they don't. Only Numbers that box primitive types (e.g. Integer, Long) do; others such as BigInteger don't.
You can add an additional bound to T to require it to be Comparable:
<T extends Number & Comparable<T>>
And you can pass in any types that are both Numbers and are Comparable: this includes Integer, Long, BigInteger etc.
Then you can use:
arr[0].compareTo(val) > 0
(but you might care to watch out for nulls).
Also, you shouldn't be using == and != to check for equality/inequality: use equals instead:
arr[0].equals(val) // Instead of ==
!arr[0].equals(val) // Instead of !=
You can, alternatively, use arr[0].compareTo(val) ==/!= 0. That may be better, in fact, because e.g. BigInteger and BigDecimal have equals methods that consider scale, so [1.00].equals([1.0]) is false, whereas [1.00].compareTo([1.0]) == 0 is true. Ultimately, it depends on what you're trying to achieve as to which way to choose.
Try it this way:
public static <T extends Number> int[] where(T[] arr, T val) {
if (arr == null || arr.length == 0) return null;
boolean boo1 = arr[0] == val; //Compiles happily, as does "!="
boolean boo2 = arr[0].intValue() > val.intValue();
// Or doubleValue()
return null;
}
I'm taking a java course on lynda.com, and the course was explaining how to "cast" a variable if you want to shorten it.
WIDENING: Widening a variable is making it larger (e.g. int int1 = 4030; long long1 = int1;)
SHORTENING: Shortening a variable is making it smaller, and requires special syntax. (e.g. int int2 = 5024; short int3 = (short) int2;).
So, my question is, why would anyone want to do this? What's the advantage? If you know you'll need to widen a variable at some point, why don't you just start it as that variable? And why would you want to make your data type smaller if you're shortening? If you think that it could be used like this:
byte byte1 = 127;
byte1++;
if (byte1 > 127) {
short short1 = byte1;
}
(I know that this would give an error message, but you get the rough idea.)
You could do this, but why? It wouldn't save data, because it just adds more lines of code which would take up that data.
There are several reasons you might want to "shorten" a variable.
One is that an API or library you are working with requires data to be passed that is of a "shorter" type than the type you were using in your code.
Another is to save space. For example, if I only need to store a two digit number, using a long would be overkill as it would use much more system memory than is needed. This isn't something you normally need to worry too much about, but it could be an issue on certain systems or for very large projects.
There may be even more reasons; these are just a few examples.
You absolutely can't avoid this if you must pass a value received from a function that is out of your control into another function that is also out of your control:
package abc.def:
public class Foo {
public static long foo() { ... }
}
package xyz.qwerty:
public class Bar {
public static void bar(int n) { ... }
}
Your code:
import xyz.qwerty.Bar;
import abc.def.Foo;
...
Bar.bar((int)Foo.foo());
There may be intermediate variables temporarily holding the value received from Foo.foo() before it gets to Bar.bar() but that doesn't eliminate the inevitable need for converting from one type to another, which must happen somewhere in between.
Imagine you want to implement a min(...) method to calculate the minimum of two numbers. You could simply write:
public static double min(double lhs, double rhs) {
if (lhs >= rhs) {
return (lhs);
}
// else if (rhs > lhs) {
return (rhs);
// }
and through auto-casting (or what you refer to as "widening"), you could call this method with all primitives in Java. The downside, however, is that the result would always be double and if you want to save it as an int, you would have to downcast the result:
int i1 = 0;
int i2 = 100;
int max = (int) max(i1, i2);
It would be nice if the method returned an int iff. both parameters are int, a long iff. one parapeter is long and the other parameter is long or int and so on. This would result in the following code1:
public static int min(int lhs, int rhs) {
if (lhs >= rhs) {
return (lhs);
}
// else if (rhs > lhs) {
return (rhs);
// }
}
public static long min(long lhs, long rhs) {
if (lhs >= rhs) {
return (lhs);
}
// else if (rhs > lhs) {
return (rhs);
// }
}
public static float min(float lhs, float rhs) {
if (lhs >= rhs) {
return (lhs);
}
// else if (rhs > lhs) {
return (rhs);
// }
public static double min(double lhs, double rhs) {
if (lhs >= rhs) {
return (lhs);
}
// else if (rhs > lhs) {
return (rhs);
// }
}
Then you could write:
int i = 0;
long g = 1L;
float f = 2f;
double d = 3.0;
int intMax = max(i, i);
long longMax = max(i, g);
float floatMax = max(i, f);
double doubleMax = max(l, d);
Through the means of autocasting and method overloading, the most specific method will be called2,3.
1 You culd write those methods for byte, short and charas well. I would not recommend doing so since all arithemtic operations in Java return at least something of type int (e.g. byte + byte will return an int). This is due to the fact that the JVM does not know of the primitives boolean, char, byte and short, they are represented as int (see JLS §2.11.1).
2 The exact behaviour is specified in JLS, §15.12.2.5
3 This is actually the same mechanism used forjava.lang.Math's implementation of min(...), its implementation is slightly different.
I'm trying to make a method that accepts a number of any primitive type (either byte, int, float, long, double or short). Then after a certain checking method either returns that very same number (i.e., for example, it accepts double and returns double) or it returns a zero.
So far I've come to this:
<AnyNumType> AnyNumType cancelAdd(AnyNumType val, String point) {
if (checkModuleMatchAndOff(point) == true) return 0;
else return val;
}
But I get a type mismatch error (with 0 underlined and explained: "Cannot convert from int to AnyNumType"). Is there a way to cope with this zero problem? I intend to use this method in equations so I really need it to return primitive types.
EDIT: Thank you very much for all your replies, guys!
No, you're not accepting any primitive type; you're accepting any object type. You may think you're accepting primitive types, but Java generics can use only reference types. Your values are being boxed when passed into this method. You could pass a String in as val.
That should indicate why 0 can't be converted to AnyNumType -- AnyNumType can be any reference type.
The best way to accept any primitive type is to have overloads for every primitive type, like many methods in the core library do. You can return the equivalent of 0 in each overload, e.g.
byte cancelAdd(byte val, String point)
{
if (checkModuleMatchAndOff(point) == true) return (byte) 0;
else return val;
}
The overloads for the other primitive types will look very similar.
You could change your method type to:
<AnyNumType extends Number> AnyNumType cancelAdd(AnyNumType val, String point);
I don't know exactly what you want do do inside the method, but this should allow you to pass in primitives via auto-boxing.
But it is not generally possible to use generics for primitive types in Java, autoboxing is, I think, the only way.
Returning a value other than the object you got in is possible, but you'd need some ugly reflection and casts, unfortunately. And it would not support all possible types.
(Maybe there is some library for this somewhere?)
For any doubters out there, here's sample code fresh out of Eclipse, without compilation errors, that demonstrates this:
public class NumberGenerix {
#SuppressWarnings("unchecked")
public static <Any extends Number> Any f(Any x) {
Class<?> k = x.getClass();
if (k == Integer.class) {
return (Any) Integer.valueOf(0);
} else if (k == Double.class) {
return (Any) Double.valueOf(0.0);
} else if (k == Long.class) {
return (Any) Long.valueOf(0L);
} else {
// and so on.
throw new RuntimeException("unsupported number type: " + k);
}
}
public static void main(String...args) {
Integer a = f(42);
System.out.println("a == " + a);
Long b = f(42L);
System.out.println("b == " + b);
Double c = f(42.0);
System.out.println("c == " + c);
}
}
Is it possible to write a single method total to do a sum of all elements of an ArrayList, where it is of type <Integer> or <Long>?
I cannot just write
public long total(ArrayList<Integer> list)
and
public long total(ArrayList<Long> list)
together as there will be an error of erasure, and Integer does not automatically extends to Long and vice versa... but the code inside is identical!
Yes, you can implement such a method, since both Integer and Long extend Number. For example you can use a wildcard type for the list element type:
public static long total(List<? extends Number> list) {
long sum = 0;
for (Number n : list) {
if (!(n instanceof Byte || n instanceof Short || n instanceof Integer || n instanceof Long)) {
throw new IllegalArgumentException();
}
sum += n.longValue();
}
return sum;
}
This only works for the integral types however, since the sum variable and the return value are of type long.
Ideally you would like to be able to also use the method with Floats and Doubles and return an object of the same type as the list element type, but this is not easy to do for two reasons:
The only thing you can do with a Number is to get its value as one of the primitive number types. You can not sum two of them in a number dependent way.
It is not possible to create a 0-object of the right class.
EDIT: Much later...
Just for fun, lets do this in a nice way for Java. The thing you have to do is to manually provide the two operations mentioned above. A kind of value with two such operation is usually called a monoid in the context of algebra and functional programming.
The problem can be solved by creating objects that represent the monoid operations:
interface MonoidOps<T> {
T id();
T op(T o1, T o2);
}
The total method can now be implemented to take an object of this type in addition to the list:
public static <T> T total(List<T> list, MonoidOps<T> ops) {
T sum = ops.id();
for (T e : list) {
sum = ops.op(e, sum);
}
return sum;
}
To provide MonoidOps implementations for the numeric classes, lets create a simple helper class:
class SimpleMonoidOps<T> implements MonoidOps<T> {
private final T idElem;
private final BinaryOperator<T> operation;
public SimpleMonoidOps(T idElem, BinaryOperator<T> operation) {
this.idElem = idElem;
this.operation = operation;
}
public T id() {
return idElem;
}
public T op(T o1, T o2) {
return operation.apply(o1, o2);
}
}
The MonoidOps implementations can now be written neatly like this:
static final MonoidOps<Integer> INT_MONOID_OPS = new SimpleMonoidOps<>(0, Integer::sum);
static final MonoidOps<Double> DOUBLE_MONOID_OPS = new SimpleMonoidOps<>(0.0, Double::sum);
And the total method would be called like this:
int sum = total(Arrays.asList(1, 2, 3), INT_MONOID_OPS);
You can also use streams in Java 8
public static <T extends Number> long sumList(List<T> list)
{
return list.stream().mapToLong(a -> a.longValue()).sum();
}
You can use Java´s generics for this
public <T extends Number> T total(List<T> list) {
T sum = 0;
for (T n : list) {
sum += n.longValue();
}
return sum;
}
Knowing that T will always be a Number, things are simplified. However, this solution could work also for Strings, if necessary. The only change would be in the extends part.
Is it possible to have a function that returns either Integer or Float? I want to have the 2 functions become one if it's possible:
private static Integer parseStringFormatInt(String val){
System.out.println(Integer.parseInt(val.substring(0, val.indexOf("."))));
return Integer.parseInt(val.substring(0, val.indexOf(".")));
}
private static Float parseStringFormatFloat(String val){
System.out.println(Float.parseFloat(val.substring(0, val.indexOf("."))));
return Float.parseFloat(val.substring(0, val.indexOf(".")));
}
Make the return type as Number since both Float and Integer are subtypes of Number like below
private static Number parseStringFormatNumber(String val){
//Based on your conditions return either Float or Integer values
}
You can also make instanceof operator to do the test on the return value, to get the exact type of the returned value. i.e Float or Integer
if(returnedValue instanceof Float)
{
// type cast the returned Float value and make use of it
}
else if(returnedValue instanceof Integer)
{
// type cast the returned Integer value and make use of it
}
You can use Number as return type, or make the method generic
static <T extends Number> T parseString(String str, Class<T> cls) {
if (cls == Float.class) {
return (T) Float.valueOf(str);
} else if (cls == Integer.class) {
return (T) Integer.valueOf(str);
}
throw new IllegalArgumentException();
}
You can return Number:
private static Number parseStringFormatNumber(String val){
try {
return Integer.valueOf(val.substring(0, val.indexOf(".")));
} catch (NumberFormatException e) {
try {
return Float.valueOf(val.substring(0, val.indexOf(".")));
} catch (NumberFormatException e2) {
// handle invalid value (throw exception, return 0?)
}
}
}
I wouldn't. I would have the method return 'float', with a small 'f', or more probably 'double'; I would have it parse the entire value without any substring operations; and I would cast the value to 'int' at the call sites that require it, and/or from there to Integer, Float, Double, ... whatever you need at the call site.
You will find this is orders of magnitude more efficient than the answer you've accepted, which is just a poor man's self-implemented runtime polymorphism where you previously had compile-time polymorphism.
But the problem itself is highly dubious. There are no ints and floats in an Excel file. There are only decimal numbers with zero or more decimal places.
Alternatively, return a discriminated union such as Either<Integer, Float>. There is a stand-alone implementation of Either for Java 8 in a small library, "ambivalence": http://github.com/poetix/ambivalence
Either<Integer, Float> either1 = Either.ofLeft(23);
Either<Integer, Float> either2 = Either.ofRight(Float.valueOf("3.14"));
BigDecimal result1 = either1.join(BigDecimal::valueOf, BigDecimal::valueOf);
BigDecimal result2 = either2.join(BigDecimal::valueOf, BigDecimal::valueOf);
You can get it from Maven central:
<dependency>
<groupId>com.codepoetics</groupId>
<artifactId>ambivalence</artifactId>
<version>0.2</version>
</dependency>