This week's book giveaway is in the OCAJP 8 forum. We're giving away four copies of OCA Java SE 8 Programmer I Study Guide and have Edward Finegan & Robert Liguori on-line! See this thread for details.
Ronwaldo Cruz wrote:
// Dog will always, always have dogName, no exceptions
// Dalmatian can have dogName null
Only one of these statements can be true at a time. If Dog always has a dogName, and Dalmatian is a Dog, then Dalmatian must always have a dogName. If, on the other hand, a Dalmatian can have a null dogName, and Dalmatian is a Dog, then Dog will not always have a dogName. You get a null pointer exception because you break the rules you define for Dog in a subclass of Dog. You need to fix one of the rules.
Joined: Oct 17, 2006
What you say is true I did break the first rule. Unfortunately, this is the situation I find myself in.
In the design, the original class "Dog" was meant to always have a "dogName". Now many years later, some components (let's call it "DogDialog") who are very tightly coupled with "Dog" needs to be subclassed (mainly to get the UI inherited, it's wrong I know but my instructions are not to reinvent the wheel by writing a new dialog. DogDialog is pretty big and writing duplicate code is very difficult to justify for budget reasons) to perform different dialog operations than its superclass.,
In this subclass dialog (let's call it DalmatianDialog), I will need to deal with objects that DON'T HAVE dogName, hence the creation of "Dalmatian" class (but it still needs to be an instance of Dog as DogDialog is tightly coupled with Dog).
Another way of putting it is having a Dalmatian class without having a Dog super class and then having a need for a chihuahua and being forced to subclass a Dalmatian because of its "Dog" properties. It will not be correct, but I want it to be as "correct" as possible
The whole things seems like a poor design, and you should at least bring it up to whoever is giving you the assignment.
That said, because now the rule that all Dogs must have a dogName, then you should adjust the Dog class to consider cases where the dogName might be null. For example the equals method might look like:
Note that breaking the 'always non-null' rule will likely cause errors in other places inside Dog. You have to walk through it with a fine-toothed comb to make sure you don't assume dogName is non-null someplace else which would cause your code to break (which is why you should not break rules like this).
Steve is correct; if you have a field dogName in the superclass, you have that field in the subclass.
The bit about super.equals(obj) depends on there being a correctly-overridden equals() method in the superclass, which should already test whether the getClass() objects are the same, and more important, tests whether obj is null. If that is not fulfilled, then your method is susceptible to an NullPointerException.
The real problem is that you are not working on the basis that a Dalmatian IS-A Dog. You ought to have a single field name, and no additional fields in Dalmatian. If you get your inheritance right, the whole thing becomes much much simpler Try it and see!
This was written before I saw Campbell's reply, which answers at least one of the same questions. But I'm feeling to lazy to re-edit it now, so just ignore the duplication. Or consider it an alternate perspective. Whichever works best.
Ronwaldo Cruz wrote:I'm having problems with if (!super.equals(obj)). What is this for anyway?
Typically your base class may have a set of fields, and your subclass may have a set of fields. And typically your base class would have an equals() method of its own, which checks equality of all the fields in the base class. In the subclass, equality will depend on both the base class fields and the subclass fields. But why should you repeat the code to check equality of the base class fields? These are already checked by code in the base class, so it should be easy to simply call super.equals() from the subclass. So that's what eclipse does.
Now in your case, the base class and subclass are not really compatible, so you need to do more work. You can delete the call to super.equals() - but that means you will have to write additional code to do whatever else the base equals() method would have done. In your example, that's easy, as you only have one field in the base class: dogName. But if you have other fields, you will need to compare them as well.
Another possibility is to do something like this:
Here we simply replace null with "", for the duration of the super.equals(), then set it back to null. That works fine for single-threaded code. With multithreading, you could create some confusing situations with this, so be careful.
It might be easiest to "fix" all Dog instances, replacing any null name with a "", permanently.
But that only works if you can, indeed, find all Dog objects in the system. Or if you can at least find all the Dogs that you need to call equals() on, before you call equals().
Joined: Oct 13, 2005
Beware: there are lots of misprints in what I posted. I tested that code and found I needed additional round brackets (), like this:
Joined: Oct 17, 2006
Thanks for all the replies.
I agree that the issue is poor design, but I think a large part of it is because we have needs now that were not so obvious back when the class was first created. To put things in better perspective I will revise my example as it does not fully capture the situation.
The more appropriate example for Dog is actually PersonalComputer with variables monitor, cpu and keyboard. Non of these fields should be null as they are essential to the concept of a complete computer. And so there is a dialog created with a structure tree inside wherein you can choose a monitor, cpu and keyboard. These are then assembled to come up with a PersonalComputer instance and placed in a variable reference in the dialog.
So now that very same dialog is needed to do a different behavior. It is needed to select individual computer parts instead of a PersonalComputer combination of parts. They share the same UI prompting the subclassing of that dialog. But the problem is that the dialog is very tightly coupled to the PersonalComputer class and if you are to reuse methods in the dialog, you need to make use of a PersonalComputer instance.
To get around this, I created a subclass PersonalComputerParts. This then no longer needs to have a full set of monitor, cpu and keyboard. It can be a monitor, a monitor and a cpu, or all three (This doesn't make sense in the analogy but this is the business rule I'm dealing with).
Naturally I get null pointers everywhere so I overriden a lot of PersonalComputer methods two of which are equals and hashcode.
Do you guys think that I should have written a completely new dialog and not subclassed everything? I know that this is no longer a very sound object-oriented design but we're not building things from scratch and there are risks in refactoring classes that are heavily used elsewhere
It boils down to this I think going back to the dog example. If you have a Dalmatian (without a Dog superclass) and then the need arises to have a Chihuahua class and given that the object Dalmatian is used very heavily elsewhere in the system do you:
a) Refactor Dalmatian, gut out shared code and create a Dog superclass which will then be subclassed by both Dalmatian and Chihuahua
b) Completely write Chihuahua from scratch which will then have redundant code with Dalmatian (NOTE: Dalmatian class has a LOT of code in it)
c) Make Chihuahua extend Dalmatian for practicality
In my humble opinion, programmers are eventually forced to make choice c when dealing with old systems and new requirements. But that's just me, I would appreciate any opinions regarding this
Joined: Oct 13, 2005
I have realised why I needed additional () earlier: the precedence of ?: is lower than of &&.
A chihuahua IS NOT A Dalmatian, but it IS A dog. So that determines the inheritance Structure.
As for the PersonalComputer class: that sounds like a case of "favour composition over inheritance"; a Keyboard IS NOT A computer, so its class should not inherit Computer, etc. You can have this sort of inheritance structure, however:
Get the classes set up and working before you try any Trees for building computers.
Do your work in stages, otherwise you will bite off more than you can chew and get all confused.
By the way: here is a suggested Chihuahua class:It might just possibly look similar to something you saw earlier today . . .