CSUN Animal inheritance Program Discussion
Description
Animal Inheritance Program
Project Prompt
In this project, we will write a program that will create a virtual zoo of animals. This zoo will contain all sorts of information on various different types of animals, such as an their height (in feet), how fast they can move (miles/hour) in different types of environments (land, sky, water), and their unique properties (how high it can jump, how deep it can dive, etc.). The user will input the exact number of animals for our zoo to contain. From this number, the animals that get added to the zoo will be randomly chosen from a selection of four possibilities: Squirrels, Pelicans, Crabs, and Lobsters. Regardless of the random selections, every animal will have all of its information printed out in a clean and organized fashion for easy reading.
What does this project cover?
This project is going to cover everything from the course in regards to inheritance. This includes code reusability, parent/child class relationships, the “super” and “protected” keywords, abstract classes, interfaces, overriding, overloading, casting, and packages. It will also cover topics from previous lectures, such as static/object classes and input error checking.
How to begin working on this project
In the previous project, we were introduced to using multiple code files: two classes. But in this project, we are now going to use NINE code files: one static class, four object classes, one abstract class, and three interfaces:
Running.java
Interface
Flying.java
Interface
Swimming.java
Interface
Animal.java
Abstract Class
Squirrel.java
Object Class
Pelican.java
Object Class
Crab.java
Object Class
Lobster.java
Object Class
Project6.java
Static Class
This might seem overwhelming and intimidating, but don’t worry. A majority of these files are very short in length, and will be provided to you from the help files. The only file to focus on creating is Project6.java.
Before writing any code out, make sure to first download the “Project 6 Help Files” that are posted in Canvas. Like with Project 5, this is a ZIP file that contains helpful files that will assist in writing the project out. One of these files is an image called “InheritanceDiagram.png”. It demonstrates the flow of inheritance that this program will follow. The solid black lines represent the standard “extends” inheritance between classes. And the dotted black lines represent the special “implements” inheritance between classes and interfaces. Remember that the direction of the arrows is always going from the child to the parent. Therefore, the order in which code is being re-used (from the parent to the child) is the exact opposite.
Also included is a folder titled “zoo”. This is the coding “package” which contains all 8 of the object/interface files that will be used in this project. Leave the code files in this folder as is. The folder will be placed in the same location as Project6.java in order for the program to run. (Similar to how LineOfText was required for Project5.java in the previous project). The Inheritance Diagram is essentially a blueprint for the code design of these Java files.
The following is a detailed analysis of all eight object/interface classes. This includes their designs, their functions, and their purposes:
The Three Interfaces
In general, interfaces are far easier to code out (and smaller in size) than classes. The only statements that go inside of them are method signature stubs, and nothing else. In the case of this project’s three interfaces, just ONE statement goes inside of each one. There are “Running”, “Flying”, and “Swimming”, each of which are nouns. Their verb equivalents (“run”, “fly”, and “swim”) are their respective method stubs. Each one is “public void”, and each one takes no input arguments inside of their parenthesis. And that’s it, as far as interfaces go.
What purpose do these super-short files serve? Interfaces act as “contracts” to the classes that implement them. Each implementing class must write out its own definition of the interface’s method stub. In other words, they must “implement” their own version of that method, in order to be used within the program. This implementation allows the object created from their classes to utilize different types of behaviors during runtime. This is more formally known as “polymorphism”.
The Animal Abstract Class
The Animal class is considered “abstract” because it acts as a hybrid between an object class and an interface. It is like an interface, in that it can contain method stubs (known here as abstract method stubs). And it is like an object class, in that it can contain object variables, constructors and regular methods. The drawback to abstract classes, however, is that they themselves cannot create object instances during runtime. Instead, the purpose of abstract classes is to be inherited by one or more object classes, where the object instances that they create all share identical behaviors at runtime. (Again, this is another example of polymorphism.)
In the case of this project, the Animal abstract class is used as the basis for the Squirrel, Pelican and Crab classes. Conceptually, all of these creatures are animals, and therefore share common animal behaviors between them. This abstract class contains three members of its own. The first is an integer variable called “number”, where the inheriting classes access it directly (via protected security). The second is a constructor that takes in one integer input argument, and assigns it to the integer variable. And the third is a method signature stub called “showProfile”. This “public abstract” method takes no input arguments, and returns nothing back (void).
The Object Classes
When inheriting the Animal abstract class, each of the object classes inherit two members to access and use: the number variable, and the showProfile abstract method. They also inherit specific interfaces, based on their creature’s natural abilities. Given all of these inherited method signatures, each animal object class defines their own unique versions of each method in order to function properly.
Animal?
Running?
Flying?
Swimming?
Squirrel
Yes
Yes
Yes
No
Pelican
Yes
No
Yes
Yes
Crab
Yes
Yes
No
Yes
This table illustrates the natural abilities (Interfaces) of each animal (Class), which are connected via implementation. Squirrels can run and fly, but not swim. Pelicans can fly and swim, but not run. And crabs can run and swim, but not fly. As a result, each of these classes implements three methods: one from the extended abstract class, and one from each of the two implemented interfaces. Inside each of these nine methods lies a single “println” statement that is written to the screen. The sentence printed depends on the name of method, and the class calling upon it.
showProfile()
“This is a [animal-name] that is [value] feet tall.”
run()
“This [animal-name] can run at [value] miles/hour.”
fly()
“This [animal-name] can fly at [value] miles/hour.”
swim()
“This [animal-name] can swim at [value] miles/hour.”
Here, the “animal-name” is the name of each class’s animal. These are written directly in each print statement, with no variables necessary. However, the “value” is concatenated in the middle of the statement. This value is different, based upon each method implemented within each class.
showProfile()
run()
fly()
swim()
Squirrel
number
number * 2
number / 2
N/A
Pelican
number
N/A
number * 2
number / 2
Crab
number
number / 2
N/A
number * 2
For showProfile, “number” is concatenated directly into the string. For the other methods, the formulas can either be assigned to an integer variable and concatenated into the string, or just concatenated into the string directly. (This project’s code files concatenate the formulas directly.) Keep in mind that the division being done in these formulas is integer division. For example, if an object’s number equals 11, then “number * 2” produces 22. Likewise, “number / 2” produces 5, instead of 5.5.
The following is what each object class uses as a template for their class signatures:
public class [class] extends [abst-cls] implements [intf-1], [intf-2] {
Object classes “extend” one other class (object or abstract), and “implement” one or more interfaces. The “extension” is always written before the “implementation”, and commas are ONLY used to separate the interface names. In situations where there are no classes to extend, the word “extends” is omitted. And in situations where there are no interfaces to implement, the word “implements” is omitted. Fortunately, neither of these is used in this project’s three object classes:
public class [class] extends [abstract-class] {
public class [class] implements [interface-1], [interface-2] {
In addition to the three methods, each of the three object classes has their own unique object variables, their own unique constructors, and their own unique methods.
Squirrel
The Squirrel class has a unique protected integer variable called “distance”. Its constructor takes in two integers as input arguments. The first is passed into the mandatory “super()” statement as an argument, and the second is assigned to our distance variable. The super statement is required, because of the existence of the parent’s constructor (where the inherited number variable is assigned). The unique method is called “jump”. It takes no input arguments, returns nothing back, and contains just one println statement: “This squirrel can jump [distance] feet high.”, where distance is concatenated in.
Pelican
The Pelican class has a unique protected integer variable called “depth”. Its constructor takes in two integers as input arguments. The first is passed into the mandatory “super()” statement as an argument, and the second is assigned to our depth variable. The unique method is called “dive”. It takes no input arguments, returns nothing back, and contains just one println statement: “This pelican can dive [depth] feet deep.”, where depth is concatenated in.
Crab
The Crab class has a unique protected integer variable called “time”. Its constructor takes in two integers as input arguments. The first is passed into the mandatory “super()” statement as an argument, and the second is assigned to our time variable. The unique method is called “pinch”. It takes no input arguments, returns nothing back, and contains just one println statement: “This crab can pinch for [time] seconds.”, where time is concatenated in.
These unique methods illustrate the individuality that each animal has, which stands in comparison to their identical inherited methods.
You might be wondering why there are no getter/setter methods listed in these object classes. It is because your Project5 static class will not use them. Thus, they are unnecessary.
The Lobster Class
You might also be wondering why I mentioned four object classes earlier, yet only covered three of them so far. That is because the fourth object class (Lobster) demonstrates a more traditional parent/child class relationship with the Crab class. So far, all of the inheritance has either been an object class extending an abstract class, or implementing an interface. But now, we have an example of a child object class extending a parent object class, where both classes have the capability to create their own unique objects at runtime. To put it simply, a Lobster is a child of a Crab. (I know that this isn’t actually true, but let’s just assume that it is for the sake of this project.)
public class Lobster extends Crab {
When Lobster extends Crab, it is also extending Animal, as well as implementing the Running and Swimming interfaces. However, these additional extensions and implementations do not need to be written inside of Lobster’s signature. This is because the Crab class takes care of it already. Therefore, Lobster inherits EVERY member from the Crab class, without needing to re-implement anything. This includes the two variables and the four methods – all of which the Lobster objects can call upon as if they were local. However, the Lobster class includes a few of its own members. This includes one variable, one constructor, and four methods. Two of these methods “override” existing Crab methods, one of them “overloads” an existing Crab method, and the last one is entirely unique to the Lobster class.
The Lobster class has a unique protected integer called “food”. Its constructor takes in three integers as input arguments. The first and second are passed into the mandatory “super()” statement as arguments (which are then passed into the Crab constructor, and later passed into the Animal constructor). And the third argument is assigned to our food variable.
The first overridden method is Lobster’s version of “public void showProfile”. This method is identical to Crab’s, in that it has no input arguments, and no return type. What makes it different is the single print statement inside of it: “This is a lobster that is [number] feet tall.”, where Animal’s number is concatenated in. The reason for overriding Crab’s version of “showProfile” is because that version of the method starts the print statement with “This is a crab…”, which is incorrect for Lobster objects. Therefore, by having the child class override the parent class’s method with an identically named method, this printing problem is remedied whenever Lobster objects call upon the “showProfile” method.
But what about situations where the opposite should occur? In other words, how can the child class override the parent class’s method, but still access that overridden method? This is what the Lobster class’ second overridden method achieves: Lobster’s version of “public void pinch”.
This method has two statements inside of it. The first calls upon the parent class’s version of “pinch()” by using the “super” keyword: “super.pinch();”. This prints the Crab’s version of the output. The second statement in the method then outputs a correction to the previous print statement: “…except that it is actually a lobster.”. As a result, Lobster overrides Crab’s pinch method, while simultaneously accessing the same method from within its own pinch method.
Let’s shift gears now from overriding to overloading. The Lobster class features a duplicate version of “public void pinch” inside of it. This version of the method features an integer input argument (called “extra”). As a result, Lobster objects are capable of differentiating between two different types of “pinch” method calls: one with no arguments, and one with an integer argument. This is what overloading achieves. Like the previous pinch method, this new one features two statements. Except that both are println statements, and one of them uses Crab’s time variable:
“This lobster can pinch for [time] seconds.”
“Using force extends it by [extra] seconds.”
At this point, the concepts of overriding and overloading should seem familiar. This is because we’ve actually been using them in previous projects. The toString method is an example of overriding, and empty println statements are an example of overloading.
Lobster’s final method is called “eat”. This method is unique to the Lobster class in that only Lobster objects can call upon it. No other object can. It takes no input arguments, returns nothing back, and contains just one println statement: “This lobster can eat [food] shrimp/day.”, where food is concatenated in.
This concludes the analysis of the Lobster class, and by extension, concludes the detailed analysis of all eight “zoo package” classes.
Refer back to the “Project 6 Help Files” from earlier. The third file included is called “TestTheAnimals.java”. Similar to the “test” java program from project 5, this one runs a demonstration that showcases what each of the zoo classes is capable of achieving. To run this demonstration, make sure that the Java file and zoo folder are in the same directory. (Do NOT put the “test” java file in the folder, or else it will not run.) From there, compile/run the “test” file (this will also compile all eight zoo classes automatically). Since this code is being provided to you from the instructor, there should not be any errors. (If there are however, please report them to the instructor immediately.) Here is what the output looks like:
Output from TestTheAnimals.java:
*** PART 1 – SQUIRREL ***
This is a squirrel that is 6 feet tall.
This squirrel can run at 12 miles/hour.
This squirrel can fly at 3 miles/hour.
This squirrel can jump 15 feet high.
*** PART 2 – PELICAN ***
This is a pelican that is 8 feet tall.
This pelican can fly at 16 miles/hour.
This pelican can swim at 4 miles/hour.
This pelican can dive 20 feet deep.
*** PART 3 – CRAB ***
This is a crab that is 10 feet tall.
This crab can run at 5 miles/hour.
This crab can swim at 20 miles/hour.
This crab can pinch for 25 seconds.
*** PART 4 – LOBSTER ***
This is a lobster that is 12 feet tall.
This crab can run at 6 miles/hour.
This crab can swim at 24 miles/hour.
This crab can pinch for 30 seconds.
…except that it is actually a lobster.
This lobster can pinch for 30 seconds.
Using force extends it by 4 seconds.
This lobster can eat 35 shrimp/day.
*** END OF TESTING ***
Project6
At this point, we are now ready to begin working on Project6.java. Before we begin writing our main method out however, let’s take a moment to reflect on the object classes that we will be using in this program. Unlike the previous project, which used a large number of various Java object classes, this project will only use two Java object classes – the same two that we’ve been using for the majority of the course: the Scanner class (import java.util.Scanner;) and the Random class (import java.util.Random;).
We must also import the “zoo” package, in order to utilize all of the files from the included folder. Why? Because our static class will make use of all eight data type keywords within its execution (including the interfaces). This import can be achieved by one of two means: Either import all eight classes/interfaces, line by line:
import zoo.Animal;
import zoo.Crab;
import zoo.Flying;
import zoo.Lobster;
import zoo.Pelican;
import zoo.Running;
import zoo.Squirrel;
import zoo.Swimming;
…or instead, import all of them at once, using the asterisk shortcut:
import zoo.*;
…either way is acceptable for this project. Once the import statements are completed, we can now move on to writing the code inside of the class signature.
Within the Project6 class, we will create two static methods:
The Main Method
The Error Method
The Error Method
This static method can be written by copy-pasting the same one from the previous project. The error method is meant for immediately terminating the program’s runtime, and for displaying a reason as to why. Because our project will feature multiple potential errors that could occur, this method helps reduce the number of times that the above statements need to be written throughout our static class.
The Main Method
As a type of nostalgic throwback to our earlier projects, this project’s main method will be structured in a more traditional “procedure-oriented” format:
Storage
Input
Calculation & Output Part 1
Calculation & Output Part 2
Calculation & Output Part 3
Storage
For our storage, we will declare and assign both a Scanner object (input), and a Random object (generator). The Scanner will be our traditional keyboard input Scanner, and the Random will be our standard random number generator.
Input
The input for this project will be our simplest to date: just a single integer, and nothing else. First, we prompt the user to type something in: “Enter a length between 5 to 10: “. Then, we check the scanner to see if it does NOT “have a next int”. If this negative check comes up true, then we make a call to our static “error” method, where we pass in the message “Non-integer input.”. (The error method itself will take care of exiting out of the program for us.) In the event that the negative if-statement never triggers, then we proceed on as normal, where the “next int” is assigned into a new integer variable (length). Next, we flush the input out (input.nextLine();) and then check the range of the length.
The valid range for lengths is between 5 to 10. So any length less than 5 OR any length greater than 10 will be invalid. We will write negative check for this condition, where its execution will once again call the “error” method with the message “Invalid length.”. Should the negative if-statement never trigger, then we proceed on as normal, where we now close our Scanner object (input.close();) and create an array of Animal objects (animals). This is a 1-dimensional array with a length of the “length” number we just scanned in. At this point, we are now ready for part 1 of our calculation and output.
Calculation & Output Part 1
Why did we create the animals array, when “Animal” is an abstract class that cannot exist as an object? Because we will not be filling the array with Animal objects. Instead, we will be filling it up with objects that are created from its descendant classes: Squirrel, Pelican, Crab, and Lobster. Thanks to polymorphism, it is possible for a child class’s object to be assigned into a parent class’s variable.
The very first statement we will type is a println statement that will clearly identify this section in the output: “*** Part 1 ***”. Next, we will create a standard for-loop statement, with variable “i” starting at 0 and ending before “animals.length”. At the very start within the for-loop, we will write a descriptive println statement that displays the current animal’s index value: “Animal at index [i]:”, where the variable “i” is being concatenated into the string. Following this, we will use our random number generator to generate four random numbers. These may be declared as either four int variables, or as an int array of length four, the choice is yours. Either way, each number (or index) must be assigned independently. “number1” will be assigned a number between 1 to 4 (4 possible choices). “number2” will be assigned a number between 4 to 14 (11 possible choices). “number3” will be assigned a number between 15 to 35 (21 possible choices). And “number4” will also be assigned a number between 15 to 35, but NOT the same generated result that was assigned to number3.
Each of these four numbers will be used as a means of creating a randomly chosen animal object to add into the array, at the current index. We will use a switch/case statement (or if/else, the choice is yours) to evaluate number1. In case 1, we create a new Squirrel and assign it into the current index of the animals array. In case 2, we create a new Pelican. In case 3, we create a new Crab. In case 4, we create a new Lobster. In the default/else case, we call the error method with this message: “Invalid animal choice: [number1].”. If number1 is being properly generated, then the default case will NEVER execute. So if it does trigger, then it means that number1’s RNG is incorrect. What about numbers 2 through 4? These go into the input arguments of the object constructors. The first and second arguments for all four object constructors will be number2 and number3 respectively. And the third argument for the Lobster constructor will be number4.
Following the switch/case statement, we will have one more statement within the for-loop: the animal located at the array’s current index will make a method call to its “showProfile” method. The showProfile method stub originates from the Animal class. Despite this, the behavior of each method call will be entirely different! If the current animal is a Squirrel, then the Squirrel’s version of showProfile is called upon. However, if it’s a Pelican, then the Pelican’s version of showProfile is called upon instead. This is just one example of how polymorphism works with objects and their method calls.
Before continuing with part 2, make sure to compile and run the code right now as a progress checkpoint. If the code is correct, then you should be able to enter a length. If that length is correct, then the “profiles” of every randomly created animal will be displayed. You should be able to see each animal’s array position (0 through length minus 1), as well as the type of animal that they are, and how tall each one is.
Naturally, the animal types and heights are randomized, so there is no way to have your output match this output exactly as is. However, if it looks similar (with the same number of lines being printed), then you can safely confirm that part 1 is complete, and proceed onwards to part 2.
Sample Inputs/Outputs:
Enter a length between 5 to 10: 5
*** Part 1 ***
Animal at index 0:
This is a squirrel that is 9 feet tall.
Animal at index 1:
This is a pelican that is 10 feet tall.
Animal at index 2:
This is a crab that is 5 feet tall.
Animal at index 3:
This is a lobster that is 12 feet tall.
Animal at index 4:
This is a squirrel that is 10 feet tall.
Calculation & Output Part 2
Once again, the first statement we will type is a println statement that will clearly identify this section in the output: “*** Part 2 ***”. Next, we will create another standard for-loop statement, the exact same as part 1’s. (You can even copy-paste it here.) At the very start within the for-loop, we will use the same descriptive println statement from the start of part 1’s loop: “Animal at index [i]:”. Next, we will use three independent if-statements. Each of these statements can trigger separately from one another. Within each one, we will check the animal located at the array’s current index to see if it is an “instance of” a given Interface. This will be accomplished using the special “instanceof” operator, where the format of the Boolean expression is “[object-variable] instanceof [class-or-interface]”. The first if-statement will check if the current animal is an instance of “Running”. The second will check if it’s an instance of “Flying”. And the third will check if it’s an instance of “Swimming”. Whenever a check triggers true, then the current animal must be casted into a variable of that Interface’s data type, and then use that interface’s implemented method. For example, here’s what happens with “Running”:
Running runner = (Running)animals[i];
runner.run();
These are the statements that go inside of each if-statement. Just replace the term “run” with “fly” and “swim”, for the 2nd and 3rd if-statements respectively.
As a result of these if-statements, we are seeing upcasting play out within the program. We have objects (stored as Animal data types) that are being casted as Interface data types, where they are then able to call upon the methods that they had implemented. This happens, despite the fact that the Animal class itself does not implement any of the Interfaces directly. Ultimately, every animal object will end up calling upon two of the three Interface method calls (due to every animal object implementing two Interfaces each). Whenever they do, their own unique versions of the Interface methods are used. In total, one of six different statements is capable of printing out per iteration within the loop, once again demonstrating the complexity and brilliance of polymorphism within our program.
Despite this, it should be noted that Lobster objects will use the “run” and “swim” methods they inherited from the Crab class. As such, they will print out messages that start with “This crab can…”, despite being Lobsters. However, this is the correct behavior, as these methods are purposely meant to be used by Lobster objects.
Before continuing with part 3, make sure to compile and run the code right now as a progress checkpoint. If the code is correct, then all of part 1’s output should print out, in addition to part 2’s output. From this newly added section, you should be able to see each animal’s array position, what type of animal they are, and the speeds of their “implemented” actions.
The nice thing about part 2’s output is that it can be checked with part 1’s. For example, is Animal 2 the same type of animal in both output parts? Let’s say that it’s a pelican. Can it fly (20) twice as much as its height (10)? And can it swim (5) half as much as its height (10)? If the answers to these questions are all yes (based on each type of animal), then you can safely confirm that part 2 is complete, and proceed onwards to part 3.
Sample Inputs/Outputs:
Enter a length between 5 to 10: 5
*** Part 1 ***
Animal at index 0:
This is a squirrel that is 9 feet tall.
Animal at index 1:
This is a pelican that is 10 feet tall.
Animal at index 2:
This is a crab that is 5 feet tall.
Animal at index 3:
This is a lobster that is 12 feet tall.
Animal at index 4:
This is a squirrel that is 10 feet tall.
*** Part 2 ***
Animal at index 0:
This squirrel can run at 18 miles/hour.
This squirrel can fly at 4 miles/hour.
Animal at index 1:
This pelican can fly at 20 miles/hour.
This pelican can swim at 5 miles/hour.
Animal at index 2:
This crab can run at 2 miles/hour.
This crab can swim at 10 miles/hour.
Animal at index 3:
This crab can run at 6 miles/hour.
This crab can swim at 24 miles/hour.
Animal at index 4:
This squirrel can run at 20 miles/hour.
This squirrel can fly at 5 miles/hour.
Keep in mind that the speeds of each animal’s actions have different correlations to their height. Refer to the third table (located near the beginning of this prompt) to figure out which specific speed values to check for. And remember that odd integers divide down (example: 11 / 2 = 5).
Calculation & Output Part 3
Just like before, the first statement we will type is a println statement that will clearly identify this section in the output: “*** Part 3 ***”. Next, we will create the same for-loop statement from parts 1 and 2. And like part 2, we will write out the same println statement at the start of the for-loop: “Animal at index [i]:”. But unlike part 2, we will not be using a series of independent if-statements. Instead, we will create an if/else-if statement, featuring two else-if statements. Here, we will be checking to see if the animal located at the array’s current index is an instance of a given animal object class. The if-statement will check if the current animal is an instance of “Squirrel”. The first else-if statement will check if it’s an instance of “Pelican”. And the second else-if statement will check if it’s an instance of “Crab”. Why are these three statements now dependent on each other, when the statements from part 2 were independent? The reason why these if/else-if statements need to be structured together is because each animal can only be of one Object class data type. Therefore, only one of the if/else-if statements will ever trigger for a given object. This is the same reason why an else-statement does not need to be attached to the end here. Whenever a statement is entered, the current animal must be casted into a variable of that Object class’s data type, and then use that class’s unique method. For example, here’s what happens with “Squirrel”:
Squirrel squirrel = (Squirrel)animals[i];
squirrel.jump();
These are the statements that go inside of each if/else-if statement. Just replace “Squ
Have a similar assignment? "Place an order for your assignment and have exceptional work written by our team of experts, guaranteeing you A results."