Core Java 2 - Volume I - Fundamentals, 5th Ed.
Interfaces and Inner Classes
Interfaces and Callbacks | Object Cloning | Inner Classes | Anonymous Inner Classes | Proxies
Interfaces
You'll be happy to find out that after the first five sections you have the basic building blocks for programming in Java. There are two advanced techniques left that you have to know in order to master Java and truly be a Java masher though. The first is getting to know and use Interfaces. An interface is just a small description of what a class should do, without telling you exactly how to do it. Huh? It's not bad, trust me.
An interface is merely a list of requirements that your class has to meet in order to use services that are supplied by other classes. A class providing a service, lets say a sort, needs to know that your class meets a certain specification in order to work. By using the implements keyword you are telling the compiler that you're hip to the requirements and will see that they're met.
Enough talkin, let's get concrete. Here's an interface...
public interface Comparable
{
int compareTo(Object other);
}
Not so bad, right? All it says is that if you want to implement the Comparable interface your class must declare it's intention to do so, and have a method called compareTo() that takes an Object as a parameter and returns an integer.
All methods in an interface are public (That's why you don't declare that explicitly)
Some interfaces have more than one method.
An interface NEVER has instance fields, that's for the classes that use it.
You can NEVER instantiate an interface.
Alright, now the big question, why bother? Well, using the above example, your class has to implement the Comparable interface in order to avail itself of the sorting service in the Arrays class. If you didn't comply and called the sort without implementing the interface the compiler would throw an exception (that's no good, more later). This way Java knows that your class has everything it needs to use the sort() method in Arrays...
Again, it's understood better if we bang out some code...
package chap6;
import java.util.*;
public class EmployeeSortTest
{
public static void main(String[] args)
{
Employee[] staff = new Employee[3];
staff[0] = new Employee("Harry Hacker", 35000 );
staff[1] = new Employee("Carl Cracker", 75000 );
staff[2] = new Employee("Tony Tester", 38000 );
Arrays.sort(staff);
// print out info on Emp objects
for (int i = 0; i < staff.length; i++)
{
Employee e = staff[i];
System.out.println("name= " + e.getName() + " salary= " + e.getSalary() );
} // eol
}// eom
} // eoc EmployeeSortTest
class Employee implements Comparable
{
public Employee(String n, double s)
{
name = n;
salary = s;
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent * 100;
salary += raise;
}
/**
Compares Employee by Salary
returns neg if "this" sal is lower
returns 0 if the same
return pos if "this" is greater...
*/
public int compareTo(Object otherObject)
{
Employee other = (Employee)otherObject;
if (salary < other.salary ) return -1;
if (salary > other.salary ) return 1;
return 0;
}
private String name;
private double salary;
} // EOC Employee
Forgive the cheesy highlighting, but it helps here. As you can see, the class said it wanted to implement the Comparable interface, provided a compareTo() method (this gets used by the Arrays.Sort() routine, that's why it has to be there) and you can sort arrays to your hearts content. Note that you can determine whether the sort ascends or descends in your code block, not the sort method. See the book for the gory details on how Java lets you know in the API docs how it wants the method to work ( how we knew to return a -1 for less than and a +1 for greater than)
There are lots of interfaces for zillions of services. All of the interface descriptions are in the online API docs so don't worry about undocumented craziness, and you will get to know the more popular ones as we move along.
You can implement as many interfaces as you like with the same class!!
(C++ folk. This is how you incorporate a mix-in and Java's answer to
the lack of multiple inheritance)
class Employee implements Comparable, Cloneable // use both!
You do this because if you tried to get this functionality with abstract classes you'd be skunked because you can only inherit from a single class.
Some Properties of Interfaces - Time for some more rules.
1. You can never instantiate an interface.
2. You CAN have an object variable of type interface that hold any object that implements it.
3. You can use instanceof() to check if a class implements an interface.
if (anObject instanceof Comparable ) {...}
4. You can extend interfaces!
public interface Movable
{
void move(double x, double y)
}
public interface Powered extends Movable
{
double milesPerGallon();
}
5. You CAN store constants in interfaces!
public interface Powered extends Movable
{
double milesPerGallon();
double SPEED_LIMIT = 95; // a public static final constant
}
In fact, many interfaces have no methods at all! You just implement them so you can use the constants they provide!
A common pattern in programming (especially in GUI programming, don't worry we're getting there) is the callback pattern where you specify an action to occur when a particular event does. Like putting up a clock when you hit a key, or beeping when you right click your mouse. Interfaces will come into play a great deal when we get to graphical programming but for now we're going to start out easy.
The javax.swing package contains a Timer class that is useful when you want to be notified that a certain period of time has elapsed. This timer requires that you pass it an object that implements the ActionListener interface. This way when the action occurs the timer will invoke a method on that object for you. Here's the interface
public interface ActionListener
{
void actionPerformed(ActionEvent event);
}
Hmm, don't worry about that ActionEvent param just now, wait until you get into building GUIs. All you need to know is that we're going to construct a timer and tell it to perform the actionPerformed method that we define and repeat it every five seconds. All we'll do is spit the time out to the screen. Seem hairy? It's not. Look how short the code is...
package chap6;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer; // to avoid a conflict with the util Timer class
public class TimerTest
{
public static void main(String[] args)
{
ActionListener listener = new TimePrinter();
// construct a timer that calls the listener
// once every 5 seconds
Timer t = new Timer(5000, listener);
t.start();
JOptionPane.showMessageDialog(null, "Quit Program?");
System.exit(0);
}// eom
}// eoc TimerTest
class TimePrinter implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
Date now = new Date();
System.out.println("At the tone, the time is " + now);
Toolkit.getDefaultToolkit().beep();
}
}// eoc TimePrinter
Now do you see why it's handy to have an object variable of an interface type? See where I highlighted. The Timer class needs an actionListener object and that's what we give it, but it's really our TimePrinter class with our actionPerformed method. Pretty neat, eh?
This section gets a little ahead of itself, but it's a good example of how interfaces are going to be a daily part of your life if you want to code in Java.
Now that we're hip to interfaces we can discuss the nasty little topic of cloning an object. Back in the day the good old...
Object NewCopy = OldCopy;
would butter the biscuit. Not in Java (or C++ for that matter). If you make a copy you really just make another variable that points to the same area in memory where your object resides. What if what you really want is a completely different instance of the same class with exactly the same state as the object you have. This, my friend, is a clone. The government frowns upon cloning people, but we can clone anything we want, by implementing the Cloneable interface and providing a clone() method for our classes.
Clone() is actually a protected method of the Object class, and since everything hails from Object, you have it inherited automatically. But, you have to cast to your class and you only get a Shallow copy (This means that if your class has an object inside it your clone will have an object variable to THAT instance inside it, in other words, you'll have two variables, one in the original object and one in the clone, pointing to the same thing again! Shallow copies are OK if the underlying object is immutable, but if it's not you have to make sure you get a unique copy of it for your clone. That's why we create our own clone method and implement the Cloneable interface to tell the world we're cool for cloning.)
Employee copy = (Employee)original.clone(); // must cast - clone returns an Object copy.raiseSal(10); // You're original is still good...
How do we make sure we get a distinct copy with all subobjects cloned properly? This is called a Deep copy and you do it by redefining the clone() method to make it public in your superclass and having the subclass call it...
class Person implements Cloneable
{
public Object clone() // make it public so other classes can call it
{
try
{
return super.clone();
}
}
}
Now, the appearance of the Cloneable interface has NOTHING to do with the normal use of interfaces. It's a tagging interface. You've already inherited the method from Object so you don't really need a spec, it's just there to indicate that the programmer (that's you) knows the cloning process and is being careful. As you can imagine, you could do some nasty security things if you aren't careful, so the compiler is a bit wary when it comes to cloning and will actually throw an exception if you do not implement the tagging interface. Plus, you can now use instanceof to check if a class can be cloned or not.
Check out some code and always be careful, it's subtle business, when cloning.
package chap6;
import java.util.*;
public class CloneTest
{
public static void main(String[] args)
{
// set em up
Employee original = new Employee("John Q Public", 50000);
original.setPayDay(2000, 1, 1);
Employee copy = (Employee)original.clone();
// mess with the copy
copy.raiseSalary(10);
copy.addPayDay(14);
// print em out...
System.out.println("original=" + original );
System.out.println("copy=" + copy );
}//eom main
}// eoc CloneTest
/**
an Employee class with a mutable payDay field
*/
class Employee implements Cloneable
{
public Employee(String n, double s)
{
name = n;
salary = s;
}
public Object clone()
{
try
{
// call Object.clone()
Employee cloned = (Employee)super.clone();
// clone the mutable fields
cloned.payDay = (GregorianCalendar)payDay.clone();
return cloned;
}
catch (CloneNotSupportedException e) { return null; }
}// eom clone
/**
Set the payday to a given date
*/
public void setPayDay(int year, int month, int day)
{
payDay = new GregorianCalendar(year, month-1, day);
}
/**
Add number of days to the payday
*/
public void addPayDay(int days)
{
payDay.add(Calendar.DAY_OF_MONTH, days);
}
public Date getPayDay()
{
return payDay.getTime();
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
public String toString()
{
return "Employee[name=" + name
+ ",salary=" + salary
+ ",payDay=" + getPayDay()
+ "]";
}
private String name;
private double salary;
private GregorianCalendar payDay;
}// eoc Employee
The Output...
original=Employee[name=John Q Public,salary=50000.0,payDay=Sat Jan 01 00:00:00 EST 2000] copy=Employee[name=John Q Public,salary=55000.0,payDay=Sat Jan 15 00:00:00 EST 2000]
That's the basics of cloning. Remember to start the methods with a lowercase. I had a "Clone" method the first time I tried to compile and got the "protected access" exception promised by the protected clone method you inherit from Object. Try it. It's nice to get an error you actually can understand so I crashed it again for fun.
Remember up top when I said that there were two techniques you had to learn to be a masher? The first was getting to know interfaces and the second is learning to work with Inner classes. An Inner class is just a class that's defined inside another class. It's that simple. They behave in weird ways though so we better go over them.
First off, you have to ask the question "Why would I use an Inner class?" The book gives four reasons
|
An object of an inner class can access the implementation of the object that created it, including private data! | |
|
Inner classes can be hidden from other classes in the same package. | |
|
Anonymous inner classes are tres useful when you want to define a callback on the fly. | |
|
Inner classes are VERY convenient when writing event driven programs |
Note for C++ folk - inner classes are like nested classes. We aren't talking about containers here.
The book says the syntax for inner classes is complex, but I disagree. It's pretty straight forward if you understand scoping rules and access limitations (some of which they break). Lets take a look at some code...
class BankAccount
{
public BankAccount(double initialBalance)
{
balance = initialBalance;
}
public void start(double rate)
{
ActionListener adder = new InterestAdder(rate);
Timer t = new Timer(1000, adder);
t.start();
}
private double balance;
private class InterestAdder implements ActionListener
{
...
}
}
See. You just define your class in with the methods and instance fields. Now the methods of your class can instantiate the inner class and get an object of type InterestAdder. Not bad so far, right? There are some things you have to be aware of though.
Just because you define the class doesn't mean that every object of BankAccount has an InterestAdder object in it. The class is there for your class methods to create local objects for use in that method! These objects are not instance fields of the outer class, they're just for the class methods.
Only inner classes can be private. A private class seems strange, but this is the circumstance when you would use it. Nothing else will have access to it except for your class methods.
Lets look at the full program. It uses a Timer class again to add interest to a bank balance every second....
import java.awt.event.*;
import java.text.*;
import javax.swing.*;
public class InnerClassTest
{
public static void main(String[] args)
{
// construct a bank account with initial balance of $10,000
BankAccount account = new BankAccount(10000);
// start accumulating interest at 10%
account.start(10);
// keep program running until user selects "Ok"
JOptionPane.showMessageDialog(null, "Quit program?");
System.exit(0);
}
}
class BankAccount
{
public BankAccount(double initialBalance)
{
balance = initialBalance;
}
public void start(double rate)
{
ActionListener adder = new InterestAdder(rate);
Timer t = new Timer(1000, adder);
t.start();
}
private double balance;
// the inner class
private class InterestAdder implements ActionListener
{
public InterestAdder(double aRate)
{
rate = aRate;
}
public void actionPerformed(ActionEvent event)
{
// update interest
double interest = balance * rate / 100;
balance += interest;
// print out current balance
NumberFormat formatter
= NumberFormat.getCurrencyInstance();
System.out.println("balance="
+ formatter.format(balance));
}
private double rate;
}
}
Now that you've taken a gander at an inner class in action let me help you with the confusion you might be having. If you examined the code carefully you may be asking yourself "Where did the method in the inner class get a balance variable?" It didn't. This exemplifies the key behavior of inner classes. They have access to the data members of the outer class! An inner class has access to it's own data fields and that of the outer class. In the above example the code merely uses the balance variable from the BankAccount class. Here lies the power of inner classes and what makes them a useful tool. As you've seen they not only have access, but don't have to rely on a clumsy name like BankAccount.balance to get the job done.
Now, I will save the gory details of how this gets done for those soldiers who have run out to get the book. But I will let you in on another nasty Java secret. This is a HUGE security hole if you know how it works and if toyed with can grant public access to all your classes private data fields! (You have to have a HEX editor though and know how the compiler creates names for these access methods inner classes use to grab outer class data). I'm not trying to be cryptic here, I just don't want to waste two paragraphs describing it, and you have to be a hacker of dubious intent to care, so go get the book.
Local Inner Classes - Forget nesting a class in a class. If you want you can define it in a method of that class! These are local inner classes. They not only have access to the outer classes data and their own data but also any local variables passed to or created inside the method they're in.
There's a caveat though. The local variables have to be static. There's also a subtle problem of variable persistence after a method is called. What you learn is that the object of the LIC actually takes a copy of all the local variables with it when it instantiates (hence static to maintain same values), so even if the method ends your object persists and has local data for a method that's dead and gone. Lots of work for compiler writers, but that's all water under the bridge now, and Java programmers can use local method variables to their hearts content.
These are the guys that can reasonably be called complex in their syntax, but again, it depends how you look at it. Why do you give classes names? Because you're going to use them over and over again to create your objects, right? But what if you knew you'd only use it once? Couldn't you just define it on the spot and assign it to a variable? That's all an anonymous inner class is. A class that's being used once, then and there, and doesn't really need a name. Check it out...
public void start(final double rate)
{
ActionListener adder = new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
// update interest
double interest = balance * rate / 100;
balance += interest;
// print out current balance
NumberFormat formatter
= NumberFormat.getCurrencyInstance();
System.out.println("balance="
+ formatter.format(balance));
}
};
Timer t = new Timer(1000, adder);
t.start();
}
This little snippet shows an anon inner class. Notice it uses new and then lists the interface or class it's going to implement or extend. You can't call a constructor for an anon inner class (it has no name after all) so if you extend an existing class you just put the parameters in it's constructor then define your additions. Even if you use an interface, as above, you need to include the parens () like you're calling a default constructor.
Now that you know about anonymous inner classes do yourself a favor. Forget about them. The book says to use restraint in using them but I say don't even bother. At best they save you a few lines of code. Who cares? You will get to a point in your programming career when you build GUI's to wrap around your logic and anon inner classes can be enticing in a few situations, but I have yet to come across a situation where you HAD to use them. They will make your code hard to read and even though you understand them, chances are the next guy won't and you'll get a phone call at 11:45pm. Name your classes and then use them. I don't know why the implementers of Java bothered with this. I hope it wasn't hard to implement this feature in the compiler. (I don't think it was. If its an interface the { token better pop off that stack though, because you can't instantiate an interface. It's just a shortcut. Why would you bother with this? I'm not sure.)
Anyway, and don't tell anyone I said this, just forget about Anon inner classes. If you're asked on the Java Cert test then they're just being jerks,... I mention them to keep these notes complete.
Static Inner Classes - Yet another entry in the useless knowledge category, or is it? If you declare an inner class to be static then you cut off all access to the outer class's data. Why bother? Remember that nasty security bug described above? This would certainly solve that problem. The book uses an example of an inner class declared to avoid a naming conflict, good solution because the inner class is only visible inside the object and is listed as outerclass.innerclassname, and the class needs no access to the outer classes data. Just more minutia, but good to know if you want to show off.
If you're looking to learn Java fast then you can safely move on to Graphics Programming! If you want to be a systems guy or be the only guy on your block that knows EVERYTHING about Java (isn't that why we do this?) then stick around. Proxies are truly hairy and will make your brain hurt, but it goes away, and you're closer to being a Java guru...
I'm going to preface this section by telling you that you are best served getting the book for this one. It's a weird topic that's not very intuitive, but I'm going to give it a shot.
Remember when we discussed the Class class? Let's say for example you have an array of objects of type class, you don't know what classes they'll be before runtime, and you'll need to create objects from these classes. It's easy because you can call the newInstance method of the class object and get an actual beastie.
But what if you need these classes to implement interfaces and you don't know which at runtime? You can't define a new class at runtime. Since Java 1.3 the SDK includes a new feature to tackle this problem called a proxy class. It's a special class that takes three parameters upon creation. A class loader (We use NULL for now, you'll learn what a class loader is later), an array of class objects (this is where your interfaces are), and an invocation handler (this is where you define any methods you want this new class to have). The new proxy object has all the default methods of the interfaces and all of the methods of the Object class (duh, don't they all).
I'll put up the code provided by the book, highlight where the proxy object is created, and finish with some properties of proxy classes...
/**
@version 1.00 2000-04-13
@author Cay Horstmann
*/
import java.lang.reflect.*;
import java.util.*;
public class ProxyTest
{
public static void main(String[] args)
{
Object[] elements = new Object[1000];
// fill elements with proxies for the integers 1 ... 1000
for (int i = 0; i < elements.length; i++)
{
Integer value = new Integer(i + 1);
Class[] interfaces = value.getClass().getInterfaces();
InvocationHandler handler = new TraceHandler(value);
Object proxy = Proxy.newProxyInstance(null,
interfaces, handler);
elements[i] = proxy;
}
// construct a random integer
Random generator = new Random();
int r = generator.nextInt(elements.length);
Integer key = new Integer(r + 1);
// search for the key
int result = Arrays.binarySearch(elements, key);
// print match if found
if (result >= 0)
System.out.println(elements[result]);
}
}
/**
An invocation handler that prints out the method name
and parameters, then invokes the original method
*/
class TraceHandler implements InvocationHandler
{
/**
Constructs a TraceHandler
@param t the implicit parameter of the method call
*/
public TraceHandler(Object t)
{
target = t;
}
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable
{
// print implicit argument
System.out.print(target);
// print method name
System.out.print("." + m.getName() + "(");
// print explicit arguments
if (args != null)
{
for (int i = 0; i < args.length; i++)
{
System.out.print(args[i]);
if (i < args.length - 1)
System.out.print(", ");
}
}
System.out.println(")");
// invoke actual method
return m.invoke(target, args);
}
private Object target;
}
You can use a proxy class for three different things...
|
Routing methods to remote servers | |
|
Associating user interface events with actions running in a program | |
|
Tracing method calls for debugging purposes |
The program above does the last, tracing a binary search through an array of integers.
Some properties of Proxy classes
They are created on the fly, but act just like regular objects when instantiated.
All proxy classes extend the class proxy and only have one instance variable, the invocation handler.
all new data items and methods must be put in the invocation handler.
The names of proxy classes are not defined.
A proxy class is public and final.
You can check if a Class object is a proxy class object by using the isProxyClass method.
Again, this is for tool builders and hardcore Java nuts. Application programmers will rarely, if ever, run into a circumstance where they will have to use a proxy class, but it's good to know what they are and a little of how they work. Again, refer to the book or the online API and Java Sun site for real hardcore info on proxy classes.
Time to have a little fun and start learning how to do Graphics programming!
Send mail to lars@sorcon.com with
questions or comments about this web site.
Copyright © 2004 Sorensen Consulting