Lab 3 Implications of Inheritance

advertisement
Session 6
Comments on Lab 3
&
Implications of Inheritance
Accumulator Example
• a simple calculator app
• classes needed:
– AdderApp - contains main
– AddingFrame - GUI
– CloseableFrame - allows X
button
– Accumulator - internal
representation and
implementation of the
accumulator
Refactoring Accumulator
public class AccumulatorV2 {
private int currentSum;
private int currentNumber;
private int displayNumber;
public Accumulator() {
clear();
}
public void clear() {
currentSum=0;
currentNumber=0;
displayNumber=0;
}
public void addDigit( int digit ) {
currentNumber=currentNumber*10+digit;
displayNumber=currentNumber;
}
public void plus() {
currentSum+=currentNumber;
prepareForNextNumber();
}
public void minus() {
currentSum-=currentNumber;
prepareForNextNumber();
}
private void prepareForNextNumber() {
currentNumber=0;
displayNumber=currentSum;
}
public int getDisplay() {
return displayNumber;
}
} // end class AccumulatorV2
Alternative structure of the program
• But another way to structure this program would be to
create a relationship which is “wide and shallow”
– AdderApp creates an an instance of Accumulator which it
passes to an instance of AddingFrame.
public class AdderApp
public static void
Accumulator a =
AddingFrame f =
f.show();
{
main( String[] args ) {
new Accumulator();
new AddingFrame(a);
} // end main
} // end class AdderApp
– This is a good example of composition.
• We emphasize that AddingFrame is composed of an Accumulator
– This is a good example of writing code that is modular.
• Now that we know the composition relation, we can compose
solutions using variations of Accumulator.
CountedAccumulator Solution
public class CountedAccumulator extends Accumulator {
private int numberOfOperations;
public CountedAccumulator() {
super();
// calls the superclass’ constructor
numberOfOperations=0;
}
public void plus() {
super.plus();
numberOfOperations++;
}
public void minus() {
super.minus();
numberOfOperations++;
}
public int getOperations() {
return numberOfOperations;
}
} // end class CountedAccumulator
CountedAccumulator Solution
• Now, before we can really work with this
we need to modify other files in our
application.
• We need to set up the AddingFrame so that
it works with a CountedAccumulator rather
than a regular Accumulator. We do this in
the AdderApp class for simplicity.
Accumulator a = new CountedAccumulator();
AddingFrame f = new AddingFrame(a);
A solution
• Why do we do this in the AdderApp rather than
leave it alone and modify the AddingFrame?
– Because in the end this makes our AddingFrame
slightly more versatile.
– Think about it...AddingFrame works with an
Accumulator (or CountedAccumulator). If one is
provided, it uses it. If one is not provided, it creates it.
– THAT, is more versatile than telling an AddingFrame to
now always create a CountedAccumulator.
Lab 3 Exercise
Create a class named EvenOddAccumulator that
subclasses Accumulator to implement this
behavior.
EvenOddAccumulators respond to all the same
messages as regular Accumulators. But, in
response to plus() and minus() messages, an
EvenOddAccumulator both computes the new
sum and writes a congratulatory message if the
sum is even.
Toward a Solution
Here is the critical new piece of the
EvenOddAccumulator class:
if ( currentSum % 2 == 0 ) {
System.out.println( "Hurray! You made
an even number." );
}
The big question is, what else is a part of the class?
Toward a Solution
• Here where I thought you would get into
trouble during Lab 3 yesterday…
A Problem Accessing Inherited Data
$ javac EvenOddAccumulator.java
EvenOddAccumulator.java:17: currentSum
has private access in Accumulator
if ( currentSum % 2 == 0 )
^
EvenOddAccumulator.java:24: currentSum
has private access in Accumulator
if ( currentSum % 2 == 0 )
^
2 errors
Oops!
currentSum is declared as a private instance variable in class Accumulator.
private means private: no code outside the Accumulator class can access
that variable.
A Possible Solution for Accessing
Inherited Data
•
Change currentSum to be public or
protected.
public class Accumulator {
protected int currentSum;
...
}
A Better Solution
for Accessing Inherited Data
(2) Add a protected “accessor” method to the
Accumulator class. Use that method to access the
currentSum instance variable in the subclass.
public class Accumulator {
...
protected int getCurrentSum() {
return currentSum;
}
}
Then use getCurrentSum() in EvenOddAccumulator.
Programming with Inheritance
Inheritance is an object-oriented programming
construct that enables us to add behavior to
an existing system without modifying the
existing classes.
Programming with Inheritance
Our new EvenOddAccumulator class adds behavior to a program
that uses Accumulators without modifying:
• the behavior of the existing Accumulator class or
• the existing AddingFrame class!
That means...
• No chance of introducing an unnecessary, unexpected errors
into the working Accumulator class.
• No need to modify programs that use instances of
Accumulator but which don’t need instances of
EvenOddAccumulator.
• The ability to use EvenOddAccumulators in programs that
expect to use Accumulators.
Programming with Inheritance
We could have achieved some of these results without using
inheritance by creating a new class named
EvenOddAccumulator that simply duplicated the behavior
of existing Accumulator class.
Using inheritance means that...
• No need to reimplement existing methods.
• No need to duplicate code.
One of the most important features of object-oriented
programming is that it encourages us to create new classes
that reuse existing code as much as possible. Without
inheritance, you have only one tool for doing that,
composition. With inheritance, you have two tools.
Polymorphism
• polymorphism comes from the Greek root for “many
shapes”
• polymorphism is about how we can use different objects
in the same place in our program, i.e., polymorphism
depends on objects that are substitutable for one another
• A polymorphic variable can hold many different types of
values
• Object-oriented languages often restrict the types of values
to being subclasses of the declared type of the variable.
Polymorphic Variables in Java
•
Java achieve polymorphic variables by two ways:
1) Interfaces: a variable defined using an interface can hold an object
of any class implementing that interface, e.g., in MemoPad,
“MemoDatabase datebase” could be assigned either a
DefaultMemoDatabase or MyMemoDatabase object.
2) Inheritance: a variable defined using a superclass can hold any
instance of a subclass, e.g., in AdderApp:
public class AdderApp {
public static void main( String[] args ) {
Accumulator a = new CountedAccumulator();
AddingFrame f = new AddingFrame(a);
f.show();
} // end main
} // end AdderApp
Implications of
Inheritance/Polymorphism
• At compile-time, the amount of memory for
polymorphic variables cannot be determined, so all
objects reside in the heap
• Because values reside in the heap, reference semantics is
used for assignment and parameter passing
• Most natural interpretation of equality is identity. Since
programmers often require a different meaning two
operators are needed
• Garbage collection needed since it is hard for a
programmer to know if/when an object is no longer
referenced
Typical Memory Layout
Heap
Stack
Global
variables
Program
Stack-based Memory
Main:
ObjA a = new ObjA();
ObjB b = new ObjB();
a.do(5, b)
public class ObjA {
int x = 100;
public void do (int y, ObjB myB) {
int loc = 6;
int t = myB.doMore(loc);
...
}
public class ObjB {
int z = 30;
public int doMore(int i) {
z = z + i;
return z;
}
}
}
• Objects are stored on the heap
• When a method is called, an activation record is allocated on the
stack to hold:
– return address (where to return after execution)
– parameters
– local variables (stuff declared in the method)
• When a method returns, the activation record is popped
Consider Factorial Example
class FacTest {
static public void main (String [] args) {
int f = factorial(3); // *
System.out.println(“Factorial of 3 is “ + f);
}
static public int factorial (int n) {
int c = n – 1;
int r;
if (c > 0) {
r = n * factorial(c);
} else {
r = 1;
}
return r;
}
}
// **
Download