Answered step by step
Verified Expert Solution
Link Copied!

Question

1 Approved Answer

Widget and WidgetDriver Thoughts on Classes Lets look at classes. We will generate a blueprint and driver together. You will be turning these in as

Widget and WidgetDriver

Thoughts on Classes

Lets look at classes. We will generate a blueprint and driver together. You will be turning these in as part of your module 4 assignment. You certainly can save time by copying and pasting, but understand what you are doing as you proceed. When you first start studying Java, it is hard to distinguish between instance variables and local variables but it is critical that you do so since they have different properties and uses. To carefully distinguish, I will often call the instance variable a field. A field is an attribute of a class. It is initialized by Java if we do not do it ourselves and exists for as long as the object does. A variable is a temporary memory location that is defined inside of a method and only exists for the length of the method. Java will not initialize these for you, and you often get variable may not have been initialized errors.

A class has only two things --- fields and methods. How do you mentally create a class? First consider the attributes that you would like to maintain for the objects of that type of class. For example lets assume I want to sell widgets (I have no clue what a widget is but programmers LOVE selling them J). What attributes do I want to keep for them? How about widget id, number in stock, the price each costs, and, some reorder level (the minimum I want in stock). We always make fields private unless there is some reason not to. So.. so far we have:

public class Widget {

private int id;

private int numInStock;

private double costEach;

private int reorder;

}

We need to define our constructors. These are used to create objects from this class blueprint that we are currently creating. In reality, you often have five, six or more of these. But for now, we are just creating two- one the no-argument constructor (sometimes called the empty constructor) that is used to create an object of this type before you fill in the values for the fields and a full constructor that is used when you have all of the data. Here they are:

public Widget()

{

}

public Widget(int i, int n, double c, int r)

{

id = i;

numInStock=n;

costEach=c;

reorder=r;

}

Just like we looked at in Chapter 5, we will have method definitions and method calls. The things in the parentheses in the method definitions are called the parameters. When I later use this method, the things in parentheses after the method call are called the arguments. It does not matter what I name the things in parentheses above since they only exist as long as the method call, but it is critical that they match the data types of the original fields (ex id is an integer so i must also be one). Often this second one is written as

public Widget(int id, int numInStock, double costEach, int reorder)

{

this.id = id;

this.numInStock = numInStock;

this.costEach=costEach;

this.reorder=reorder;

}

Note: this is the same as:

public Widget(int i, int n, double c, int r)

{

id=i;

numInStock = n;

costEach=c;

reorder=r;

}

If you name your parameters the same name as the fields (first of the two examples above), you need to distinguish which is the field and which is the parameter, so we say this.id=id; which means to take the parameter value for id and assign it to the field value (remember you are in the blueprint class so this.id means the id field in this blueprint). If you use unique names for the fields and parameters as in the second case above, you do not need to use the word this.

We always need a toString method. I will insist that you write one for every class you generate. This method is for writing out a widget object. We all know how to write out the number 8, but how do you write out a widget? This method must return a String. So how about

public String toString()

{

return "There are " + numInStock + " widgets with an id of " +

id + " which cost " + costEach + " each. The reorder level is " +

reorder;

}

(Note that Strings can span multiple lines).

We need to generate the getters and setters that are public. This is how other classes will get information about our widgets. A getter retrieves a piece of information about an object and a setter sets or changes a piece of information about an object. Your software will generate these for you. Right-click in the source area and choose Source generate getters and setters and choose which ones you wish them to be generated for (usually all of them).

Often we need an equals method, especially if we are ever going to need to search for an item. Again, I know if the number 8 equals the number 8 but what does it mean for two widgets to be equal? Or two Employees? For our widgets, we will assume that if their id values are the same, they are the same.

The name of the method is equals and it returns a boolean data type. I need to pass it a parameter of the other widget that I want to compare to this one. So the signature will be

public boolean equals(Widget w)

{

}

But how do I refer to each of these in the method? The one I passed in is w and I can get to its fields by using the getters (so that is why we created them!!! J ). But remember we are in the Widget class, so the object that I am comparing w to, is the current one. The way we say the current one in Java is to use the word this. Hence, the method looks like:

public boolean equals(Widget w)

{

if (this.getId() == w.getId())

return true;

else

return false;

}

(Note: a common mistake is the if statement with primitive versus reference data types. If my widgets had names and I wanted to compare these, the if statement would be if (this.getName().equals(w.getName()))

Now what other methods do we need? Depends on the enterprise application, but how about one that prints out all widgets below a certain reorder level. We will call it belowLevel (note that methods are always named with small letter on the first word and capital on all others).

When first writing methods, it is often hard to know when you need parameters and when you do not. Here is a great way to think of it. First determine what fields you created in your class. If they have some information that you need, use it and do not also make a parameter or local variable for it!! If it is information that someone else (like the driver program) needs to supply, make it a parameter. If it is truly a temporary memory location to store data only for the length of the method, make it a local variable.

For our example, to find those items below the reorder level, I need to know how many are in stock and what the reorder level is. Since both of these are fields, I do not need any parameters.

Now what data type should this method return? Often a beginning object oriented programmer will answer all the items below the reorder level. WRONG! You are in the Widget class and we will use the constructors in this class to create a single instance of the class. Therefore in this class, we are dealing with a single widget and can only return information about that single widget. To get all of them, I will need a loop around this method when I call it in the driver.

How about returning a booelan? How about

public boolean belowLevel()

{

if(numInStock

return true;

else

return false;

}

Note that methods in the class can directly address the fields even if they are private so I do not need to use getters and setters. Some people would write the following but it is not at all necessary!

public boolean belowLevel()

{

if(this.getNumInStock()

return true;

else

return false;

}

Now lets assume that if you buy a lot of widgets, I will give you a discount. The question becomes --- is the discount always the same (ex if you buy over 100, the cost is decreased by 10%) or can it vary? Lets assume that at times we do each.

The first one (if you buy over 100, the cost is decreased by 10%), we need to know the cost each (a field) and how many you buy (not a field). So we will need one parameter (plus the 10% constant). It should return a double. How about:

public double discount(int numBought)

{

double cost;

final double DISCOUNT = 10;

if (numBought >100)

cost = numBought * costEach*((100-DISCOUNT)/100);

else

cost = numBought * costEach;

return cost;

}

Note above that I made a local variable named cost to temporarily hold a calculation until I return it. Also note that costEach was a field so I just used it in the method, but numBought was not so I had to pass it in.

But sometimes I want to give a different discount. This would be information that I provide. I need to overload the method.

public double discount(int numBought, double percent)

{

double cost;

if (numBought >100)

cost = numBought * costEach*((100-percent)/100);

else

cost = numBought * costEach;

return cost;

}

Now lets work on the driver. We need it to run so we put a check mark to include the main method when we first add it to our package. The outline is

public class WidgetDriver {

public static void main(String[] args) {

}

}

Lets create some Widget objects!!! How about first one where we do not know the information yet.

Widget w1 = new Widget();

If we did not fill in the information, do the fields have values? Yes Java initializes fields but not variables. So the id = 0 as is the numInStock, the reorder, and the priceEach. Make certain somewhere along the line, that these values get set. If I create a second one with the default constructor, it would also have an id of 0 which is not good. Maybe we need Java to create a unique id for us and not do it ourselves so we do not end up with two different types of widgets with the same id number. Lets do this because it illustrates a number of other principles.

Go back to your Widget blueprint class. We need a class field (belongs to the whole class) to maintain this. Remember the word static means for the whole class. Every time a widget is created we will use this to create the id for us. Lets assume that we want our ids to start at 100. Go back to the Widget class and make the following changes:

private int id;

private int numInStock;

private double costEach;

private int reorder;

private static int nextId=100;

public Widget()

{

id=nextId;

nextId++;

}

public Widget(int n, double c, int r)

{

numInStock=n;

costEach=c;

reorder=r;

id = nextId;

nextId++;

}

Note that the full constructor now has one less parameter since Java will create the id for us. (We may also need the old version of the full constructor for reading already existing widgets from files but more on that later).

Now back to the driver (the WidgetDriver class). If I now have the two lines of code in the main method:

Widget w1 = new Widget();

System.out.println(w1.toString());

Run the program. The output is:

There are 0 widgets with an id of 100 which cost 0.0 each. The reorder level is 0

Now lets create a widget where the information comes from the keyboard using the Scanner class. Do not forget your import statement import java.util.Scanner; at the top. We need to ask the user for three pieces of data and Java will create the id for us. We then need to call the full constructor to create the object.

Scanner scan = new Scanner(System.in);

System.out.println("Create a new widget. How many in stock?");

int num = scan.nextInt();

System.out.println("What is the reorder level?");

int reorder = scan.nextInt();

System.out.println("How much does each cost?");

double cost = scan.nextDouble();

Widget w2 = new Widget(num, cost, reorder);

System.out.println(w2.toString());

A number of points here. First it does not matter what order you ask the questions, but it does matter the order of the arguments. The method signature has parameters of

public Widget(int n, double c, int r)

so we need to provide the arguments to match int, double, int. The name of the arguments and parameters do not need to match and usually do not since often different developers are writing the different classes. When we run this portion of the program, we get

Let's create a new widget. How many are in stock?

100

What is the reorder level?

50

How much does each cost?

4.50

There are 100 widgets with an id of 101 which cost 4.5 each. The reorder level is 50

Note that this widget object was given the next number (101) unique from the first widget object. I now need to give my first widget values for the three fields. I will do this the easy way with constants. I will call the setters to do this.

w1.setCostEach(7.50);

w1.setNumInStock(100);

w1.setReorder(75);

Can we store more than one object? We will learn about arrays in the next module (stay tuned). Here we will just copy and paste the code. Arrays are collections of either objects or primitive data types. We will see that arrays get rid of the problem of trying to name each widget type a unique variable name (w1 and w2). We want a collection of widget objects.

Widget [] widgets = new Widget[2];

widgets[0]=w1;

widgets[1]=w2;

The first line creates an array of widget objects that can hold a maximum of two objects (one of the major disadvantages of arrays is that you must indicate the size when you define them. There are alternative collections to use where this is not the case but this is the easiest for us here). The second and third lines add our current widgets to the collection. The real advantage of this is that you can loop through the collection.

Anyhow, lets write a method to print out all of the widgets. Where does this belong? Answer in the driver class since it utilizes more than one widget. The Widgets class has methods relating to one widget only. What information is needed for this method? This array which will be our parameter. We then can loop through the array printing each. We want to make the method generic for any size array. So in the driver, we have a method that prints all the widgets and returns nothing. It must also be static or it can not be called from the main. Make certain that this is outside the } that ends the main method! In normal Java, you can not put a method inside of a method,

public static void printAll(Widget[] w)

{

for (int i=0;i

System.out.println(w[i].toString());

}

More details about these kinds of methods in the next module.

We now will want to call this from our main method. Since it is in the same class as the main method is, we do not use object.method() but just call the method passing it to our array that we created.

printAll(widgets);

Think about where this line belongs! In a Java class all lines of code are either field definitions or are part of a method! You can never have a line of code like this one above outside of a method. Since it is part of the running program, it belongs inside of the main method.

Now lets write a method that has more internal logic. I want to look through the array and see if a widget with a certain number exists. I will need to pass in the array and the number that I am looking for. I will search until I find it or until I get to the end of the array (i.e. I have looked through all of the objects). There is a property of arrays called length that tells you how large the array is. This is a property and not a method, so do not put () after the name. I will return a string that lists the information for the widget or indicates that the widget does not exist. Study this code - you will use similar logic in a later module!

public static String find(int num, Widget[] w)

{

boolean found = false;

int location=0;

String output = " ";

while (!found &&location

{

if (w[location].getId()==num)

{

found = true;

output=w[location].toString();

}

else

location++;

}

if(!found)

output= "The widget does not exist";

return output;

}

Study this logic: I created a local variable (only lasts for this method) named found of type boolean and I will search while !found (means not found) and the location that I am looking at is less than the length of the array (the end of the array). I create a String variable named output that I will return. Try changing the line from String output = " "; to String output;. Java will come back and say local variable may not have been initialized. Again Java does not initialize local variables for you!!! Next I check the ids and if they are the same, I found it! So I change the boolean value of the found variable and set the output variable to the toString() method of the correct widget. Otherwise, I keep looking. When I am done looking, it is either because I am at the end of the array or because I found it. So I check to see if it is because it is still not found. If so, I set the output string. I then return the output string.

To test this, add two lines (again - they must go inside of the main method) to your driver that says

System.out.println(find(100,widgets));

System.out.println(find(104,widgets));

Your program will find the first one and not the second.

Run the driver program. The output should look like:

There are 0 widgets with an id of 100 which cost 0.0 each. The reorder level is 0

Create a new widget. How many in stock?

100

What is the reorder level?

150

How much does each cost?

45

There are 100 widgets with an id of 101 which cost 45.0 each. The reorder level is 150

There are 100 widgets with an id of 100 which cost 7.5 each. The reorder level is 75

There are 100 widgets with an id of 101 which cost 45.0 each. The reorder level is 150

There are 100 widgets with an id of 100 which cost 7.5 each. The reorder level is 75

The widget does not exist

Step by Step Solution

There are 3 Steps involved in it

Step: 1

blur-text-image

Get Instant Access to Expert-Tailored Solutions

See step-by-step solutions with expert insights and AI powered tools for academic success

Step: 2

blur-text-image

Step: 3

blur-text-image

Ace Your Homework with AI

Get the answers you need in no time with our AI-driven, step-by-step assistance

Get Started

Recommended Textbook for

Database Application Development And Design

Authors: Michael V. Mannino

1st Edition

0072463678, 978-0072463675

More Books

Students also viewed these Databases questions