Abstract Classes versus Interfaces
Somebody at work has just made an interesting observation and its bugging me, comments please.
When I started Java I just used classes (and abstract classes), and didnt bother with Interfaces I understood them to be a way of getting round the lack of MultipleInheritance and it wasnt a problem that concerned me.
But as time went on I found that sometimes I did want classes that had little in common to provide some additional feature (such as logging), and interfaces were great for this.
..so I was happy ..
During all this time if I want a HashSet I would write code like this
HashSet dataSet =new HashSet();
but then I read that HashSet was an implementation detail and we should be using Interfaces as follows:
Set dataSet =new HashSet();
So whereas before I might have had a class hierachy with an Abstract class at the top , I started slotting in Interfaces above that and viewing the Abstract class as just a convenience to implementing the Interface.
So which is the right method , originally I saw subclassing object as an 'is a ' relationship and implementing an interface as a 'implements' relationship. But more recently it seems to be turned on its head and implementing an interface is an 'is a' relationship (but supports multiple inheritance)
Also it seems to be the trouble with this second approach is that anyone can implement an interface, for example I could implement Set,Map and List with one class, the class hierachy is not so helpful .
So your question is, what is better:
(1) HashSet hashSet = new HashSet();
or
(2) Set hashSet = new HashSet();
As you already know, objects should usually receive interfaces as parameters. So if you have a User object that needs to know about the hashSet, it should expect a Set, so that you can swap the implementation without affecting other classes.
If you have a class that instantiates a HashSet on its own and uses it, then the only convenience I can see in using the second approach is that if you have to change the implementation of the Set in the class that instantiates it, then you only have to make one change: changing the line of code that instantiates it. Sometimes you don't want to do this, however. As you pointed out, an object can implement multiple interfaces and thus an instantiator that uses an object may need to talk to multiple interfaces of that object. In that case, it should be the least common interface -- even if that is the immediate class implementation (you shouldn't go to the trouble of making a wrapper interface).
The instantiator will already know the implementation. The only advantage in having the instantiator handling the object by its interface instead of its implementation is that if you change the implementation within that class, you will only have to change one line of code. Let's say you wanted to pass the object to the used-to-be-instantiator, instead of instantiating it there. In this case you will want to handle it by its interface, and if you will have to make less changes (1 line of code compared to many) if you were already doing so. This works the same for dependency injection, such as XML configuration.
So, in summary, the abstraction you talk about is a small convenience -- small because refactoring makes the changes you would encounter go quickly.
This is my take on it. I hope I'm not completely off :(
Sorry, I think I missed your question.
It's true that if you have to call methods from multiple interfaces of an object, then you are going to have to cast back and forth between those interfaces if you want to handle the object by its interface rather than its implementation. With abstract classes, you can't add another abstract class on anyways. You can add other interfaces on -- true -- but then you've hit the same problem. And you don't want to lump unrelated interfaces into a single abstract class -- it won't be reusable at all and you will have to make base changes to your hierarchies every time you want to reuse that code.
So it doesn't make sense to inherit an empty implementation from an abstract class to disallow the addition of other interfaces. You want that flexibility. There always is the risk that interfaces will have conflicting methods (same name and arguments), but if you cast back and forth between those interfaces when you have to call methods from both from a single object, then you'll be fine with that.
I hope that this also didn't competely miss the point!
Thanks, but the question was alot wider than that, the HashSet example was just one part of it.
I suppose the question is
"How do you code model "is a " relationship in java through extending a class or creating an interface and implementing it"
an alternative question is
"is it correct to mix/match both methods I described above"
> Thanks, but the question was alot wider than that,
> the HashSet example was just one part of it.
>
> I suppose the question is
>
> "How do you code model "is a " relationship in java
> through extending a class or creating an interface
> and implementing it"
>
It depends on the context.
> an alternative question is
>
> "is it correct to mix/match both methods I described
> above"
Yes.
You said the following in the OP....
>viewing the Abstract class as just a convenience to implementing the Interface.
That concerns me. If you are using abstract class parents simply to gain behavior (not data) then your model is broken - you should be using composition. You do not implement inheritence based on convenience but instead should base it on the logic of the requirements (use cases) which suggests such a model is appropriate. It doesn't matter whether it is an interface or class.
>
> It depends on the context.
>
Does it ? Could you give an example of when to use one and when to use another, as everything Ive read on this point seems rather wolly.
> That concerns me. If you are using abstract class
> parents simply to gain behavior (not data) then your
> model is broken - you should be using composition.
> You do not implement inheritence based on
> convenience but instead should base it on the logic
> of the requirements (use cases) which suggests such
> a model is appropriate. It doesn't matter whether
> it is an interface or class.
You misunderstand me I am using abstract classes int he correct way, I assure you.
> Thanks, but the question was alot wider than that,
> the HashSet example was just one part of it.
I think it is representative of all the situations that you talk about, which are whether an instantiator should handle the instantiated object by its interface or by its implementation.
> I suppose the question is
>
> "How do you code model "is a " relationship in java
> through extending a class or creating an interface
> and implementing it"
MySubClass extends MySuperClass implements MyInterface {}
MySubClass is a MySuperClass and a MyInterface. The way that I view it, the "is a" relationship applies to all type inheritance, even for the multiple type inheritance that you can have with interfaces. The "is a" relationship doesn't doesn't have to be a 1:1 mapping. There's no point in thinking about it differently since that's how the language behaves.
> an alternative question is
>
> "is it correct to mix/match both methods I described
> above"
I thought I gave an answer. You said that my answer is not "broad enough." How so? My answer was that handling an object that you instantiate by its interface can reduce the amount of changes you have to make if you change the implementation. You will have to make one change (the line of code that instantiates the object) instead of many. Also, handling it by its interface makes it easier to switch from object A instantiating object B to object A being passed a reference to B and to object A getting its dependencies injected by XML configuration.
It seems weird to handle an object by its interface if you already know it's implementation, but it's commonly seen and this is why I think it must be used. As I said, it's a small benefit of abstraction.
The times when you may not want to do this is when the instantiator has to call methods from different interfaces of the object. You can handle it by its interface by casting between the different interfaces you have to work with, or you can just handle it by its implementation, gaining access to all of the object's interfaces.
Note that everything I explained concerns the type by which the instantiator handles an instantiated object. If a class does not instantiate a certain object, then it almost always should receive it by its interface, unless it's some really common implementation that's never going to change.
I use interface, abstract class, and base class interchangeably. It's up to you to decide which to use for type inheritance. It's good practice to favor interfaces.
> >
> > It depends on the context.
> >
> Does it ? Could you give an example of when to use
> one and when to use another, as everything Ive read
> on this point seems rather wolly.
JDBC uses interfaces. While some of the collection classes have actual bases.
> You misunderstand me I am using abstract classes int
> he correct way, I assure you.
Then the wording that I quoted did not describe what you actually do.
Ok, we seems to be on focusing on how easy it is to refactor code if you decide to change it, what I am focusing on is "with unlimited resources what would be the best way to model a UML diagram which contained interfaces and inheritance elements in Java within a application development environment".
JDBC is a bit of a special case because the JDBC classes are there to be used by application developers not extended, the implementation of these interfaces is usually provided by Database vendors in such a way to be of no concern to the average application development
1>The Collection classes are generally going via the interface route, providing Abstract classes where they may be useful. For example AbstractSet implements Set, and it is extended by HashSet,TreeSet and EnumSet. So this seems to be using an 'is a' relationship between AbstractSet and Set, and between HashSet and AbstractSet.
2>Whereas the Swing framework take a different approach , for example JTree extends JComponent but does not implement a Tree interface.
These seem to be to be fundamentally different approaches, and I cant see why it was right to use one method for one and one for another.
> Ok, we seems to be on focusing on how easy it is to
> refactor code if you decide to change it, what I am
> focusing on is "with unlimited resources what would
> be the best way to model a UML diagram which
> contained interfaces and inheritance elements in Java
> within a application development environment".
And again it depends on the context. A GUI interface to a system driver is significantly different than an enterprise solution written for a J2EE container.
>
> JDBC is a bit of a special case because the JDBC
> classes are there to be used by application
> developers not extended, the implementation of these
> interfaces is usually provided by Database vendors
> in such a way to be of no concern to the average
> application development
>
Huh? Users write drivers and thus they extend the interfaces.
And other users just use the drivers.
That doesn't alter the fact that the choice of interfaces in this case was probably the ideal solution.
> 1>The Collection classes are generally going via the
> interface route, providing Abstract classes where
> they may be useful. For example AbstractSet
> implements Set, and it is extended by HashSet,TreeSet
> and EnumSet. So this seems to be using an 'is a'
> relationship between AbstractSet and Set, and between
> HashSet and AbstractSet.
>
An abstract class is still a class. And I would suppose that the use of a class, versus an interface, is appropriate for that usage.
You asked for an examples and so I provided them.
> 2>Whereas the Swing framework take a different
> approach , for example JTree extends JComponent but
> does not implement a Tree interface.
>
> These seem to be to be fundamentally different
> approaches, and I cant see why it was right to use
> one method for one and one for another.
Again it is probably because of the context. However you must also keep in mind that you shouldn't look at the implementation of the Java API as the ideal model. There are mistakes in it. (And I don't know anything about GUIs so I can't comment on your specific question.)
Personally I believe multiple interface inheritance is bad. multiple implementation inheritance is not bad, its just unclear/confusing to implement so Java did away with it.
If you tack a new interface onto a class, you are extending the classes responsibility. If you tack new methods onto an interface, you are extending an objects responsibility. The class to object relationship is not one to one. Be careful who you are adding responsibility to, and why.
so a class can represent multiple objects. This means each child of that class also represents the same multiple objects. This is a caveat of extending classes be they abstract or not. Child objects should have an is-a relationship with their parents. On the other hand, classes are implementations of objects. The is-a (or whatever) relationship should stay in the object domain not enter the class domain.
extending classes brings the is-a relationship into the implementation domain.
I could be crazy though :D
To clarify, classes live in the implementation domain and I don't think object relationships should be created or modified in the implementation domain. Im not uptight enough to enforce this, but you have to shoot for A++.
> > Thanks, but the question was alot wider than that,
> > the HashSet example was just one part of it.
>
> I think it is representative of all the situations
> that you talk about, which are whether an
> instantiator should handle the instantiated
> object by its interface or by its implementation.
>
The Set/HashSet reference should never be passed outside of the instantiators context anyway. It does not matter how you refer to it locally since that is within the instantiators implementation. If it has to get our for whatever reason, then it should exit as a Set. This is typical for Set/HashSet, but your own classes may or may not align with this.
>>If you tack a new interface onto a class, you are extending the classes responsibility. If you tack new methods onto an interface, you are extending an objects responsibility. The class to object relationship is not one to one. Be careful who you are adding responsibility to, and why.
>>so a class can represent multiple objects. This means each child of that class also represents the same multiple objects. This is a caveat of extending classes be they abstract or not. Child objects should have an is-a relationship with their parents. On the other hand, classes are implementations of objects. The is-a (or whatever) relationship should stay in the object domain not enter the class domain.
extending classes brings the is-a relationship into the implementation domain.
Sounds like you know what you are talking about, but I dont understand it ' A class can represent multiple objects', could you rephrase it
I still think my Set example is confusing people, it was given as an example whereby there was an Interface representing the main purpose of a class implemented by a class (Set - HasSet) rather than an ancilliary function. Im trying to understand how to build my class hierachy, rather than instantaition of Objects and their use within the class hireachy
class HashSet implements Set, Cloneable, Serializable {
...
}
1. Here we have a class named HashSet . This class represents several different "objects". It represents Set, Cloneable, and Serializable objects.
2. All we wanted to do when we created HashSet was to implement these three defined objects. Unfortunately what we have also done is defined a new object/type called HashSet . So technically we are implementing 4 objects.
3. This HashSet object happens to be the child of Set, Cloneable, and Serializable objects. Essentially multiple object inheritance. While the HashSet java class has multiple responsibilities, this is not bad. But the HashSet object/type having too much responsibility is bad.
4. Now if we start using the type HashSet (as opposed to Set), we have given validity to this accidental object HashSet . This is the idea behind using Set and not HashSet. Its an attempt to not recognize the object/type HashSet.
4. If we extend HashSet, we are doing the same thing we tried to avoid in #3. Look at LinkedHashSet which extends HashSet. See how it declares that it implements Cloneable, Serializable, and Set? Why, when implementing the HashSet object/type meant it inherited these objects/types already? Its another attempt to not add validity to the accidental object/type HashSet. In doing so, theoretically Sun could change LinkedHashSet to no longer extend HashSet but use composition. This would only work if the HashSet type is not used anywhere.
P.S. Please ignore the fact that Serializable and Cloneable are marker interfaces and have no methods. They still define an object/set of responsibilities.
> They (interfaces) still define an object/set of responsibilities.
interfaces do not define objects, only classes do. interfaces may serve as types, for the sole purpose of the interacting among entities/objects. abstrac classes define the abstract concepts of an entity. interfaces and abstract classes may have some functional similarities, they are for difference purposes. as far as the design of an object is concerned, interfaces do not have a role in this.
The interface represents the conceptual object. The class is an implementation of one or more conceptual objects. I have no idea what you mean by 'entity.' However, when you say "abstrac classes define the abstract concepts of an entity" It looks like you are talking about the conceptual 'object.'
> The interface represents the conceptual object. The
> class is an implementation of one or more conceptual
> objects. I have no idea what you mean by 'entity.'
> However, when you say "abstrac classes define the
> abstract concepts of an entity" It looks like you
> are talking about the conceptual 'object.'
the interface is just an interface, for different entities to interact smoothly. it does not represent the conceptual object.
a conceptual object can only be represented by implementations, which uses classes. when there is an abstractness in a concept, such as number, fruit, etc, abstract classes come to play.
a class, which defines the instance/object, can have more than one interface, and those interfaces do not have to have anything to do with the class, conceptually or in any other sense.
an entity is an instance at any level in a hirachy, it could be a single object, or many objects.
any entity can serve as a type, an interface is in itself sufficient to serve as a type as well.
>
> a class, which defines the instance/object, can have
> more than one interface, and those interfaces do not
> have to have anything to do with the class,
> conceptually or in any other sense.
>
That is going to be a good trick.
Warning to other readers - dafei has proven on countless occassions (probably 100s) that he doesn't know what he is talking about in any subject (yes not a single one) and has dismissed the comments of large number (probably over 100 now) of different posters that have tried to educate him in java specifics, java generalities and even computer science.
So proceed at your own risk.
> >
> > a class, which defines the instance/object, can
> have
> > more than one interface, and those interfaces do
> not
> > have to have anything to do with the class,
> > conceptually or in any other sense.
> >
>
> That is going to be a good trick.
>
thats not a trick at all: interfaces are for contracting purpose among different entities that want to interact. they have nothing to do witht he design of an object.
This concept of a 'conceptual object' is plain confusing, much safer to restrict the term Object to mean an instance of a Class, a Class can implement an interface.
Having said that I think I'm with daFei on this if I've unsterstood you right. Logically it seems a HashSet is not a cloneable or a serialiable but is is a Set yet we are using the same mechanism for all three relationships. Interfaces should be used for only clonebale and Serializable which is how java code used to normally be written.
> This concept of a 'conceptual object' is plain
> confusing, much safer to restrict the term Object to
> mean an instance of a Class, a Class can implement an
> interface.
>
can't dumb it down. It is what it is. The term 'instance' already has a meaning.
> Having said that I think I'm with daFei on this if
> I've unsterstood you right. Logically it seems a
> HashSet is not a cloneable or a serialiable but is is
> a Set yet we are using the same mechanism for all
> three relationships. Interfaces should be used for
> only clonebale and Serializable which is how java
> code used to normally be written.
I think your confused by the mechanisms offered by these interfaces. They are special cases.
A HashSet is a Set and it properly calls this out by implementing Set. However, this HashSet class is also serializable and cloneable. These are properties of the _class_ not of the conceptual object. These are implementation domain facilities provided by the JVM through marker interfaces.
But now that we inadvertently defined another conceptual object HashSet, it has attached to it cloneable and serializable. So facilities that were meant to be implementation domain, have now attached themselves to the HashSet object.
Contrast that with the object SortedSet which is defined
interface SortedSet extends Set
In this case we have purposefully defined another conceptual object, SortedSet. In this case there are no implementation domain facilities attached to it.
You might also want to note how TreeSet declares it implements SortedSet but it does not declare that it implements Set. It inherited Set through SortedSet. Contrast that with how HashSet declares that it implements Set eventhough it already inherited that implementation since it extended AbstractSet.
The inheritance through object domain is recognized, the inheritance in implementation domain is not.
As you can see these are practices chosen by Sun, and I agree with them. However, they are not enforced by the language. Whatever you do with an interface you can certainly do with an abstract class. I prefer not to treat them the same.
> > This concept of a 'conceptual object' is plain
> > confusing, much safer to restrict the term Object
> to
> > mean an instance of a Class, a Class can implement
> an
> > interface.
> >
>
> can't dumb it down. It is what it is. The term
> 'instance' already has a meaning.
>
yes Im not saying reuse the term instance I am saying the term 'Object' already has a meaning but youve mentioned 'Object' a number of times in this thread to mean something completely different.
For example
>>Contrast that with the object SortedSet which is defined
SortedSet is an interface NOT an object
Anyway Ive taken a look at your examples and I think i know what you are getting at.
Set is a 'Conceptual Object' represented by an Interface, Cloneable is not a 'Conceptual Object' but is also represented by an Interface, although the only way to distinguish between the two in the Java language is via naming conventions, Set is a noun and Cloneable is an adjective.
SortedSet is an interface whereas HashSet is a class so Sorted Set is a 'Conceptual Object' whereas HashSet is an 'ImplementationObject' so HashSet also implements Cloneable which is required purely for implemenentations, however if you return a HashSet from a method rather than a Set you are then exposing implementations details such as cloneable to calling methods.
If this is correct we could do with an annotation to distinguish between interfaces such as Set and interfaces such as Cloneable
> yes Im not saying reuse the term instance I am saying
> the term 'Object' already has a meaning but youve
> mentioned 'Object' a number of times in this thread
> to mean something completely different.
>
I can list 3 uses of the term;
1. The design time object (that have started to differentiate with 'conceptual' object)
2. The runtime object (bka instance)
3. The compile time object (bka class)
My point is that #3 and #1 are different. When we created HashSet#3 inadvertently we created HashSet#1, but we do our best to ignore HashSet#1.
> For example
> >>Contrast that with the object SortedSet which is
> defined
>
> SortedSet is an interface NOT an object
>
Call it a conceptual object unless you have a better Computer Science term that works?
>
> Anyway Ive taken a look at your examples and I think
> i know what you are getting at.
>
> Set is a 'Conceptual Object' represented by an
> Interface, Cloneable is not a 'Conceptual Object' but
> is also represented by an Interface, although the
> only way to distinguish between the two in the Java
> language is via naming conventions, Set is a noun and
> Cloneable is an adjective.
>
> SortedSet is an interface whereas HashSet is a class
> so Sorted Set is a 'Conceptual Object' whereas
> HashSet is an 'ImplementationObject' so HashSet also
> implements Cloneable which is required purely for
> implemenentations, however if you return a HashSet
> from a method rather than a Set you are then
> exposing implementations details such as cloneable to
> calling methods.
>
yes. In fact this is why I use ArrayList instead of List locally. So I can clone it :D
Now that you understand this you will understand reasons behind stuff like http://contractualj.com/
> If this is correct we could do with an annotation to
> distinguish between interfaces such as Set and
> interfaces such as Cloneable
True. If annotations were around in the beginning they would have probably been used instead.
ContractualJ ( http://contractualj.com/ ) is very restrictive, interesting idea I suppose.
The annotations idea was an idea for adding to the current system not replacing it. I thought you could add an annotation to an interface which would indicate whether a class would implement it as the 'Conceptual Interface' (i.e Set) or as an 'Implementation Detail' (i.e Cloneable)
> The annotations idea was an idea for adding to the
> current system not replacing it. I thought you could
> add an annotation to an interface which would
> indicate whether a class would implement it as the
> 'Conceptual Interface' (i.e Set) or as an
> 'Implementation Detail' (i.e Cloneable)
Cloneable and Serializable are not true interfaces. I don't think you fully appreciate how special they are. Really these two should be dropped from this discussion since they are a distraction. They are merely tools to invoke special behavior out of the JVM. No other interfaces do this. Thats why I said annotation would replace them. But I don't even know how annotation works so...
Basically what I am saying is that ALL java interfaces (with methods) map to conceptual objects.
> Cloneable and Serializable are not true interfaces.
they are conceptual objects!
> I don't think you fully appreciate how special they
> are.
they are conceptually special.
> Really these two should be dropped from this
> discussion since they are a distraction.
they are conceptually delusions.
> They are
> merely tools to invoke special behavior out of the
> JVM.
they are conceptual tools.
> No other interfaces do this.
for example? conceptually delusionally you may feel right.
> Thats why I said
> annotation would replace them. But I don't even
> know how annotation works so...
>
that must be conceptually advanced.
> Basically what I am saying is that ALL java
> interfaces (with methods) map to conceptual objects.
if conceptually conceptual objects delusionally exist in your brain with conceptual brain cells...
my goodness, i am missing those conceptual chickens in loko's village...
Conceptually an interface is nothing but an abstract class which has only abstract methods in it. When you are extending a class then you are inheriting some implementation along with it, that is you are sharing the capability. Implementing an interface is just inheriting the contract. Here you just share the responsibility.
Whether an abstract class or an interface they are the base types. You need to consider selecting one of them when you are creating a base type and expect people to adhere to the contract when they create new classes in the future.
OK, Now an interface is flexible. The future class could be inherited from a base class of their choice since they implement your interface. But beware when you force a contract through a base class they have no option of extending from a class appropriate to their problem domain.
According to Bob Martin the is-a relationship should be used only when there is a common behavior. And inheritance will lead to a tight coupling and better to decouple with an interface using dependency inversion. Either read Bobs articles at www.objectmentor.com or purchase his book Agile software development principles, patterns and practices.
I'm 1/2 way through that book now. Its pretty good.
"Conceptually an interface is nothing but an abstract class which has only abstract methods in it. " I think this is wrong. Replace "Conceptually" with 'practically.' Perhaps even, 'mal-practically.'
To call an interface "nothing but an abstract class which has only abstracts methods in it" illustrates your are viewing from an implementation standpoint.
An interface lives in the design domain. It is not a cut down implementation. Its a template for implementation. However, an abstract class should live in the implementation domain, providing support functionality or implementation functionality for 'child' objects.
You can certainly create an abstract class that serves no other purpose but that of an interface. I wouldn't do it, but I defend your or anyone's right to.
But thats just Java. You can't define a class without defining a new type. If you could then abstract classes could loose their ability to define and would never again be used as types. Maybe Sun could add some real new feature to Java '6' besides the nothing they appear to. SIX even!!
> [...]
> 1>The Collection classes are generally going via the
> interface route, providing Abstract classes where
> they may be useful. For example AbstractSet
> implements Set, and it is extended by HashSet,TreeSet
> and EnumSet. So this seems to be using an 'is a'
> relationship between AbstractSet and Set, and between
> HashSet and AbstractSet.
>
> 2>Whereas the Swing framework take a different
> approach , for example JTree extends JComponent but
> does not implement a Tree interface.
>
> These seem to be to be fundamentally different
> approaches, and I cant see why it was right to use
> one method for one and one for another.
The major difference could be that the AbstractSet (or its implementations) represents what is called a model in the MVC architecture,
while JTree represents the controller. If you dig a little, you will find that JTree instances use an instance of TreeModel to hold the data.
Of course, TreeModel is an interface, which gives some warranties on the independence between the model and the controller.
Personnally, I don't see abstract classes and interfaces as identical.
Ideally, abstract classes give an inheritance in terms of structure, while interfaces bring the behavior.
A creature is composed of a corpse, a brain, a heart and members.
members give a creature the ability to move.
some objects can move, which doesn't mean they belong to the creatures family.
some creatures won't be able to make deductions, while others will.
abstract class Creature implements Thought, Movement, ... {
private Organ brain, heart, ...;
private Member[] arms, legs, ...;
...
}
interface Thought {
storeIdea(Idea idea);
makeDeduction(Rule[] rules, Fact[] facts, Variable[] variables);
...
}
interface Movement {
moveForward();
moveBackward();
turn(Direction direction);
moveToPoint(Point point);
...
}
Take the above only for a (non serious) sample, of course :)
