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.
Related
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.
System.out.println( Integer.valueOf(1).equals(Long.valueOf(1)) ); // false
Above print statement prints false, when using Integer and Long classes valueOf() methods respectively, the reason is very clear the two objects (Integer and the Long) have different types so they are not equal.
But, if passing integer value 1 and long value 1l to valueOf() method of string class, my print statement prints true
System.out.println( String.valueOf(1).equals(String.valueOf(1l)) ); // true
I am not able to understand what is exact difference between them.
I need explanation for that.
The answer is on the implementation of Integer#equals:
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
You don't enter the if statement because they are of different types.
In the second version you are getting true because.. 1 is the same in String representation.
As you said, Integer and Long are different types, so equals returns false.
However, when comparing two Strings, if the value is the same (which is), equals returns true.
Conversion of Long or Integer to String is determined by the number it represents. Integer 1 and Long 1 have the same digits, so converting both to String give same results ("1").
Quite simply, the String representation of the number one is equal independent of what you use to retrieve that value. The valueOf method is overridden to work with ints and longs.
Your first experiment and your second experiment are not at all comparable.
Simple; regardless of whether the number 1 comes from an int, long, byte, or whatever, String.valueOf(1) -> "1". So you're running System.out.println("1".equals("1")), which is true.
Thats because in Java there are specific suffixes, for long (1L), float (1.4f) and double (-3.123d)..
Without any suffix, e.g. 1234 its being assumed to be an int. If its for example 3.1234 it would be assumed that its a double.
It doesnt matter if the suffix is in upper or lower case.
Integer.valueOf(1).equals(Long.valueOf(1)) // false
Yes, because Integer.ValueOf(1) result in an Integer instance and Integer.equals(Object) expects that the Object is a type Integer too.
String.valueOf(1).equals(String.valueOf(1l)) // true
Results is true, because both String.valueOf(1) and String.valueOf(1l) results in a string representing a number with plain character of digit. For example:
String.valueOf(12335454654465421L); will result in "12335454654465421"
public int compareTo(Object a) {
int Output = 0;
if(this.equals(a))
Output = 0;
if(a instanceof this.getClass()) {
if(this._numDec > ((this.getClass())a)._numDec)
Output = 1;
if(this._numDec < ((this.getClass())a)._numDec)
Output = -1;
}
return Output;
}
Hello. My CS instructor told our class to create a function that would determine which of the two values would be larger, outputting 1 if the former is larger, 0 if they are equal, and -1 if the former is smaller. The this.getClass() was originally a Hexadecimal class for converting Hexadecimal values into _numDec. However, I get errors when I try to use this.getClass(). Can somebody help?
Write out the exact class name explicitly.
if (a instanceof Hexadecimal) {
if (this._numDec > ((Hexadecimal) a)._numDec)
Output = 1;
if (this._numDec < ((Hexadecimal) a)._numDec)
Output = -1;
}
By the way, if a is not a Hexadecimal object then your method is going to return 0. It should return non-zero.
You are not having problems because your call to getClass() isn't working, you are having problems because you are using incorrect casting syntax. The correct syntax is of the form:
<type> target_var = (<type>) source_var;
Where <type> is expected to be an actual Java type (primitive or object). Valid Java primitive types are (int, float, long, double, char, boolean ...). Valid object types are specified by their fully qualified name, or simple name if the type is imported. For example (String, Serializable, Object, JFrame ...).
In your code, this line:
if(this._numDec > ((this.getClass())a)._numDec)
Will not compile because you are specifying the casting to be this.getClass(), which is a dynamic expression, not a valid type.
There are several ways to accomplish your real goal, which is too determine if the supplied object is of a type that you can compare to the current object, and then to perform the comparison. Here's one such way:
if(a instanceof Binary)) {
if(this.getNumDec() > ((Binary)a).getNumDec())
Output = 1;
else
Output = -1;
} else if(a instanceof Hexadecimal) {
if(this.getNumDec() > ((Hexadecimal)a).getNumDec())
Output = 1;
else
Output = -1;
}
Note that I have replaced the use of direct property access with accessor methods.
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.
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