Real world application of widening / narrowing conversion?

25,810

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.

Share:
25,810
Hermes Trismegistus
Author by

Hermes Trismegistus

Updated on November 18, 2020

Comments

  • Hermes Trismegistus
    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!