Real world application of widening / narrowing conversion?
Solution 1
(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.
Solution 2
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.
Solution 3
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.
Solution 4
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!
Solution 5
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.
Hermes Trismegistus
Updated on November 18, 2020Comments
-
Hermes Trismegistus over 3 years
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!