Sunday, March 20, 2011

Java Conversions


Conversions
Implicit conversions 
  • conversions which happen automatically
  • any primitive type value can be converted to a type which supports a larger value (widening primitive conversion)
  • implicit conversion occurs from integer to floating point values but not vice versa
  • you can use an object of one type wherever a reference to one of it's supertypes is required ie you can reference up the class hierarchy but not down
  • you can assign a null object reference to any object reference
Explicit conversion 
  • when one type cannot be assigned to another type through implicit conversion you can use the cast operator
Identity Conversion
  • any type can be converted to it's own type
  • only conversion allowed for boolean primitive type
Widening Primitive Conversion 
byte -> short -> int -> long -> float -> double
char -> int -> long -> float -> double
  • widening conversions of integer types preserve the exact original value of the number
  • runtime errors never occur as a result of widening conversion
  • which is why widening conversion does not allow byte and short values to be converted to char as the char type is unsigned while byte and short are signed; the byte and short would lose information
·                    byte  b =  126;
·                    short s = 1000;
·                    char  c;
·                    
·                    c = b;  // compile error: possible loss of precision
·                    c = s;  // compile error: possible loss of precision   
  • widening conversion of an int or long to a float may result in loss of precision however the new float value will be the correctly rounded equivalent of the original number
  • the same applies when a long is widened to a double
Narrowing Primitive Converson (JLS §5.1.3)
double -> float -> long -> int > char -> short > byte
  • narrowing primitive conversion may lose information about the overall magnitude of the number and may also lose precision
  • runtime errors never occur as a result of narrowing conversion because compile time errors occur if you try it; need to use cast operator
  • narrowing conversion loses all but the lowest bits (see Working with Binary, Octal and Hex numbers)
  • narrowing from floating-point numbers to integer numbers occurs within the following minimum and maximum values (values are rounded-toward-zero)
long:  -9223372036854775808..9223372036854775807
int:   -2147483648..2147483647
short: 0..-1
char:  0..65535
byte:  0..-1
  • if the floating-point value is NaN the result is an int or long value of zero
Widening Reference Conversion 
  • convert from any class, interface or array reference to an Object reference
  • convert from any class to any interface that it implements
  • convert from any class, interface or array type to a null reference
  • convert from any subinterface to any interface it extends
  • from any array to type Cloneable or type java.io.Serializable
  • from any array of references to an array of compatible reference types
  • the above conversions never produce a runtime error or require special action
You can't instantiate an interface reference as interfaces are always abstract
    SuperInterface si = new SuperInterface();   // compile-error
Narrowing Reference Conversion 
  • from Object to any other class, interface or array type
  • from any superclass to a subclass
  • from any non-final class to any interface as long as the class does not implement the interface
  • from any interface to any non-final class
  • from any interface to any final class providing the final class implements the interface
  • from any interface to any other non-superinterface and providing neither interface contains methods with the same signature
  • from any array of reference types to any other array of reference types as long as the types of each array are compatible under the Narrowing Reference rules
The above will be allowed at compile time but may throw a runtime ClassCastException if the types are not compatible
Summary
  • widening conversions do not require casts and will not produce compile or runtime errors
  • narrowing conversions require explicit casts. Will compile ok but may result in runtime ClassCastException errors
String Conversions
  • every other type, including null, can be converted to String
Method Conversion
  • each argument is converted to the type of the method parameters
  • widening conversion is implicit
  • narrowing conversion is not implicit (values must be cast)
Forbidden Conversions
  • reference to primitive
  • primitive to reference (excepting String)
  • null to primitive
  • reference or primitive to boolean
  • boolean to reference (excepting String) or primitive
  • one class to another unless they have a superclass/subclass relationship (excepting String)
  • final class to interface unless the final class implements the interface
  • class to array unless the class is Object
  • array to any class other than Object or String
  • array to any interface other than java.io.Serializable or Cloneable
  • interface to interface if they contain methods with the same signature
Example Code
class TestConversions {
    public static void main(String[] args) {   
    /** Widening **********************************************/   
    double d = 2.12345D;
    float f = 150.50F;
    long  l = 15000L;
    int   i = 55;
    char  c = 20;
    short s = 1000;         
    byte  b = 126;          
      
    // following compile ok
    System.out.println();
    System.out.println("Implicit Widening conversions:");
    System.out.println("------------------------------");
    System.out.println("  byte to short: \t -> " + (s=b) );
    System.out.println("   short to int: \t -> " + (i=s) );
    System.out.println("    int to long: \t -> " + (l=i) );
    System.out.println("  long to float: \t -> " + (f=l) );
    System.out.println("float to double: \t -> " + (d=f) );
   
    // following produce compile error: possible loss of precision
//    System.out.println("byte to char: \t -> " + (c=b) );
//    System.out.println("short to char: \t -> " + (c=s) );   
 
    // following compile ok with cast 
    System.out.println();
    System.out.println("Explicit Widening conversions:");
    System.out.println("------------------------------");   
    System.out.println("cast byte to char: \t -> " + (char)b );
    System.out.println("cast short to char: \t -> " + (char)s );
   

    /** Narrowing **********************************************/
   
    // following produce compile errors
    d = 150.234256421235489645;
   
    System.out.println();
    System.out.println("Implicit Narrowing conversions:");
    System.out.println("------------------------------");
//    System.out.println("double to float:  -> " + (f = d) );
//    System.out.println("  float to long:  -> " + (l = f) );
//    System.out.println("    long to int:  -> " + (i = l) );
//    System.out.println("   int to short:  -> " + (s = i) );
//    System.out.println("  short to byte:  -> " + (b = s) );   
    System.out.println("All require explicit cast");   

    // following compile ok with cast
            
    System.out.println();
    System.out.println("Explicit Narrowing conversions:");
    System.out.println("------------------------------");
    System.out.println("double to float:  -> " + (f = (float)d) );
    System.out.println("  float to long:  -> " + (l = (long)f) );
    System.out.println("    long to int:  -> " + (i = (int)l) );
    System.out.println("   int to short:  -> " + (s = (short)i) );
    System.out.println("  short to byte:  -> " + (b = (byte)s) );   
       
       
    /** Widening Reference ***************************************/
   
    SuperClass sc = new SuperClass();
    SubClass   sb = new SubClass();
    SubClassWithInterface sbi = new SubClassWithInterface();
    SuperInterface si;
    SubInterface subi;
    AlternateInterface ai;
    Object o;
    Cloneable cl;
   
    int[] arr = { 1,2,3 };
    SuperClass[] scArray = { new SuperClass(), new SuperClass() };
    SubClass[] subArray = { new SubClass(), new SubClass() };
   
    // following compile ok
    // note: cannot instantiate an interface
    System.out.println();
    System.out.println("Widening Reference conversions:");
    System.out.println("-------------------------------");
    System.out.println("          class to Object: \t -> " + (o=sc) );
    System.out.println("          array to Object: \t -> " + (o=arr) );
    System.out.println("       class to interface: \t -> " + (subi=sbi) );
    System.out.println("subinterface to interface: \t -> " + (si=subi) );
    System.out.println("       array to Cloneable: \t -> " + (cl=arr) );  
    System.out.println("   ref array to ref array: \t -> " + (scArray = subArray));
           
    // following do compile ok but throw runtime errors
   
    o = new Object();    
    sc = (SuperClass) o;
    si = (SuperInterface) o;
    arr = (int[]) o;
    sb = (SubClass) sc;
    si = (SuperInterface) sc;   
    ai = (AlternateInterface)sc;    // compiles ok, runtime error
    si = (SuperInterface)sc;        // compiles ok, runtime error
    
    }

}

class SuperClass {
    public String toString(){
        return "SuperClass";
    }
}

class SubClass extends SuperClass {
    public String toString(){
        return "SubClass";
    }
}

class SubClassWithInterface extends SuperClass
                            implements SubInterface {
                           
    public void method(){}                           
    public String toString(){
        return "SubClassWithInterface";
    }                           
}

final class FinalClass implements SuperInterface {
    public void method() {}
}

interface SuperInterface {
    public void method();
}

interface SubInterface extends SuperInterface {
    public void method();
}

interface AlternateInterface {
}
Traps
  • variables requiring narrowing conversion being passed to methods without using a cast
  • assigning a typed byte or short variable to a char variable

No comments:

Post a Comment