Conversion Between Types

Classes are distinct groupings of data attributes and methods which work with those data attributes. The data attributes are often complex, such as arrays, or even arrays of objects, and beyond that other "abstract data types" that we have yet to cover. But in terms of the simpler data types, we tend to work primarily with three primitives: ints, doubles, boolean, and also with the String class. And sometimes we expand the kinds of primitives we work with to include char and other number types such as short, long, and float.

These types often have to be converted between each other, and this is particularly true when working in a GUI environment in which most information is input as text, though it may have to be converted to numbers, for example.

"Parsing" Methods from "Wrapper" classes.

One way of converting between data type we have already often use is methods from what are termed "wrapper classes". Previous notes went into a bit more details, but here are some straight-forward examples:

String s = "true";
boolean b = Boolean.parseBoolean(s);

String s1= 32;
int i = Integer.parseInt(s1);

String s2 = 987.343;
double d = Double.parseDouble(s2);

The above techniques are important when moving user input from a GUI environment, usually through the getText() method of things like jTextFields, to the code behind the scenes.


And going back the other way, information has to be in the form of Strings to be put back on the GUI, usually through setText() methods of things like jTextFields.

To do this, all you have to do is add a string to the non-String type. And usually, you don't want anything to show except the number, so you just add an empty string with empty double quotes "". For example:

int x = 34;
userNameTextField.setText(x + "");


Converting Between String and Char

Another thing that often occurs within a GUI is that we want to work with chars rather than with Strings. But just becuase what is input to a jTextField is only on letter does not make it a char; rather it remains a String, in this case a one letter string. So to understand what we are to do to get a char it is important to understand a bit more about what a String is.

In fact, a String is an object of the String class. A String is actually not a primitive. We have kind of thought of it as being one up to now, because it sort of fits the mold. We can declare a String the same way that we can declare an int or a boolean. We simply go String s = "sdfsd". No new operator is needed as is needed with making other instances of other classes. In this way, the String class is unique, and there is nothing else like it in Java. As it turns out, we could declare a new String with the new operator, and later on we will probably look at the difference between treating a String as a primitive and treating it as an object.

For now just realize two things, first that a String is actually an array of chars, and secondly the String class has methods associated with it which we can use.

The String "hello world" is actually an array of 11 chars.

[0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10]
h e l l o w o r l d

One of the methods available to us with Strings is charAt(). charAt takes in an int value, and returns the char which is at that element of the array of chars which is the String.

So charAt(6) from the above String would return 'w', and charAt(5) would return a space.

So if a String intended to be treated as a char is input into a jTextField of a GUI form, in the code behind the scenes, in order to have it treated as a char, we take advantage of this charAt method:

char c = jTextFileld1.getText().charAt(0);

It's straight-forward to convert the other way, since the String is the "bigger type" and the char is the "smallerType", so if you had a char c = 'a', then you could give it to String s simply with s = c;




Conversions Between "Big" and "Small" Number Types
And Casting

One of the main reasons there are different primitive types is to conserve memory by not wasting too many bits on types which don't need them. An int for example is meant for integers which are within a pretty common range of values - roughly -2 billion to +2 billion. It can do so by using 32 bits. (The calcuation used to determine the exact range is 2^32, which is approximately 4 billion.) The Java data primitive called long can represent much much larger integer values, but it does so at a cost, since it requires 64 bits for each long value.

So what happens when you try to assign one primitive type to another, and they take up different amounts of memory. Well, if a variable of the smaller type is assigned to the bigger type, it's not a problem; a smaller thing can fit into a bigger thing. But the other way around it is not. Consider the following examples of different Java primitive signed integer types. (By "signed" we mean that the type can represent both negative and positive values.)

Type     Number of Bits       Range of Possible Values

short 16 -32,768 to 32,767

int 32 -2,147,483,648 to 2,147,483,647

long 64 -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807

And next consider the following assignments of values within range and outside of possible range for each of the types:

short s1 = 30000;

short s2 = 40000; //Error - not possible - out of range.

int i1 = -47000222;

int i2 = 2400000000; //Error - not possible - out of range.

long x = 923422342342342;

long y = -12342342342342349890809899879798; //Error -not possible - out of range.


So now consider trying to assign one type to another:

long z = 32;

int i = 45;

short s = 99;

s = i; //Error - "possible loss of precision"

i = z; //Error - "possible loss of precision"

The Java compiler will not allow this because of a possible loss of precision. Yes, in the cases above, the short could store 45, and the int could store 32, but what if the values were much larger than that, and in fact were outside of the range of the short or the int. So the compiler will simply not allow straight-forward assignment of a larger type to a smaller type. Though it will not allow straight-forward assginment, it will allow it through other means, and we've already seen one - the use of parsing functions, like Boolean.parseBoolean. But another way, for number kinds of primitives is "casting".


Casting is explicitly making a value of a bigger type to be held by a variable of a smaller type. The programmer has to make the decision to do so, and has to be conscious of the possible loss of precision. Here's how it works:

long zz = 32;

int ii = 45;

short ss = 99;

ss = (short) ii; //Ok, because casting used.

ii = (int) zz; //Ok, because casting used.


The output would be as expected: 32

So in the above example, casting was Ok to use, since we know that the values would be able to be properly held by each of the smaller data types.

But here's an example of the problem that can occur if the programmer is not careful:

int i = 34098798;

short s = (short) i; //Not a good idea because the int value is outside of the range of a short


The output would be problematic; indeed very unexpected: 20078