Can someone please explain why you would ever use widening or narrowing conversion? I've read a lot about these but no one ever gives me a practical example. Thanks!
(Java) Widening and Narrowing Conversions have to do with converting between related Types. Take, for example, the relationship between an abstract (super) class and its (child) subclass; let's use the java.lang.Number Class (abstract) and a direct subclass Integer. Here we have:
(superclass) Number
__________/\__________
/ | | \
(concrete subclasses) Integer Long Float Double
Widening Conversion: occurs if we take a specific type (subclass) and attempt to assign it to a less specific type (superclass).
Integer i = new Integer(8);
Number n = i; // this is widening conversion; no need to cast
Narrowing Conversion: occurs when we take a less specific type (superclass) and attempt to assign it to a more specific type (subclass), which requires explicit casting.
Number n = new Integer(5); // again, widening conversion
Integer i = (Integer) n; // narrowing; here we explicitly cast down to the type we want - in this case an Integer
There are certain issues that you need to be aware of such as ClassCastExceptions:
Integer i = new Integer(5);
Double d = new Double(13.3);
Number n;
n = i; // widening conversion - OK
n = d; // also widening conversion - OK
i = (Integer) d; // cannot cast from Double to Integer - ERROR
// remember, current n = d (a Double type value)
i = (Integer) n; // narrowing conversion; appears OK, but will throw ClassCastException at runtime - ERROR
One way to handle this is to use an if statement with the instanceof keyword:
if( n instanceof Integer) {
i = (Integer) n;
}
Why would you want to use this? Let's say you are making a hierarchy of personnel for some program and you have a generic superclass called "Person" which takes a first and last name as parameters, and subclasses "Student", "Teacher", "Secretary", etc.. Here you can initially create a generic person, and assign it (through inheritance) to, say, a Student which would have an additional variable field for studenID set in it's constructor. You can use a single method that takes the more generic (wider) type as a parameter and handle all subclasses of that type as well:
public static void main(String[] args) {
Person p = new Student("John", "Smith", 12345);
printInfo(p);
}
// this method takes the wider Person type as a parameter, though we can send it a narrower type such as Student if we want
public static void printInfo(Person p) {
System.out.println("First: " + p.getFirstName());
System.out.println("Last: " + p.getLastName());
if (p instanceof Student) {
System.out.println( (Student)p).getStudentID() ); // we cast p to Student with Narrow Conversion which allows us to call the getStudentID() method; only after ensuring the p is an instance of Student
}
}
I realize this may not be the ideal way to handle things, but for the sake of demonstration I thought it served to show some of the possibilities.
If some code returns an int containing a true/false value, you could shorten it yourself to a bool which is what it properly represents.
You can also do the opposite.
You can widen a char to int to do some comparisons with ascii values.
You can take an instance of Dog and widen it to IAnimal to pass it to a function.
You can shorten a IAnimal to Dog when you know the type of animal in a List<IAnimal> in a factory or elsewhere for whatever reason.
You use implicit conversions to do math with numerical values of different types. For example, if now() returns a timestamp in seconds as a long:
long t = now()
long nextMinute = t + 60
you have done an implicit widening conversion of 60 (an int) to a long so you can add it to t. Being able to do such conversions makes math much easier to code.
One canonical example of widening and narrowing conversions is how certain file I/O libraries work. Often, a file processing library will have a function / method that reads a single character from a file. If there is a character to read, the function should return that character, and if no characters are left it should return a sentinel value EOF to signal this.
Because any character can appear in a file, typically the function / method would have this signature:
int readCharacter();
Here, the function returns an int that holds a char value if a character was read and which holds EOF as a sentinel otherwise. EOF is typically chosen as an integer that is too big to hold in a char. That way, you can do this:
while (true) {
int ch = readCharacter();
if (ch == EOF) break;
char actualCharValue = (char) ch;
/* process actualCharValue here */
}
Hope this helps!
Take this...
Conversion - Specialized -> Generalized, then it is known as Widening, when you are becoming more general.
Such as Surgeon -> Medico. In this case, you do not need a casting. Because, a surgeon is a Medico by default. So, it is natural that a surgeon can perform all those stuffs that a Medico can do.
While on the other hand,
Conversion - Generalized -> Specialized, then it is known as narrowing, when you are becoming more specialized.
Such as Medico -> Surgeon. Well, in this case, you must have to add casting. Because, a medico can be a surgeon or a physician or a nurse. Think of it, if you ask a nurse to operate on you...
Horrible, right ???
Hope you got the idea.
Related
What is the difference between them?
l is an arraylist of Integer type.
version 1:
int[] a = new int[l.size()];
for (int i = 0; i < l.size(); i++) {
a[i] = l.get(i);
}
return a;
version 2:
int[] a = new int[l.size()];
for (int i = 0; i < l.size(); i++) {
a[i] = l.get(i).intValue();
}
return a;
l.get(i); will return Integer and then calling intValue(); on it will return the integer as type int.
Converting an int to Integer is called boxing.
Converting an Integer to int is called unboxing
And so on for conversion between other primitive types and their corresponding Wrapper classes.
Since java 5, it will automatically do the required conversions for you(autoboxing), so there is no difference in your examples if you are working with Java 5 or later. The only thing you have to look after is if an Integer is null, and you directly assign it to int then it will throw NullPointerException.
Prior to java 5, the programmer himself had to do boxing/unboxing.
As you noticed, intValue is not of much use when you already know you have an Integer. However, this method is not declared in Integer, but in the general Number class. In a situation where all you know is that you have some Number, you'll realize the utility of that method.
The Object returned by l.get(i) is an instance of the Integer class.
intValue() is a instance method of the Integer class that returns a primitive int.
See Java reference doc...
http://docs.oracle.com/javase/6/docs/api/java/lang/Integer.html#intValue()
Java support two types of structures first are primitives, second are Objects.
Method that you are asking, is used to retrieve value from Object to primitive.
All java types that represent number extend class Number. This methods are in someway deprecated if you use same primitive and object type since [autoboxing] was implemented in Java 1.5.
int - primitive
Integer - object
Before Java 1.5 we was force to write
int i = integer.intValue();
since Java 1.5 we can write
int i = integer;
Those methods are also used when we need to change our type from Integer to long
long l = integer.longValue();
Consider this example:
Integer i = new Integer(10);
Integer j = new Integer(10);
if (!(i == j)) {
System.out.println("Surprise, doesn't match!");
}
if (i.intValue() == j.intValue()) {
System.out.println("Cool, matches now!");
}
which prints
Surprise, doesn't match!
Cool, matches now!
That proves that intValue() is of great relevance. More so because Java does not allow to store primitive types directly into the containers, and very often we need to compare the values stored in them. For example:
oneStack.peek() == anotherStack.peek()
doesn't work the way we usually expects it to work, while the below statement does the job, much like a workaround:
oneStack.peek().intValue() == anotherStack.peek().intValue()
get(i) will return Integer object and will get its value when you call intValue().In first case, automatically auto-unboxing happens.
They are exactly the same. As other posters have mentioned, you can put either the Integer object or the int primitive into the array. In the first case, the compiler will automatically convert the Integer object into a primitive. This is called auto-boxing.
It's just a convenience method for getting primitive value from object of Number: http://docs.oracle.com/javase/1.4.2/docs/api/java/lang/Number.html
Consider the code:
Integer integerValue = Integer.valueOf(123);
float floatValue = integerValue.floatValue();
The last line is a convenient method to do:
float floatValue = (float)(int)integerValue;
Since any numeric type in Java can be explicitly cast to any other primitive numeric type, Number class implements all these conversions. As usual, some of them don't make much sense:
Integer integerValue = Integer.valueOf(123);
int intValue = integerValue.intValue();
int intValue2 = (int)integerValue;
int intValue3 = integerValue;
If we can typecast an Integer object to Object and unbox an Integer to int, why can't we unbox the same Object type to int?
Integer i=new Integer(5);
Object p=i;
int s=p;//gives error
The compiler has no idea that something of type Object is actually an integer. To your eyes, you can clearly see that you are assigning an integer and that is converted into an integer object. However, as soon as you assign it to an Object, that information becomes lost for the semantic purposes of the source code.
It's the same reason why this code would be wrong:
Object o = getSomeObject(); // What exactly does it return? We don't know
int i = o; // How do you know this will work?
The function getSomeObject() could return anything. We have no idea if it will be an integer, or a String, or an ArrayList... and therefore we have to assume it is an unsafe conversion. If the above makes sense to you, then you can extrapolate to your example why you can't do the conversion.
Now the part I assume that is confusing you is "I clearly put an integer in there, which is auto boxed into an Integer and should be assignable by unboxing!"
... however the rules of the language state that once we do that assignment, we have to treat it as that it could be any object of that type. Since we can't blindly assume that the object is an integer, you need to explicitly cast it.
This also plays a role in going up the hierarchy chain for polymorphism. If you have a parent P, and a child C that extends from P, then we know that:
Child c = new Child();
Parent p = c; // Valid, because c is definitely a Parent
but
Parent p = new Parent();
Child c = p; // Can't go down the hierarchy, this is also wrong
The same thing is seen in your example. Integer is a child of Object, that's why we can do
Integer i = 5;
Object o = i;
and likewise, we can't do it in the reverse
Object o = new Object();
Integer i = o; // Not allowed
and because for your example we need to go from Object -> Integer -> int (by unboxing), the second step from Object to Integer is not allowed.
Now if you did in fact write code like
Integer i = new Integer(5);
Object p = i;
int s = (Integer) p;
this would work. In fact, the JVM at runtime (via HotSpot) would likely notice exactly what you're doing and convert the above code into:
int s = 5;
because the JVM is likely smart enough to realize exactly what you as a human realize.
So while you have to write semantically correct Java source code (which doing int i = someObj is not when the right hand side of the equals side is an Object), the compiler will likely be smart enough to inline all of it for you when you run your program.
Upcasting is casting to a supertype in your case object, while downcasting is casting to a subtype in your case int. Upcasting is always allowed, but downcasting involves a type check.
Object p=i;
Here you are doing Upcasting which is always allowed because it make sense that int is an Object.
int s = p;
Here p is object and you are trying to assign it to subtype int, how would the compiler will know that p-object is an int not a string or double etc. So to solve this your have to do downcasting explicitly like this:
int s = (int) p;
NOTE: Wrong downcasting can throw a ClassCastException.
How do I convert char to Integer type?
int p2 = (int)stack2.pop();
int p1 = (int)stack2.pop();
int res = result(p2, p1, calStr.charAt(i));
stack2.push(res);
I did the above method, but getting a runtime error that java.lang.Character cannot be cast to java.lang.Integer
Exception in thread "main" java.lang.ClassCastException:
java.lang.Character cannot be cast to java.lang.Integer
Explanation
You can not directly cast a Character to an Integer, those are objects. The cast between them only works if we are talking about the datatypes char and int.
While Java indeed auto-boxes and -unboxes Integer to int, Character to char and vice versa, it does not automatically use this technique for a fast conversion between Integer to Character.
Solution
Of course you can do a transformation like:
Character -> char -> int -> Integer
So as code this could look like:
Character itemAsCharacter = stack2.pop();
char itemAsChar = itemAsCharacter.charValue();
int itemAsInt = (int) itemAsChar;
Integer itemAsInteger = Integer.valueOf(itemAsInt);
Or in short:
// Last step uses the implicit auto-boxing of int -> Integer
Integer item = (int) stack2.pop().charValue();
The backwards direction works similar:
Integer resultAsInt = ...
Character result = (char) resultAsInt.intValue();
stack2.push(result);
Note
Please note that the class Stack, also Vector, are outdated classes in Java. There are classes with the same functionality that provide more methods, are more robust and simply more up-to-date. Examples for LIFO data-structures are listed under the interface Deque, the most common used implementation is LinkedList and there is also an array-variant, the ArrayDeque.
Here is the relevant excerpt from the documentation of Stack:
A more complete and consistent set of LIFO stack operations is
provided by the Deque interface and its implementations, which should
be used in preference to this class.
With autounboxing, this statement will automatically work:
int myPrimitive = (Integer) doIt();
But if I want to explicitly convert from an Integer to an int here in a single line, where do I have to put the parentheses?
You could do this :
int myPrimitive = (int) (Integer) doIt();
But as you said, auto-unboxing will get that for you.
A bad example to show that chain casts work (don't ever use this code) :
Map notReallyAMap = (Map) (Object) new String();
The thing with chain casts, is that wherever you use it, either the cast is legit, and you can remove intermediaries; or the cast will simply cause a ClassCastException. So you should never use it.
Either the compiler unboxes the Integer for you, or you do it yourself - this cannot be avoided.
So you need to either do
int myPrimitive = ((Integer) doIt()).intValue();
or more simply, change doIt() to return an int since you seem to want to deal with ints rather than (null-able) Integers.
Running this code:
public class SomeSet {
public static void main(String[] args) {
Set<Short> s = new HashSet<Short>();
for (short i = 0; i < 100; i++) {
s.add(i);
s.remove(i - 1);
}
System.out.println(s.size());
}
}
Will print the value 100.
Why does it print this value?
s.remove(i - 1);
The line above will attempt to remove Integer objects from the set, because all integer calculations in Java have int (or long) results. Since the set contains Short objects, the remove() method will not have any effect.
This (and similar problems) is the main reason why you should almost never use short (and, more so, Short). Using a Set implementation to contain autoboxed numbers incurs a massive (easily 1000%) overhead, so it's rather pointless to try and save space by using Short rather than Integer.
The problem is that remove(i-1) calls the remove method with an Integer object, since i-1 is of type int (which gets auto-boxed into an Integer).
To make sure that you call remove with a Short object use this:
s.remove((short) (i - 1));
The type of i - 1 is int, so it gets autoboxed to an Integer.
Normally you'd expect a generic collection to prevent you performing operations which have arguments of the wrong type, but the interface to Set<E> is a bit loose.
Because the remove method of Set<E> takes an Object rather than an E, the compiler doesn't warn you that you're removing a different type to what the set contains.
To force it to be a Short, cast the numeric value to (short). (casting to (Short) isn't allowed, and you'd have to cast the numeric value to use Short.valueOf)
Note that the add method is generically typed boolean add(E o) so in your case of Set the add method will take a short, whereas the remove method is not generically typed boolean remove(Object o) so i - 1 autoboxes to a Integer. For any value of i new Short(i).equals(new Integer(i)) will always be false.
Note that if you try s.add(i - 1); you will get a compiler error because i - 1 becomes an instance of Integer and the types Integer and Short do not match.