I am given an instance of the object class (String, int, double, or boolean) from a database. I need to write a method that can compare this object to something else (String, int, double, or boolean) using binary operators (e.g. <=, !=, >, etc.). I will only run this method if the two objects are of the same type. My method looks like
public boolean comparison(Object a, Object b, String operator) {
int result = a.compareTo(b);
String a2 = a.getClass().getName();
//followed by if/else if blocks to return true or false
//depending on operator and result
}
I have designed the if/else if blocks to ensure that no binary operator will be used for incompatible types (e.g. >= for a String object). The problem is that I get a "can't find symbol error" when I try to compile because the object class doesn't have a compareTo() method. If this was python, there wouldn't actually be any issue because I would never be putting anything into the comparison function that didn't have a compareTo() method. However, because of java's formatting I'm forced to declare the input as 'Object' because I can't say specifically what type of object I have to compare at a given moment.
Is there some way I could override Java's restrictions and force it to trust me that Object a will always have a compareTo() method? Because right now, it seems like I'm going to have to downcast the objects into Strings, ints, doubles, or booleans, and then write 4 different new comparison functions for each data type.
Since the compareTo method is declared in the java.lang.Comparable<T> interface, the usage of Bounded Type Parameters can constrain your a and b arguments in order to accept only objects which implement the interface itself.
You can check the operator parameter in order to affect the boolean result.
If you wish, the usage of an enum value helps to avoid string mismatching.
For brevity, the code below doesn't take in account null values.
enum Operator {
GREATER_OR_EQUALS,
LESS_OR_EQUALS
}
public <T extends Comparable<T>> boolean comparison(T a, T b, Operator operator) {
int test = a.compareTo(b);
switch(operator) {
case GREATER_OR_EQUALS:
return test >= 0;
default:
return test <= 0;
}
}
The Java way is to first use the instanceof operator to then cast to an appropriate class:
if (a instanceof String && b instanceof String) {
String aAsString = (String) a;
int comparisonResult = a.compareTo(b);
compareTo() gives you a negative result if a < b, 0 when a == b and a positive result if a > b. So alone that int result tells you enough to decide what your potential comparison operations should result in. The numeric types int/Integer, ... have similar methods.
Of course this only works if your assumptions are really correct and the object is really a String or Boolean. And please note that boolean and Boolean are two somehow different types!
Casting variables in Java
The top answer summarizes what I wanted to do. The code successfully compiled when I said
int result;
if (a instanceof Comparable && b instanceof Comparable) {
result = ((Comparable) a).compareTo((Comparable) b);
}
I haven't tested it, but fantaghirocco's solution also seems like it could work.
Related
I am trying to compare two Object whose type is unknown during runtime, but it is at least guaranteed that the type will be some form of Number. Is there a way to compare an Object whose base class is Number without explicitly casting the object to one of Java's integral types?
So, for example:
Object objectInt1 = (Integer) 5;
Object objectInt2 = (Integer) 6;
if (objectInt1 > objectInt2) {
// Clearly won't compile
}
if ((Number) objectInt1 > (Number) objectInt2) {
// This of course won't compile either
}
The only way I can think to accomplish this is by using instanceof and check for each integral type, and do explicit casting. But maybe there is a cleaner way to do this.
If you know they will be Number, you can use:
Object object1 = Integer.valueOf(5);
Object object2 = Float.valueOf(6);
Number number1 = (Number) object1;
Number number2 = (Number) object2;
if (number1.doubleValue() < number2.doubleValue()) {
System.out.println("number1 < number2");
}
Of course you can also do your casting inline.
There are only two “integral” types of Number: Integer and Long, and an Integer can safely be expressed as a Long.
The Number type has the longValue() method, so you can safely cast to Number and compare the result of calling that method:
if (((Number)object1).longValue() > ((Number)object2).longValue())
To compare something you must know how to do it. So you cant just take something unknown and compare it to something else.
Operators < > are defined for primitive types only.
Objects that are meant to be compared implement Comparable interface. You can check that Object implements it and call compareTo method to compare Objects of the same type. All common java number types (Integer, Short, BigDecimal) implement Comparable.
As the String is an object, you need to use the string.equals(string) method to confirm that the two strings are equal.
However, why do you use the == to check if two chars are equal, char1 == char2, rather than char1.equals(char2)?
Perhaps, this will help to understand the difference on == vs equals.
#Test
public void testCharacterEquals() {
//primitive type uses == operator for equals comparasion
char a1 = 'A';
char a2 = 'A';
if (a1 == a2) {
System.out.println("primitive type comparasion: it's equal");
}
//From Java doc; The Character class wraps a value of the primitive type char in an object. An object of type Character contains a single field whose type is char.
//Object type uses equals method for equals comparasion
Character character1 = 'A';
Character character2 = 'A';
if (character1.equals(character2)) {
System.out.println("object type comparasion: it's equal");
}
}
It depends on using a primitive type, char, int, etc. And using Objects like String. A primitive type like an int can be compared 1 == 1 and if you check 2 objects to each other ObjectA != ObjectB.
Check out this answer over here: Primitive vs Object type in Java
Or over here: https://chortle.ccsu.edu/java5/Notes/chap09C/ch09C_2.html
Quote:
A primitive data type uses a small amount of memory to represent a
single item of data. All data of the same primitive type are the same
size.
For example, primitive type int represents integers using 32 bits. All
variables of type int use 32 bits.
There are only eight primitive data types in Java: byte, short, int,
long, float, double, char, and boolean. A Java program cannot define
any other primitive data types.
An object is a large chunk of memory that can potentially contain a
great deal of data along with methods (little programs) to process
that data. There are thousands of object classes that come standard
with Java, and a programmer can easily create additional classes.
(Although there are thousands of standard classes, for this course you
only need become familiar with a dozen or so classes.)
Where 2 strings are 2 different objects. Therefor not the same object and not the same string. While the characters might be the same.
Basically Java has primitive types (int, char, short, long, byte ....)
and Reference Data types/ Objects composed of other primitives and Objects.
equals() is a method of all Java Objects. But char is not an Object type in Java, it is a primitive type, it does not have any method or properties, so to check equality they can just use the == equals operator.
You can also use a comparator if you want.
public static boolean compareChars(char c1, char c2){
int comp = Character.compare(c1, c2);
if(comp>0){
return false;
}else{
return true;
}
}
public static void main (){
boolean b1 = compareChars('A', 'A') //return true;
boolean b2 = compareChars('A', 'C') //return false;
}
Because char is a primitive type and does not implement equals, == compares char values directly in this case, where as String is an object. So for object comparison, the equality operator is applied to the references to the objects, not the objects they point to. Two references are equal if and only if they point to the same object, or both point to null.
If you want to compare strings (to see if they contain the same characters), you need to compare the strings using equals().
Hence, the operator == checks equality of values on primitive types, but it checks references equality for objects. If the two objects are referenced by equal references, a reference and an object are different in Java.
Let's first consider the following expressions in Java.
Integer temp = new Integer(1);
System.out.println(temp.equals(1));
if(temp.equals(1))
{
System.out.println("The if block executed.");
}
These all statements work just fine. There is no question about it. The expression temp.equals(1) is evaluated to true as expected and the only statement within the if block is executed consequently.
Now, when I change the data type from Integer to Long, the statement temp1.equals(1) is unexpectedly evaluated to false as follows.
Long temp1 = new Long(1);
System.out.println(temp1.equals(1));
if(temp1.equals(1))
{
System.out.println("The if block executed.");
}
These are the equivalent statements to those mentioned in the preceding snippet just the data type has been changed and they behave exactly opposite.
The expression temp1.equals(1) is evaluated to false and consequently, the only statement within the if block is not executed which the reverse of the preceding statements. How?
You're comparing a Long to an int. The javadoc for java.lang.Long#equals says that the equals method
Compares this object to the specified object. The result is true if and only if the argument is not null and is a Long object that contains the same long value as this object.
Instead try System.out.println(new Long(1).equals(1L)); Now that you're comparing a Long to a Long instead of a Long to an Integer, it will print true.
The reason you can do that comparison is because of autoboxing in Java.
The actual method you are calling is this:
http://docs.oracle.com/javase/1.4.2/docs/api/java/lang/Long.html#equals(java.lang.Object)
which is comparing your Long object to some other Object, not to an actual primitive int.
What happens when you call the method is that your primitive integer(1) is being autoboxed into an Object(Integer) so then you are effectively calling:
new Long(1).equals(new Integer(1));
which is why it fails.
This is why if you call
new Long(1).equals(1L)
this would work, because Java will autobox the 1L (primitive long, not int) into a Long object, not an Integer object.
The literal value 1 is not a long, it's an int. Try the above code with this instead:
System.out.println(temp1.equals(1L));
And
if (temp1.equals(1L))
As you can see, putting an L after the literal value 1 indicates that it's a long, and then the comparisons work as expected.
Java is being lazy.
When you perform the following comparison java will automatically cast the int to a long (as a long can contain any value an int can contain). And the comparison is between two longs and not two ints.
int i = 1;
long l = 1L;
boolean b = i == l;
Java is able to do this because the type information about i and l is known at compile time and when performing the comparison. However, when you use the boxed version the type can be known at compile time, but not when performing the comparison. This is because the comparison has to done within an equals method, and since equals takes Object as a parameter the type information is lost. Thus Java is lazy and only checks to see if two boxed numbers are equal if they are both of instances of same Number class (eg. both Integer, or both Long, or both Double, etc...).
Turns out the only fully reliable way to compare two numbers of unknown type at runtime is to convert both to strings and both to BigDecimal and then to use the method compareTo (and not equals). Though if you know you are only ever going to get longs and ints then life is simpler as you can just do the following.
Number n0 = new Long(1L);
Number n1 = new Integer(1);
boolean equal = n0.longValue() == n1.longValue();
According to Javadoc's page on Long, the .equals method evaluates to true only if
The argument is a Long object
If (1) is true, then the Long objects must have equal values
In your scenario, 1 is an int, not a Long object, so it fails (1), and therefore, evaluates to false. If you need to test to a long, use 1L instead.
That behaviour is consistent with autoboxing converting the 1 to an Integer which then compares equal to another Integer(1). Comparing a Long to an Integer yields false.
If you would use 1L to compare against Long it would yield true.
Long temp1 = new Long(1); System.out.println(temp1.equals(1));
if(temp1.equals(1)) {
System.out.println("The if block executed."); }
in this code temp1.equals(1) is comparing a Long object to Integer object which gives the result false ,we can correct it by using 1L instead of 1 ,,,eg temp1.equals(1L), by doing this we are comparing Long object with a Long and gives result TRUE
The implementation of equals() method of class Long illustrates why:
public boolean equals(Object obj) {
if (obj instanceof Long) {
return value == ((Long)obj).longValue();
}
return false;
}
The equals method in Java.lang.Long initially starts with an instanceOf Long check only after that the value is compared.
public boolean equals(Object obj) {
if (obj instanceof Long) {
return value == ((Long)obj).longValue();
}
return false;
}
So if you are going to use and Integer Value int the place of a Long value then the first check fails and hence you will get false as the result .
You can compare Long/integer values without uting equals(). This is only needed when you are comparing strings as far as I know.
I have a code snippet which I am not able to understand what exactly it does..
This code is in JavaBean..
private Object myNumb;
//then getter and setter for this
public int compareTo(myRptObj o){
if (myNumb instanceof String && o.myNumb instanceof Integer) {
return_value = 1;
} else if (myNumb instanceof Integer && o.myNumb instanceof String) {
return_value = -1;
} else if (myNumb instanceof String && o.myNumb instanceof String) {
return_value = ((String) myNumb).compareTo((String)o.myNumb);
} else if (myNumb instanceof Integer && o.myNumb instanceof Integer) {
return_value = ((Integer) myNumb).compareTo((Integer)o.myNumb);
}
}
I want to understand compareTo and how the comparison for String and Integer is done?
myNumb can be of type Integer or String.
The snippet makes sure that any Integer compares less than any String.
Integer-Integer and String-String comparisons are done the way you'd expect.
the first two if clauses ensure that Strings are considered greater than integers. The second two if clauses delegate the comparison between Strings and between Integers to the compareTo() implementation of these classes so that they are ordered as usual.
Note that it is very, very bad design to have this kind of code where a variable can be "of type X or Y" - exactly because it forces you to write code like this.
Instead, decide on one type and convert to/from that type where necessary.
... what exactly it does.
Basically: strings are greater than integers, if both are the same type use their natural ordering (either compare 2 strings or 2 integers).
Since myNumb can be either a String or an Integer, compareTo is checking what types they are first. If they are not the same type (one is an Integer and one is a String) they are not equal and therefore returns 1 or -1 as appropriate. If they are of the same type, it is casting them to that type and delegating to that type's compareTo.
In Java, all numeric types extend from java.lang.Number. Would it be a good idea to have a method like the following:
public boolean areEqual(Number first, Number second) {
if (first != null && second != null) {
return first.equals(second);
}
}
I'm concerned about cases where a double 2.00000 does not equal an int 2. Are these handled by the built-in equals? If not, is there any way to write a simple number compare function in java? (external libraries such as apache commons are ok)
A Double is NEVER equals to an Integer. Moreover, a double is not the same as a Double.
Java has primitive types and reference types. The truly numeric types in Java do not extend from Number, because they're primitives.
You may want to consider a system where you're not mixing types, because that usually will cause a lot of trouble with implicit/explicit conversions that may/may not lose information, etc.
Related questions
On int vs Integer:
What is the difference between an int and an Integer in Java/C#?
Is Java fully object-oriented?
On Number comparison:
Why doesn't java.lang.Number implement Comparable?
Comparing the values of two generic Numbers
See also
Java Language Guide/Autoboxing
JLS 4.2 4.2 Primitive Types and Values
The numeric types are the integral types and the floating-point types.
The integral types are byte, short, int, and long and char.
The floating-point types are float and double.
On mixed-type computation
Mixed-type computation is the subject of at least 4 puzzles in Java Puzzlers.
Here are various excerpts:
it is generally best to avoid mixed-type computations [...] because they are inherently confusing [...] Nowhere is this more apparent than in conditional expressions. Mixed-type comparisons are always confusing because the system is forced to promote one operand to match the type of the other. The conversion is invisible and may not yield the results that you expect
Prescription: Avoid computations that mix integral and floating-point types. Prefer integral arithmetic to floating-point.
To compare two Numbers in Java you can use the compareTo from BigDecimal. BigDecimal can hold everything from short until double or BigInteger, so it's the perfect class for this.
So you can try to write something like this:
public int compareTo(Number n1, Number n2) {
// ignoring null handling
BigDecimal b1 = BigDecimal.valueOf(n1.doubleValue());
BigDecimal b2 = BigDecimal.valueOf(n2.doubleValue());
return b1.compareTo(b2);
}
This is surely not the best approach regarding to performance.
The following tests worked so far, at least with JDK7:
assertTrue(compareTo(new Integer(1), new Integer(2)) == -1);
assertTrue(compareTo(new Integer(1), new Double(2.0)) == -1);
assertTrue(compareTo(new Integer(1), new Double(Double.MAX_VALUE)) == -1);
assertTrue(compareTo(new Integer(1), new Double(Double.MIN_VALUE)) == 1);
assertTrue(compareTo(new Integer(1), new Double(1.000001)) == -1);
assertTrue(compareTo(new Integer(1), new Double(1.000)) == 0);
assertTrue(compareTo(new Integer(1), new Double(0.25*4)) == 0);
assertTrue(compareTo(new Integer(1), new AtomicLong(1)) == 0);
The specific method you suggest would fail, because it's using equals() inherited from Object. That is, it would check to see if the Number objects were the same, not whether their values were the same.
If that was just an illustrative example, I will update my answer.
polygene's answer actually pretty much covers the ground I was heading for. You may also be interested in this question: Why doesn't java.lang.Number implement Comparable?.
If you want to know whether the object references are the same, then the existing methods fit the bill. A Double representing 2.0 and an Integer representing 2 are definitely different objects, and certainly not interchangeable in a general sense.
If you just want to know whether the numeric values are the same, you can use the Number.doubleValue() method to convert both numbers to doubles, then compare those numbers together (probably allowing for a small tolerance, as most numbers are represented inexactly, such as 1.99999999996583 for what should be 2, depending on the intermediate calculation steps). Something like the following:
private static final double EPSILON = 0.000000000000001d;
public static boolean areEquivalentNumbers(Number a, Number b)
{
if (a == null)
{
return b == null;
}
else if (b == null)
{
return false;
}
else
{
return Math.abs(a.doubleValue() - b.doubleValue()) < EPSILON;
}
}
On a tangent to a couple of the responses, may I suggest that instead of writing something like:
boolean compare(Object o1, Object o2)
{
if (o1==null)
return o2==null;
if (o2==null)
return false;
return o1.equals(o2);
}
It's much more concise, and I believe slightly more efficient, to write:
boolean compare(Object o1, Object o2)
{
return o1==o2 || o1!=null && o2!=null && o1.equals(o2);
}
If both are null, o1==o2 will return true. If they're not but they're the same object, that's fine too.
Technically the o2!=null is not necessary for most implementations of equals, but if you were really being so generic as to do this on Objects as in the above example, you of course wouldn't know how every override was written.
public static boolean compareTo(Number d1, Number d2) {
Double num1=d1.doubleValue();
Double num2=d2.doubleValue();
if(Double.compare(num1, num2)==0)
return true;
else
return false;
}
OR
public static boolean compareTo(Number d1, Number d2) {
if(d1.doubleValue()==d2.doubleValue())
return true;
else
return false;
}
Comparing numbers between integer and floating point is almost never going to yield what you are after. If however this is a simple exercise, you could implement the comparison by comparing the string representations of the values, as in:
public boolean areEqual(Number first, Number second) {
if (first == null) {
return second == null;
}
if (second == null) {
return false;
}
return first.toString().equals(second.toString());
}
you cannot call
number.equals(number2);
because, if number is a Double and number2 is an Integer, they will not be of the same class and you will get an exception telling you of that fact.
You could write a comparison class yourself that accepts Number objects, but you will have to take into account the different subclasses of Number