Chapter 6: A Closer Look at Methods and Classes (pp. 201-250) |
Session 6Access specifiers Passing objects to methods
Returning objects from methods Overloading Overloading constructor and chaining constructors with this() Overloading both constructor and method varargs - variable number of arguments static keyword Enumerations - the enum keyword |
We cannot fly a plane that does not yet exist. The purpose of a constructor is to initialize an object at creation time. A method of the class to which an object belongs can be called on an object only AFTER the object has been instantiated. Analogy:
// call the constructor for the form of
PaperAirplane I want
PaperAirplane pa1 = new PaperAirplane(paper8by11, folds15);
// now we can call the fly method ON the object that exists
in JVM memory
pa1.fly(north);
Analogy: we can only ask a waiter to bring us water AFTER the waiter has been born, or hired, that is, "created" or "instantiated". Therefore, in the main method we might write the following.
Waiter waiter7 = new Waiter("Philippe Garcon");
waiter7.bringWater(2);
The main method is like the CEO of a corporation who says: First, let's hire some waiters, and then, let's tell them to do work - see CEOAtUpscaleRestaurant.java.
In the example above, both the constructor for Waiter and the bringWine method are overloaded.
The fields, constructors, and methods are defined OUTSIDE of main, so that main can focus on its executive function.
The three classic buzzwords in object-oriented programming are encapsulation, polymorphism, and inheritance. Let's look at encapsulation.
The cell wall encapsulations all the members that are private to the cell. The function of the wall or encapsulation is to protect the cell from external threats. | A medicine capsule encapsulates its ingredients or components. |
Active ingredient = 1% Filler for bulk = 42% Gell or binder = 15% Stabilizer = 20% Sugar = 10% Food coloring = 10% |
Encapsulation of the RCA hand-cranked portable turntable of 1939:
A Java class encapsulates its members (fields, constructors, and methods), such that all the logic for, say, ColdCapsule, is in the definition of the ColdCapsule class. The UML (unified modeling language) diagram uses a box to indicate class-level encapsulation.
For the automobile class, the engine is normally encapsulated under the hood, and access is not public.
In 1955, when no one was worried about someone siphoning away 25-cent-per-gallon gasoline from their Chevrolet, the gas cap did not have to be locked, but, for aesthetic reasons, it still was encapsulated behind the tail light.
When gasoline became scarce in the fall of 1973, and no car was allowed to fill up on two consecutive days, gas tank access became private.
Encapsulation is central to our economy of thinking.
Even this prototype of the iMac, though transparent, still has protective encapsulation.
The internals of the engine are encapsulated, and drivers do not want to monitor each cycle of each piston.
Instead, the driver wants a simple public interface, that is, set of public methods, such as pushDown() and release() that can be called on the pedal objects.
Keep It Simple: Similarly, when we withdraw money at an Automatic Teller Machine (ATM), we think, "show me the money" or "transfer the money to the checking account", and do not want to be bothered with the detailed logic of the encapsulated steps involving network authentication, application business rules, and database updates as integral multi-step transactions that can be rolled back if interrupted:
Encapsulation keeps things simple for the user, and also allows flexibility for the provider, who is free to change the IMPLEMENTATION behind the private wall without having to let the user know. For example, these users do not know that their every paw-press is monitored by the NSA.
The most basic access control modifier is
private. This example demonstrates the
protective action of the keyword
private.
A private member can only be accessed from within the class in which it is
declared.
In this particular example, AccessDemo.java, the route of access to the
field, alpha, is though its "getter" (accessor) and "setter" (mutator) methods.
The following examples related to salary give a realistic use cases but, for the sake of simplicity, do not have realistic implementations.
Corporation X allows the payroll department to find out what the salary is with read-only access so that payroll can print the paycheck. Therefore, the payroll department uses the getSalary() method, but does NOT have direct access to the private field, salary, and therefore cannot change the amount of the salary. See the AccessSalaryDemo.java example. SUMMARY: The keyword private makes a field or method have scope limited the class in which the field or method is declared.
Note: It would not make sense to have a class be private because that means no that class in the application, even the class with main, could make use of the code. The exception to this rule is the inner class, an advanced topic that applies to handling events within a user interface - http://docs.oracle.com/javase/tutorial/java/javaOO/innerclasses.html.
Let us suppose that an organization allow application programmers within the Human Resources Department to set the private salary field. See the HumanResourceDemo.java example.
Application programmers within the Payroll department might be able to call the getSalary method, but not the private setSalary method or reassign the value of the salary field.
Or perhaps the Human Resources department can grant the predefined 10 percent raise by calling the applyRaise method, but not call the setSalary method to assign an arbitrary salary. See the AccessSalaryDemo2.java example.
Note: These kinds of restrictions could be implemented by using the protected access modifier, which we will discuss in Week 8 http://www.write-technical.com/126581/session8/session8.htm#protectedfromforeignpackages
PassAnObjectToAMethod.java uses an object as the runtime argument to a method call.
The sameBlock method takes an object.
Generally speaking, the object that is passed to a method or a contructor has an identifer. However, an unnamed object can be passed to a method or constructor. See PassAnObjectToAMethodWithoutIdentifier.java.
The use case for a nameless object is if you only need to use it one time.
ErrorInformationDemo.java on line 29 demonstrates that you can define a constructor to take an object as its format parameter. In other words, Java supports both primitive parameters and reference type (object) parameters. Unlike a primitive, any object, such as a String, is a reference type.
A primitive type is simple. It does not have a field. Its value is immediate, and resides on the Java "stack". An object is complex, and its field values reside on the Java "heap". An object can become quite large, but passing values associated with an object my reference means that only the reference, not the object itself, is passed. If two objects point to the same place in JVM memory, changing the field value on one object also changes that field value on the second object.
Let look at a code example:
An object is an instance of class, and an object can be of a custom data type. We saw that a method can take an object as a parameter. Similarly, an object can also be the return value of a method. See ObjectReturnForSodaPopDemo.java.
See ObjectReturnDemo.java. The keyword return
in line 13 fulfills the "contract" that the method returns an object of type
Test.
Note: Although the compiler enforces a method's return type, but it does require
that code subsequently make use what the method call returned.
In ObjectReturnForAutomobileDemo.java, a method returns an object of type Automobile. In this case, the method calls a constructor to obtain the object it returns.
Method overloads rely on the concept of a signature that differentiates.
A signature can take many forms, and now we are talking about polymorphism.
The elaborate regal symmetry of
Shakespeare's Queen Elizabeth, Elizabeth R (regina is Latin for Queen):
The presidential/military/regimented perfection of George Washington, even at age 12:
The stylistic perfection, confidence, and equilibrium of presidential actor Ronald Reagan:
The over-reaching inventiveness and finality of Thomas Edison:
The over-reaching downfall of Waterloo's Napoleon:
The far-reaching flare of rope-throwing, spontaneous, candid cowboy John Wayne:
The cosmic upwardness of Albert Einstein:
The downward lowliness of Hitler:
The downward roots-in-evolution and survival of the fittest of Charles Darwin, author of The Descent of
Man:
The erasure of metaphysics and earthly pleasure in philospher Immanual Kant:
The self-erasure of existential philospher Jean-Paul Sartre:
Martyr Martin Luther King, whose ending is both a downfall and a fading gesture
toward eternity:
A signature has a specific form that reflects its purpose.
A signature is considered a unique form, and can be used any number of times.
Overloading is when a method (or constructor) has more than one signature. Overloading is useful for slightly different but fundamentally almost-identical situations. Overloading simplifies the management of names while providing reasonably flexibility. Imagine a non-polymorphic environment in which a bank had to have all of the following method names:
SUMMARY: The signature of a constructor or method concerns the list of the formal parameters. The compiler enforces signature uniqueness of each overload by checking the formal parameters (and matching runtime arguments) for:
The return type is NOT relevant to method overloads, and a constructor does not have return type.
The concept of overloading exists in natural speech.
So that program code can easily evolve and adapt to various needs, object-oriented programming offers a feature calls polymorphism. The evolutionary biologist might say that polymorphism is the engine of life on planet earth. The basic necessities are common, but the ways in which they are satisfied are many.
Let us consider polymorphism in biology for what we might call the display method: | |
Only 6% of jaguars are black jaguars, using melanin to conceal the default spots to provide a better camouflage() method in the dense forest. The default spots are well suited for the light brown savannah with dried grasses. | |
Monarch butterflies are typically orange, but 10% on
Oahu, Hawaii are white, probably so that predators have no bright color
to easy recognize them. This is a different form of the display() method. |
|
Mallota flies mimic bees so that preditors think they have a nasty stinger and taste bad, so they have a different form of lookLikeABee() than an actual bee has | |
The cuckoo chick has hatched first and tossed out the reed
warbler eggs, so the reed warbler mother executes her
feedChick() method on a different form, happy to feed a bird of a
different species. It is as if normally the
feedChick() method normally has a verify species parameter but
in this case does not because the only parameter is an open mouth. feedChick(boolean isSameSpecies) feedChick(openMouth anySpecies) |
Polymorphism enabled Baby Huey's constructor to initialize him to be large, a highly advanced adaptation that ensures his caregiver does not fail to notice his needs:
Donald Duck is constructed with for the Navy, with a sailor uniform, and, unlike innocent Micky Mouse for the kiddies, was designed to be capable of irritability and anger, so he might appeal to adults.
Daffy Duck is Warner Brother's response to the irritability of Disney's Donald Duck be being even more outrageous.
Similarly, designing an application is much easier if its behavior can adapt to slightly different situations. Overloading a method or a constructor is less complicated that having to have a separate name for each combination of method name and method signature.
Overloading is the primary way in which Java allows you to implement the object-oriented concept of polymorphism: many forms for the same thing. For example, you might want your bank to allow you to open an account even if you do not deposit money, or to open an account that initially has associated with it not money but a credit card number. openAccount(217.34) versus openAccount() versus openAccount(visaCard555555555)
So we have three constructors, one with double, one with no arg, and one with int. That easier than having openAccountWithDouble, openAccountNoArg, openAccountWithInt.
SUMMARY: A parameter list provides a unique signature to the compiler through the combination of:
Here, we see that a boolean followed by an int is different from an int followed by a boolean.
Sometimes it is convenient to have one name for something even it takes several slightly different forms.
The English words "drive" and "ride" are polymorphic, because there is a different form of driving or riding, depending on the class of the vehicle.
If the only difference is that between one car and another car, the drive method remains the same (except driving a manual transmission has an additional parameter of gearShiftedTo that an automatic transmission encapsulates privately).
When driving different cars, such as a 1932 Citroen (when headlights were rationed and hand-cranked ignition was cheaper than electric ignition) versus the 1960 "Sahara" version with two engines (one in front and one in the rear, and hence no room for spare tire in the trunk), we have a difference in properties (data), but not in behavior.
Polymorphism has to do with behavior (methods).
We "greet" different people with different levels of formality. We behave in
different forms, using multiple form, or many forms: poly-morphic.
It makes our language more simple when we can use the same word for closely
related forms.
The same word means different things in different contexts.
Otherwise, we would need to have many more words, and that would be
inconvenient.
Java has its counterpart to this flexibility of natural language.
The compiler looks for the "signature" of a method. Multiple methods can have
the same method name, and yet have different signatures.
For example:
We say that myMethod() is overloaded, and has six signatures.
The following does not compile because the method signatures are not unique
in type, number, and order. The parameter list is int, String in both cases:
For signature uniqueness, it does not matter what the return type is, nor what name
is assigned to the formal parameters.
A signature consists of the:
Therefore, a method with one argument can have multiple signatures with one argument if that argument has a different data type for each signature.
During a runtime call in main, the code specifies the method and method signature, but not its return type. The return type is specified in the method definition. Therefore, at runtime, the return type is not relevant for determining which overload definition to run.
Here, we use all three classic buzzwords of object-oriented terminology:
OverloadMethodDemo.java demonstrates overloading a method.
PassAnObjectToAMethod.java is an example in which the method definition requires an object as a parameter (line 13). Therefore, the method calls in lines 30-32 have runtime arguments that were instantiated in lines 25-27.
To enable objects of the same class to be initialized with different properties (data or state), Java permits overloaded constructors. A class can have 0, 1, or many custom constructors. This technique support a fundamental aim of object-oriented programming: code reusability. The initial constructor takes care of the common logic. Subsequent constructors can add specialization.
ConstructorWithThisDuckDemo.java is
an example of constructor
overloading has two constructors that take a single argument.
Which are the lines the code that call these two constructors, and how does the
javac
compiler know the difference between the signature of the two constructors?
OverloadedConstructorDemo.java also has constructor chaining.
Another example of chaining one constructor to another, that is, to have a constructor BEGIN by calling basic constructor with this() on line 12, and then adding more initialization logic AFTER the call to the basic constructor.
Polymorphism maximizes code reuse while allowing for specialization. One manifestation of this is constructor chaining, where the initial constructor has the most general and common code, and one or more additional constructors supplement it.
We have seen this.myMethod() and this.myProperty.
Note: Next week, we will see super() as the way for the constructor of this class to call the constructor of its superclass. The rule of unique signature applies to this() and super().
In the following example, the initial constructor does the minimum initialization. The subsequent constructor added additional initialization. This avoids writing the logic of lines 9 and 10 two times. However, each constructor call does incur the delay of object creation.
ThisConstructorDemo.java:
If your constructor uses
this(), it must be the initial line in that
constructor.
The goal is to reuse existing code as
a basis, and then add some specialization.
It would not make sense to add specialization and then get the basis.
In building constructor, first we lay the foundation, then we add the
distinctive walls.
Similarly, it would not make sense to start as a brain surgeon, and then go get
the initial medical degree.
You can overload a constructor or a method any number of times if each overload has a unique signature, that is, a unique set of parameter types, parameter order, and number of parameters.
OverloadTestDemo.java:
In Week 4 (p. 139) we learned about the default constructor. If you do not define any constructor(s) for a class, Java automatically
provides a default constructor that has no arguments.
However, if you define any constructor(s) for a class, then you must define them
all, and Java provides no default constructor.
Even if you define what looks like the default constructor, that is, with no
parameters, it is a custom constructor and there is no longer any default
constructor.
In this example, we use a custom constructor that has the same signature as the default constructor, that is, no parameter (lines10-15). However, this custom constructor does custom initialization because it assigns hard-coded values to box objects. It is common for a class to have multiple constructors.
SUMMARY: A recursive method is a method that calls itself repeatedly, and each time reduces the problem to a simpler version.
The logic of recursion is somewhat analogous to that of a loop in which each iteration moves toward the terminating condition.
The following example has two palindromes, which are sentences that makes sense whether read forward or backward. To process each character in the string, instead of using loop iterations, we can increment the index (line 16) within the string, which is a char array internally. Each increment moves toward the terminal condition of having processed each character in the string. When we arrive at the index that represents the final letter, that is, the length of the string minus one (line 12), we cease the recursive call. The useLoop method arrives at the same result through loopiterations instead of recursive method calls.
The output is:
str.length() - 1 is the terminal integer: 22
recursive call index equals 0
recursive call index equals 1
recursive call index equals 2
recursive call index equals 3
recursive call index equals 4
recursive call index equals 5
recursive call index equals 6
recursive call index equals 7
recursive call index equals 8
recursive call index equals 9
recursive call index equals 10
recursive call index equals 11
recursive call index equals 12
recursive call index equals 13
recursive call index equals 14
recursive call index equals 15
recursive call index equals 16
recursive call index equals 17
recursive call index equals 18
recursive call index equals 19
recursive call index equals 20
recursive call index equals 21
nam esol d'I ,dlos emaN
for loop equivalent: rise to vote Sir
Another example is
Factorial.java.
Factorial:
5! = 5 * 4 * 3 * 2 * 1
Note that factorial 0 = 1 by definition.
Let's see how concise a recursive program can be:
To avoid an infinite loop, each call needs to gravitate toward the terminating case, also called the base condition, otherwise nothing returns. In Factorial, the terminating case is n being equal to 1 (line 10). Each call gravitates toward that terminating case by decrementing the value of n by 1 (line 12).
Notes:
SUMMARY: Sometimes it is convenient to avoid overloading a method. The vararg syntax (type ...) permits a flexible (or variable) number of arguments to be passed in at runtime. An example is VarArgsAllowsMethodToAcceptFlexibleNumberOfArguments.java. To avoid ambiguity, the varargs is the final argument in the method signature.
VarArgsFinalArg.java demonstrates that the String argument can precede the variable number of integer arguments, but a String cannot be the final argument. This is because the variable argument type must be the final type.
In VarArgsExample.java, the sumArrays method can take a variable number of integer arrays. Here we pass in five arrays with the method call, but we pass in any number of arrays, such as zero, one, or seven.
The modifier keywords include
http://java.sun.com/docs/books/tutorial/reflect/class/getModifiers.html
The HelloWorld.java program is the first
place we saw the keyword static.
The main() method must be static because
the Java Virtual Machine needs to call the main()
method BEFORE any objects exist. If you forget to make main static, the runtime
cannot main because it would expect any method to require being on an object,
which would mean calling a constructor to get an object BEFORE calling a method
on that object.
If a class has a static member (variable or method), then that method is
independent of an object.
Static members exist before the coming and going of objects. They are in a
different place in memory than the instance variables.
Static members are efficient for memory: there is only one copy, no matter how
many objects exist, Java stores static values in a separate place.
In the sense of
their always being there, they are outstanding or pre-standing or standing by (standing in Latin is
static).
main must be static so that execution can begin. Note that we never call main, we only define main. Normally, a method is only available on an object, and we would first have to create an object. We would have to say HelloWorld hw = new HelloWorld(); but that still leave us the problem of which method would contain the call to the constructor. (Static is Latin for "standing, having status". A static member has a special status: it stands already ready, independent of the initialization of an object.)
This is analogous to the paradox of "Which came first, the chicken or the egg". The answer is that static came first. You can begin with either a static egg or a static chicken, or make both static. Static means something does not need to have a beginning. Static means something is always already there at the beginning.
A French anthropologist found a "native" tribe that did not have radio or television. There were not impressed with Coke commercials. They considered any program with people talking or people moving to be unreal. "Those are not people." What they did consider real, is static. "We recognize the full pulse and rhythm of the natural world." |
The tribe understood that static pre-exists the specific orders of the main method.
It is as if there were a static method, makeRandomNoise() from which the accidents begin that provide the random mutations that make possible the occasional adaptations that drive nature, and can be observed over long periods of time as evolution. As if the uncertainty principle (instead of intelligent design) is the creator.
But on the other hand, you can view static as the opposite of noise: it is in static main that you deliberately drive your program in linear fashion.
For Java, we have a static main, and we do not need a HelloWorld() constructor to initialize the HelloWorld object prior to making use of the main method.
Why does StaticMethodLimitationDemo.java fail to compile?
Because a static method needs to be able to run independent of object creation, and therefore any fields in the method definition must be static.
Does StaticFieldAccessDemo.java call a constructor? Perhaps a default constructor? Is an object of the StaticField class created and initialized? This example uses a static field to force a field to have a specified value. Suppose you want a program that automatically sets a maximum value, such as maximum number of stories for a skyscraper.
CountInstances.java has a static field and a static method. What does line 27 do? Why don't we write something like the following?: CountInstances myCI = new CountInstances();
Let's situate the keyword static within the context of a discussion about methods.
myObject.methodOnThatObject();
myObject.propertyOnThatObject;
A class is both a compilation unit and a unit of encapsulation.
A class typically has members: methods, properties (instance variables), and
constructors.
Unlike a local variable, an instance variable is on every instance of an
object.
It is common to say, "This is a property
of the class", and "This is a property
on the object".
Objects have data (instance variables) and behavior (methods).
An instance variable is data that is also a property (also called an attribute or a field).
Static variables and methods are at the class level and independent of the creation of an instance of that class. Static variables are not re-created for each instance. Instead, they are already available when an instance comes into being.
Java does not support direct concatenation of methods in the way Unix shell scripting uses the pipe ( | ), which goes left to right.
ls -f | more
Instead of a data pipe running from left to right, think of communication, or
messages running, like Arabic numerals, from right to left.
like when you add starting from the right column, then moving to the column to
the left
356
+ 509
---------
You can pass the return value of one method as an argument to another method:
myObject.UseOutputOfAnotherMethod( myObject.MethodOnThatObject() );
Method nesting is unlimited:
myObject.UseOutputOfAnotherMethod( myObject.MethodOnThatObject( anotherObj.someMethod() );
The sequence of method calls goes from right to left, which is the mathematical norm.
Can a static method return a value? StaticMethodDividesByTwo.java does just that.
Note: There is no static constructor because the meaning of static is that no constructor is necessary.
Note: A class cannot be static unless it is a nested class, which is an advanced topic.
Enumerations
(keyword is enum), which are a collection of
static constants.
http://download.oracle.com/javase/tutorial/java/javaOO/enum.html
Enums are a collection, but unlike an array, the rules about the values are more strict. You could use an enum to list the names of the planets if you want that list to be locked down and not change, which is the example at http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html
In effect, the values of an enum constitute a unique data type. This is "type safe" compared to allowing the values to be Strings or integers, which can have arbitrary values. For example, you could have an enum for PrintedBill that allowed 1, 2, 5, 10, 20, 50, 100 but would not allow a 3 dollar bill.
More details at http://javarevisited.blogspot.com/2011/08/enum-in-java-example-tutorial.html, which uses the example of Penny, Nickel, Dime, Quarter for currency.
EnumTest.java sets the names of the days of the Week
In EnumDemo4.java, the Apple enum stores a controlled set of values for an Apple object.
A typical use case for an enum is a controlled vocabulary of values that describe the state of a process. The example, EnumForAntStatus.java, uses the advanced concept of an enum map, which is economical for storage and performance - http://download.oracle.com/javase/1.5.0/docs/api/java/util/EnumMap.html