Computer Programming for Homeschool Students and Other Beginners

Programming Fundamentals using Java

What do we mean by Type?

Learn about the concept of type in general.  Also learn about the primitive types in Java.

Published:  June 29, 2008
By Richard G. Baldwin

Homeschool Programming Notes # Hs00308


 

Preface

General

This tutorial lesson is part of a continuing series that is designed specifically for teaching computer programming to homeschool students and their parents.  Even though the series is designed for homeschool students, everyone is welcome to use the lessons to learn computer programming.

In this lesson, I will explain the concept of type in general.  I will also teach you about the primitive types in Java.

Figures

I recommend that you open another copy of this document in a separate browser window and use the following links to easily find and view the figures while you are reading about them.

Supplementary material

I recommend that you also study the other lessons in my extensive collection of online programming tutorials.  You will find a consolidated index at www.DickBaldwin.com.

Introduction

Type-sensitive languages

Java and some other modern programming languages make heavy use of a concept that we refer to as type, or data type.  We refer to those languages as type-sensitive languages or strongly typed languages.

Not all languages are strongly typed.  In particular, some languages hide the concept of type from the programmer and automatically deal with type issues behind the scenes.

So, what do we mean by type?

One analogy that comes to my mind is international currency.  For example, many years ago, I spent a little time in Japan and quite a long time on an island named Okinawa (Okinawa is now part of Japan, but that wasn't the case when I was there in 1956.)

Types of currency

At that time, as now, the type of currency used in the United States was the dollar.  The type of currency used in Japan was the yen, and the type of currency used on the island of Okinawa was also the yen.  However, even though those two currencies had the same name, they were different types of currency, as determined by the value relationships between them.

The exchange rate

As I recall, at that time, the exchange rate between the Japanese yen and the U.S. dollar was 360 yen to the dollar.  The exchange rate between the Okinawan yen and the U.S. dollar was 120 yen to the dollar.  This suggests that the exchange rate between the Japanese yen and the Okinawan yen would have been 3 Japanese yen for each Okinawan yen.

Analogous to different types of data

Why am I telling you this?  I am telling you this to illustrate the concept that different types of currency are roughly analogous to different data types in programming.

Purchasing transactions were type sensitive

In particular, because these were three different types of currency involved with different exchange rates among the three, the differences in the types had to be taken into account in any purchasing transaction to determine the price in that particular currency.  In other words, the purchasing process was sensitive to the type of currency being used for the purchase (type sensitive).

Different types of data

Type-sensitive programming languages deal with different types of data.  Some data types involve whole numbers only (no fractional parts are allowed) while other data types involve numbers with fractional parts.

Some data types conceptually have nothing to do with numeric values, but deal only with the concept of true or false or with the concept of the letters of the alphabet and the punctuation characters.

An instance of a type
Think of the type specification as being analogous to the plan or blueprint for a model airplane.  Assume that you build three model airplanes from the same set of plans.  You will have created three instances of the plans.

We might say that an instance is the physical manifestation of a plan or a type.

Type specification

For every different type of data used with a particular programming language, there is a specification somewhere that defines two important characteristics of the type:

  1. What is the set of all possible data values that can be stored in an instance of the type?  (We will learn some other names for instance later.)
  2. Once you have an instance of the type, what are the operations that you can perform on that instance alone, or in combination with other instances?

Using mixed types

Somewhat secondary to this specification, but also extremely important, is a set of rules that define what happens when you perform an operation involving mixed types (such as making a purchase using some Japanese yen currency in combination with some dollar currency).

The short data type

For example, there is a data type in Java known as short.  If you have an instance of the short type, the set of all possible values that you can store in that instance is the set of all the whole numbers ranging from -32,768 to +32,767.

This constitutes a set of 65,536 different values, including the value zero.  No other value can be stored in an instance of the type short.  For example, you cannot store the value +32,768 in an instance of the type short in Java, because it is too large to fit.  If you need to store that value, you will need to use some type other than short.

Sort of like an odometer

The concept of whole number (integer) types is somewhat analogous to the odometer in your car (the thing that records how many miles the car has been driven).  For example, depending on the make and model of the car, there is a specified set of values that can appear in the odometer.  The value that appears in the odometer (usually) depends on how many miles your car has been driven.

It is fairly common for an odometer to be able to store and to display the set of all positive values ranging from 00000 to 99999.  If your odometer is designed to store that set of values and if you drive your car 100000 miles or more, it is likely that the odometer will roll over and start back at 00000 after you pass the 99999-mile mark.  In other words, that particular odometer does not have the ability to store a value of 100000 miles.  Once you pass the 99999-mark, the data stored in the odometer becomes corrupt.

Now let's return to the Java type named short

Assume that you have two instances of the type short in a Java program.  What are the operations that you can perform on those instances?  Here are some examples of the allowable operations:

There are other operations that are allowed as well.  In fact, there is a well-defined set of operations that you are allowed to perform on those instances, and that set of operations is defined in the specification for the type short.

What if you want to do something different?

However, if you want to perform an operation that is not allowed by the type specification, then you will have to find another way to accomplish that task.

For example, some programming languages allow you to raise whole number types to a power such as four squared, six cubed, nine to the fourth power, etc.  However, that operation is not allowed by the Java specification for the type short.  If you need to do that operation with a data value of the Java short type, you must find another way to do it.

Two major categories of type

Java data types can be subdivided into two major categories:

I will discuss these categories in more detail in the following sections.

Primitive types

Java is an extensible programming language

What this means is that there is a core component to the language that is always available.  Beyond the core component, individual programmers can extend the language to provide new capabilities.  The primitive types discussed in this section are the types that are part of the core language.  A later section will discuss user-defined types that become available when a programmer extends the language.

Subdividing again...

It seems that when teaching programming, I constantly find myself subdividing topics into sub-topics.  I am going to subdivide the topic of Primitive Types into four categories:

Hopefully this categorization will make it possible for me to explain these types in a way that is easier for you to understand.

Whole number types

With the exception of the boolean types, the whole number types, (often called integer types), are probably the easiest to understand.  whole number types are types that can be used to represent numeric data without fractional parts (integers).

Applesauce and hamburger

For example, consider purchasing applesauce and hamburger.  At the grocery store where I shop, I am allowed to purchase cans of applesauce only in whole number or integer quantities.

Can purchase integer quantities only

For example, the grocer is happy to sell me one can of applesauce and is even happier to sell me 36 cans of applesauce.  However, she would be very unhappy if I were to open a can of applesauce in the store and attempt to purchase 6.3 cans of applesauce.

Counting doesn't require fractional parts

A count of the number of cans of applesauce that I purchase is somewhat analogous to the concept of whole number data types in Java.  Applesauce is not available in fractional parts of cans at the store where I shop.

Fractional pounds of hamburger are available

On the other hand, the grocer is perfectly willing to sell me 6.3 pounds of hamburger.  This is somewhat analogous to floating-point data types in Java.

Accommodating applesauce and hamburger in a program

Therefore, if I were writing a program dealing with quantities of applesauce and hamburger, I might elect to use a whole number type to represent cans of applesauce and to use a floating-point type to represent pounds of hamburger.

Different whole number types

In Java, there are five different whole number types:

(Only the first four types in the above list are typically used to represent numeric data.  I will have more to say about the char type later.)

The first four types in the list differ primarily in terms of the range of values that they can accommodate and the amount of computer memory required to store instances of the types.

Differences in operations...

For the most part, you can perform the same operations on instances of all four whole number types.  Although there are some subtle differences in the operations that you can perform on them, I will defer a discussion of those differences until a more advanced lesson (for example some operations require instances of the byte and short types to be converted to type int before the operation takes place).

Algebraically signed values

All four of these types can be used to represent algebraically signed values ranging from a specific negative value to a specific positive value.

Range of the byte type

For example, the byte type can be used to represent the set of whole numbers ranging from -128 to +127 inclusive.  (This constitutes a set of 256 different values, including the value zero.)

The byte type cannot be used to represent any value outside this range.  For example, the byte type cannot be used to represent either -129 or +128.

No fractional parts allowed by byte type

Also, the byte type cannot be used to represent fractional values within the allowable range.  For example, the byte type cannot be used to represent the value of 63.5 or any other value that has a fractional part.

Like a strange odometer

To form a crude analogy, the byte type is sort of like a strange odometer in a new (and unusual) car that shows a mileage value of -128 when you first purchase the car.  As you drive the car, the negative values shown on the odometer increment toward zero and then pass zero.  Beyond that point they increment up towards the value of +127.

Oops, numeric overflow!

When the value passes (or attempts to pass) +127 miles, something bad happens.  From that point forward, the value shown on the odometer is not a reliable indicator of the number of miles that the car has been driven.  The same thing happens when you do integer arithmetic in Java and experience numeric overflow.

Ranges for each of the whole number types

Figure 1 shows the range of values that can be accommodated by each of the four whole number types:

Figure 1. Range of values supported by each whole number type.
byte 
-128 to +127
short 
-32768 to +32767
int 
-2147483648 to +2147483647
long 
-9223372036854775808 to +9223372036854775807

Can represent some fairly large values

As you can see, the int and long types can represent some fairly large values.  However, if your task involves calculations such as distances in interstellar space, these ranges probably won't accommodate your needs.  This will lead you to consider using the floating-point types discussed in the upcoming sections instead.

I will explain the operations that can be performed on whole number types in a future lesson.

Floating-point types

Floating-point types are a little more complicated than whole number types.  I found the following definition of floating-point in the Free On-Line Dictionary of Computing at this URL:

A number representation consisting of a mantissa, M, an exponent, E, and an (assumed) radix (or "base"). The
number represented is M*R^E where R is the radix - usually ten but sometimes 2.
So what does this really mean?

Assuming a base or radix of 10, I will attempt to explain it using an example.

Consider the following value:

623.57185

I can represent this value in any of the following ways (where * indicates multiplication):

.62357185*1000
6.2357185*100
62.357185*10
623.57185*1
6235.7185*0.1
62357.185*0.01
623571.85*0.001
6235718.5*0.0001
62357185.*0.00001

In other words, I can represent the value as a mantissa (62357185) multiplied by a factor where the purpose of the factor is to represent a left or right shift in the position of the decimal point.

Now consider the factor

Each of the factors shown above represents the value of ten raised to some specific power, such as ten squared, ten cubed, ten raised to the fourth power, etc.

Exponentiation

If we allow the following symbol (^) to represent exponentiation (raising to a power) and allow the following symbol (/) to represent division, then we can write the values for the above factors in the following ways. Note in particular the numbers in blue, which I will refer to later as the exponents.

1000 = 10^+3 = 1*10*10*10
100 = 10^+2 = 1*10*10
10 = 10^+1 = 1*10
1 = 10^+0 = 1
0.1 = 10^-1 = 1/10
0.01 = 10^-2 = 1/(10*10)
0.001 = 10^-3 = 1/(10*10*10)
0.0001 = 10^-4 = 1/(10*10*10*10)
0.00001 = 10^-5 = 1/(10*10*10*10*10)

In the above notation, the term 10^+3 means 10 raised to the third power and 10^-5 means ten raised to the negative fifth power.

The zeroth power

By definition, the value of any value raised to the zeroth power is 1.  (Check this out in your old high-school algebra book.)

The exponent and the factor

Hopefully, at this point you will understand the relationship between the value shown in blue (the exponent) and the factor introduced earlier.

Different ways to represent the same value

Having reached this point, by using substitution, I can rewrite the original set of representations of the value 623.57185 in the following ways.  It is very important to for you to understand that these are simply different ways to represent the same value.

.62357185*10^+3
6.2357185*10^+2
62.357185*10^+1
623.57185*10^+0
6235.7185*10^-1
62357.185*10^-2
623571.85*10^-3
6235718.5*10^-4
62357185.*10^-5

A simple change in notation

Finally, by making a simplifying change in notation where I replace *10^ by E I can rewrite the different representations of the value of 623.57185 in the following ways.

.62357185E+3
6.2357185E+2
62.357185E+1
623.57185E+0
6235.7185E-1
62357.185E-2
623571.85E-3
6235718.5E-4
62357185.E-5

Getting the true value

Floating-point types represent values as a mantissa containing a decimal point along with an exponent value, which tells how many places to shift the decimal point to the left or to the right in order to determine the true value.

Positive exponent values mean that the decimal point should be shifted to the right.  Negative exponent values mean that the decimal point should be shifted to the left.

Maintaining fractional parts

One advantage of floating-point types is that they can be used to maintain fractional parts in data values.

Accommodating a very large range of values

Another advantage of floating-point types is that a very large range of values can be represented using a reasonably small amount of computer memory for storage of the values.

Another example

For example (assuming that I counted the number of digits correctly) the following very large value

62357185000000000000000000000000000000.

can be represented as

6.2357185E+37

Similarly, again assuming that I counted the digits correctly, the following very small value

.0000000000000000000000000000062357185

can be represented as

6.2357185E-30

When would you use floating-point?

If you happen to be working in an area where you need to keep track of fractional parts (such as the amount of hamburger in a package), have to work with extremely large numbers (distances between galaxies), or have to work with extremely small values (the size of atomic particles), then you will need to use the floating-point types.

Two floating-point types

Java supports two different floating-point types:

These two types differ primarily in terms of the range of values that they can support.  Figure 2 shows the smallest and largest values that can be accommodated by each of the floating-point types.  Values of either type can be either positive or negative.

Figure 2. Range of values supported for each floating-point type.
float
1.4E-45 to 3.4028235E38
double
4.9E-324 to 1.7976931348623157E308

I will discuss the operations that can be performed on floating-point types in a future lesson.

Character type

Computers deal only in numeric values.  They don't know how to deal directly with the letters of the alphabet and punctuation characters.

Purpose of the char type

The purpose of the character or char type is to make it possible to represent the letters of the alphabet, the punctuation characters, the numeric characters, and many other symbols (such as ) internally in the computer.  This is accomplished by assigning a numeric value to each character, much as you may have done to create secret codes when you were a child.

Extended characters
Java also supports a capability for representing characters that won't fit in the standard char type, but that topic is beyond the scope of this tutorial lesson.

The char type

The character type named char uses a standard character representation known as Unicode to represent up to 65,535 different characters.

Why so many characters?

The reason for the large number of different characters is to make it possible to represent the characters making up the alphabets of many different countries and many different languages.

What are the numeric values representing characters?

As long as the characters that you use in your program appear on your keyboard, you usually don't have a need to know the numeric value associated with the different characters.

Representing a character symbolically

In Java, you usually represent a character to the program by surrounding it with apostrophes as follows:  'A'.

The Java programming tools know how to cross-reference that specific character symbol against the Unicode table to obtain the corresponding numeric value.  (A discussion of the use of the char type to represent characters that don't appear on your keyboard is beyond the scope of this lesson.)

I will explain the operations that can be performed on the char type in a future lesson.

Boolean type

The boolean type is the simplest type supported by Java.  It can have only two values:

The boolean type is commonly used in some sort of a test to determine what to do next, such as that shown in Figure 3.

Figure 3. Typical use of a boolean type.
if some test returns true, then
    do this
otherwise
    do that

There are several operations that can be applied to an instance of the boolean type.  I will discuss the operations that can be performed on the boolean type in more detail in a future lesson.

User-defined types

Extending the language

As mentioned earlier, Java is an extensible programming language.  There is a core component to the language that is always available.  Beyond this, different programmers can extend the language in different ways to meet their individual needs.

Creating new types

One of the ways that individual programmers can extend the language is to create new types.  When creating a new type, the user must define the set of values that can be stored in an instance of the type as well as the operations that can be performed on instances of the type.

No magic involved

While this might initially seem like magic, once you get to the heart of the matter, it is really pretty straightforward.  New types are created by combining instances of primitive types and instances of previously defined new types.

An example

For example, a String type, which can be used to represent a person's last name, is just a grouping of a bunch of instances of the primitive char or character type.

A Person type, which could be used to represent both a person's first name and their last name, might simply be a grouping of two instances of the user-defined String type.

The company telephone book

A programmer responsible for producing the company telephone book might create a new type that can be used to store the first and last names along with the telephone number of an individual employee.  Using this new type, the programmer could create an instance for each employee in the company.  (At this point, let me sneak a little jargon in and tell you that we will be referring to such instances as objects.)

A comparison operation

The programmer might define one of the allowable operations for the new type to be a comparison between two objects of the new type to determine which is greater in an alphabetic sorting sense.  This operation could be used to sort the set of objects representing all of the employees into alphabetic order.  The set of sorted objects could then be used to print a new telephone book.

A name-change operation

Another allowable operation that the programmer might define would be the ability to change the name stored in an object representing an employee.

For example when Suzie Smith marries Tom Jones, she might elect to thereafter be known as Suzie Jones, Suzie Smith-Jones, or Suzie Jones-Smith, or any one of a variety of other possibilities.  In this case, there would be a need to modify the object that represents her to reflect her newly elected surname.  (Or perhaps Tom Jones might elect to be thereafter be known as Tom Smith, in which case it would be necessary to modify the object that represents him.)

An updated telephone book

The programmer could use the name-changing operation to modify the object, use the sorting operation to resort the set of objects, and then print and distribute a modified version of the telephone book.

Many user-defined types already exist

Unlike the primitive types which are predefined, I am unable to give you much in the way of specific information about user-defined types, simply because they don't exist until the user defines them.

I can tell you, however, that when you obtain the Java programming tools from Sun, you not only receive the core language containing the primitive types, you also receive a large library containing several thousand user-defined types that have already been defined.  A large documentation package is available from Sun to help you determine the individual characteristics of these user-defined types.

The most important thing

At this stage in your development as a Java programmer, the most important thing for you to know about user-defined types is that they are possible.

You can define new types.  Unlike earlier procedural programming languages such as C and Pascal, you are no longer forced to adapt your problem to the available tools.  Rather, you now have the opportunity to extend the tools to make them better suited to solve your problem.

The class definition

The specific Java mechanism that makes it possible for you to define new types is a mechanism known as the class definition.

In Java, whenever you define a new class, you are at the same time defining a new type.  Your new type can be as simple, or as powerful as you want it to be.

An object of your new type can contain a very small amount of data, or it can contain a very large amount of data.

The operations that you allow to be performed on an object of your new type can be rudimentary, or they can be very powerful.

It is all up to you

Whenever you define a new class (type) you not only have the opportunity to define the data definition and the operations, you also have a responsibility to do so.

Much to learn and much to do

But, you still have much to learn and much to do before you will need to define new types.

There are a lot of fundamental programming concepts that we will need to cover before we seriously embark on a study involving the definition of new types.

For the present then, simply remember that such a capability is available, and if you work to expand your knowledge of Java programming one small step at a time, when we reach the point of defining new types, you will be ready and eager to do so.

Summary

In this lesson, I explained the concept of type in general.  I also taught you about the primitive types in Java.

What's next?

The next lesson will teach you how to use variables and literals.

Resources

General resources

Previous lessons in the series


Copyright

Copyright 2008, Richard G. Baldwin.  Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited.

About the author

Richard Baldwin is a college professor (at Austin Community College in Austin, TX) and private consultant whose primary focus is a combination of Java, C#, and XML. In addition to the many platform and/or language independent benefits of Java and C# applications, he believes that a combination of Java, C#, and XML will become the primary driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects and he frequently provides onsite training at the high-tech companies located in and around Austin, Texas.  He is the author of Baldwin's Programming Tutorials, which have gained a worldwide following among experienced and aspiring programmers. He has also published articles in JavaPro magazine.

In addition to his programming expertise, Richard has many years of practical experience in Digital Signal Processing (DSP).  His first job after he earned his Bachelor's degree was doing DSP in the Seismic Research Department of Texas Instruments.  (TI is still a world leader in DSP.)  In the following years, he applied his programming and DSP expertise to other interesting areas including sonar and underwater acoustics.

Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.

Baldwin@DickBaldwin.com

-end-