Re: Design question - methods calling methods
Lew wrote:
Are you familiar with the difference between interfaces and classes?
Rhino wrote:
I'm not sure how to answer that. I've read formal definitions of both and
have written both and gotten them to run. But even after all the time
I've spent writing Java - I started in 1997 but had a 4 year gap that
ended a few months back where I wrote no Java at all - I don't feel solid
on a lot of the theory. Maybe I'm expecting too much of myself but I feel
like I should know instantly and intuitively what the key differences are
between classes and interfaces and not have to wrestle with "should I use
an interface or class here".
The Java tutorial describes the difference, but doesn't really address your
question, so I'll take a stab.
Interfaces are pure contract - they describe the signatures of the methods
that class instances must implement, but may not contain any method
implementation nor instance members. (It's also usually a bad idea to give
them 'static' members.)
Classes may contain some implementation, and must be completely implemented
(if not 'abstract').
A class can implement an interface - that is, it is a subtype of the interface
that fills in the missing implementation details.
So you can have an interface:
public interface Foo
{
public void fooishBehavior();
}
Notice the semicolon instead of a method body - no curly braces in the method.
It's a promise that there will be such a method in any class that implements
the interface.
You can have an implementing class:
public class FooImpl implements Foo
{
public void fooishBehavior()
{
}
}
The class's claim that it 'implements Foo' requires it to implement the 'Foo'
methods, or at least be an abstract class. We'll ignore abstract classes for
you to study on your own.
The interface is a type - this is the key. All subtype classes or interfaces
are necessarily also of that type. 'extends' and 'implements' both express a
subtype relationship, and a subtype thing /is-a/ thing of its parent types.
(Inheritance expresses /is-a/; membership, a.k.a. composition, expresses /has-a/.)
The key is to think in terms of types, not classes. Interfaces are pure
expressions of type, and classes express a way for the type to do something
actual.
Different implementors of an interface can do quite different things, but they
must do the things promised by the type they implement.
Think of "type" as a contract - it shall have these behaviors!
All implementors of 'Foo' above must implement (or pass on the responsibility
abstractly to implement) that 'fooishBehavior()' method. How they do so is up
to them.
Variables have a type - that's what's important. When you use a variable of
type 'Foo', all you really care about is to use its 'fooishBehavior()'.
You should not (usually - always the disclaimer) care how the thing does its
'fooishBehavior()'.
So you typically have a construct like
Foo foo = new FooImpl();
Since everything of a subtype ('FooImpl') /is-a/ thing of the supertype
('Foo'), this is legal, and since 'Foo' is the type you care about, this is
proper.
Later you might refactor the code. Perhaps you discover that
'FooImpl#fooishBehavior()' is not thread safe but you need it to be. However,
the logic of the program doesn't need to know that, only that 'foo' has the
method, safe or not. So you change that initialization to:
Foo foo = new ConcurrentFooImpl();
The rest of the program, knowing only that 'foo' is of type 'Foo', needs no
rewrite. The hidden secret of the refactored 'foo' buys you thread safety,
and the rest of the program needs no recompile. Magic!
This came up in a project where I worked when we substituted something like
Map <Foo, Bar> whatever = Collections.synchronizedMap(
new HashMap <Foo, Bar> () );
with
Map <Foo, Bar> whatever = new ConcurrentHashMap <Foo, Bar> ();
and got a huge boost in the program's performance.
--
Lew