• Post Reply Bookmark Topic Watch Topic
  • New Topic

Polymorphism questions

 
Alan Ong
Greenhorn
Posts: 6
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hello everyone! I'm currently reading a book called Java How to Program(Early objects) - 10th edition and am onto Polymorphism and there is a part which totally confuses me and need everyone's enlightenment.
Please bear with me as there are quite a number of codes here and kindly find the attached file as the uml diagram for the overall project.



















Ok here are my questions. in the PayrollSystemTest class lines 44-65 iterate through the array and invoke methods toString and earnings with Employee variable currentEmployee. And specific methods for each class are being invoked for this case. The book explains that this process is known as dynamic binding by determining the type of the object to which currentEmployee refers to during execution time. Furthermore, it says that only methods of class Employee can be called via an Employee variable including the methods of class Object. A superclass reference can be used to invoke only methods of the superclass---the subclass method implementations are invoked polymorphically. Till this part I kind of understand what it's trying to say.

At line 49, it uses the instanceof operator to determine if the Employee object's type is BasePlusCommissionEmployee and if it is true that the object referenced by currentEmployee is a BasePlusCommissionEmployee then we shall have to increase their base salary by 10%. Line 53-54 shall then downcast the object type to BasePlusCommissionEmployee and invoke the busclass' getBaseSalary and setBaseSalary on the current Employee object.

What I don't understand is that since through the array of objects we could invoke the relevant object type's toString method why can't we do the same as in to invoke the methods, getBaseSalary and setBaseSalary directly and have to downcast before doing so.

Thank you all for reading my post here and hope you can help me understand the logic here.


2016-12-14.png
[Thumbnail for 2016-12-14.png]
 
Campbell Ritchie
Sheriff
Posts: 52055
90
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I thought Deitel knew better than that
Once you start using instanceof, you shou‍ld start wondering whether you have a correct or an incorrect inheritance hierarchy. The trimToSize and ensureCapacity methods which exist in some kinds of List and not in other sorts of List are an example which has caused no end of annoyance to people posting on this website. You have to use instanceof and cast Lists to use those two methods.
There are problems in that code, against which the offence of using doubles for money pales into insignificance.
One is the use of public setXXX methods like that. Public setXX and getXXX methods are part of the Bean pattern, but the intention is not to allow one simply to change the values of fields. The idea of the bean pattern is that code elsewhere can record the state of your object, and create an object elsewhere (or later) otherwise identical to it. Simple use of setSalary methods might set the salary to anything, so the Bean pattern is maybe best confined to a secure environment where you can be confident everybody know what they are doing and where you are not at risk of hostile code. What they are doing is taking the salary and enhancing it by 10%. What they ought to have done is given the superclass an increaseSalary method. That is the single responsibility principle; let the Employee object calculate its salary raise for itself.
I can understand the notion of increasing the salary by 10% for everybody in a particular category. But instanceof is probably not the best way to do it.
 
Junilu Lacar
Sheriff
Posts: 9355
96
Android Eclipse IDE IntelliJ IDE Java Linux Mac Scala Spring Ubuntu
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Alan Ong wrote:... A superclass reference can be used to invoke only methods of the superclass... (← you already had the answer!)

At line 49, it uses the instanceof operator to determine if the Employee object's type is BasePlusCommissionEmployee and if it is true that the object referenced by currentEmployee is a BasePlusCommissionEmployee then we shall have to increase their base salary by 10%. Line 53-54 shall then downcast the object type to BasePlusCommissionEmployee and invoke the busclass' getBaseSalary and setBaseSalary on the current Employee object.

What I don't understand is that since through the array of objects we could invoke the relevant object type's toString method why can't we do the same as in to invoke the methods, getBaseSalary and setBaseSalary directly and have to downcast before doing so.

The loop variable, currentEmployee, is of type Employee, the superclass of all other XXXEmployee objects. On lines 56-60, you are accessing methods that only the BasePlusCommissionEmployee class has. Per your understanding that I highlighted in the quote, a superclass reference (Employee currentEmployee) can only be used to invoke methods in the superclass. To invoke methods introduced in a subclass, you'd need a reference to that subclass (BasePlusCommissionEmployee employee), hence the need to cast currentEmployee to that type.

As Campbell pointed out, the use of instanceof here should be recognized as a red flag that there's something seriously flawed in your design if you have to resort to this kind of code.  I suppose Deitel gave this example to illustrate the concepts we just discussed but you should note that this is not a proper way to code/design real-world applications. It's unfortunate that somehow, either through the author's failure to give caveats about real-world application or the students' failure to recognize that nuance, these practices work their way into real-world applications. There's a high cost involved in propagating these kinds of poor coding/design practices, not the least of which includes bugs, software rigidity, and a high cost of maintenance.
 
Alan Ong
Greenhorn
Posts: 6
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

The loop variable, currentEmployee, is of type Employee, the superclass of all other XXXEmployee objects. On lines 56-60, you are accessing methods that only the BasePlusCommissionEmployee class has. Per your understanding that I highlighted in the quote, a superclass reference (Employee currentEmployee) can only be used to invoke methods in the superclass. To invoke methods introduced in a subclass, you'd need a reference to that subclass (BasePlusCommissionEmployee employee), hence the need to cast currentEmployee to that type.


Thank you for taking the time to explain to me but I have a question here.

At line 64 why is it that currentEmployee is able to access all the relevant subclasses' earnings method without downcasting the other object types?

Thanks again bro!
 
Paul Clapham
Sheriff
Posts: 21708
33
Eclipse IDE Firefox Browser MySQL Database
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Alan Ong wrote:At line 64 why is it that currentEmployee is able to access all the relevant subclasses' earnings method without downcasting the other object types?


Well, as you already know the currentEmployee variable is of type Employee. And Employee has a public method named earnings(). So naturally the Employee variable currentEmployee can call Employee's public method earnings().
 
Alan Ong
Greenhorn
Posts: 6
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Paul Clapham wrote:
Alan Ong wrote:At line 64 why is it that currentEmployee is able to access all the relevant subclasses' earnings method without downcasting the other object types?


Well, as you already know the currentEmployee variable is of type Employee. And Employee has a public method named earnings(). So naturally the Employee variable currentEmployee can call Employee's public method earnings().


Hello there Paul! thanks for replying to my topic.

Please correct me if I'm wrong. I just learned Inheritance before proceeding to the Polymorphism topic and I remember that it says superclasses are able to invoke subclasses' methods as a superclass can be a subclass type which totally makes sense.
But over here in this application example, the Employee array object should be able to invoke its subclasses' methods as well right?
Furthermore, line 64's currentEmployee.earnings() actually invokes all the subclasses' earnings method without having to downcast the variable at all.
 
Junilu Lacar
Sheriff
Posts: 9355
96
Android Eclipse IDE IntelliJ IDE Java Linux Mac Scala Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I think your confusion comes from conflating two entirely different things: a reference variable's declared type and an object's actual type. The two are different.

currentEmployee is a reference variable with a declared type of Employee.

When you assign an object to this variable, the object's actual type does not change to Employee. A BasePlusCommissionEmployee object does not change into an Employee object just because you assign it to the currentEmployee variable. The only thing that changes is how you can "see" the object, which is as an Employee.

Think of the reference variable as a filter or mask that only allows you to see through the "holes" that are in it. The "holes" are determined by the methods defined in the variable's declared type, Employee. Since the Employee class has fewer methods than what is defined by the BasePlusCommissionEmployee class, you are more restricted in what you can do with a reference variable like currentEmployee: you can only see the methods defined in the Employee class even though the actual object that is referenced has more methods underneath the "mask". Those extra methods remain hidden and inaccessible through the more restrictive reference variable because it does not have the "holes" that will allow you to see those extra methods.

If you want to to see more of what the actual object is capable of doing, then you have to change the "mask" to a reference variable of type BasePlusCommissionEmployee. The employee variable is one such "mask". Since you are getting the reference value from currentEmployee, however, you have to assure Java that it's Ok to switch from the more general mask of Employee to a more specific mask of BasePlusCommissionEmployee. This "assurance" is done via the downcast.

Does that make more sense?
 
Campbell Ritchie
Sheriff
Posts: 52055
90
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Junilu Lacar wrote:. . .  I suppose Deitel gave this example to illustrate the concepts we just discussed but you should note that this is not a proper way to code/design real-world applications. . . .
Unfortunately I don't think they do. It is a long time since I used that example and I have had the good fortune to learn from people like Jim Yingst, Ulf Dittmer, Fred Rosenberger, Jeanne Boyarsky, Winston Gutkowski, Liutautas Vilda and Junilu Lacar and many others since then. So I have learnt to get suspicious of instanceof. I also had the good fortune to have Elizabeth Norval lecture me. She told me off when I tried to use instanceof in an application with a slight similarity to that discussed here only with shapes rather than text colours. She also laughed like a drain when I misled Joseph Onibokun into writing −cos instead of cos and his clock application resolutely went anti‑clockwise (that was almost the first thread I participated in on what was then called JavaRanch). Elizabeth told me to use polymorphism instead and after about ten minutes I worked out how to do it polymorphically.
I have come to learn there is a thing called type extension and there is a thing called functional extension and I can never remember which is which, but I have become suspicious of whichever type adds fields and adds methods. For this very reason. If you have an increaseSalary method only in one subtype, how are you going to call it. And why don't the other types have an increaseSalar method too Maybe it shou‍ld be called increasePay, and it shou‍ld be available in every class.
It is a long time since I used that Deitel example, but I don't think they explain what sort of awkward coding instanceof represents. Increasing the pay of one category of staff, now that is bound to cause trouble in real life, too, but how would you apply such different pay rises? What about an enum associated with the different classes, so you can get a category of staff and call increaseSalary for those with a particular category?

I shall let others think about that. I can't think of any better suggestions at the present.
 
Junilu Lacar
Sheriff
Posts: 9355
96
Android Eclipse IDE IntelliJ IDE Java Linux Mac Scala Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Campbell Ritchie wrote:I shall let others think about that. I can't think of any better suggestions at the present.

Overloaded method dispatch in Java can be done using the Visitor Pattern / double dispatch.
 
Campbell Ritchie
Sheriff
Posts: 52055
90
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Junilu Lacar wrote:. . .
What about this, where the ints represent percentages?Obviously my increasePay method will have to accept negative arguments not less than −100
 
Junilu Lacar
Sheriff
Posts: 9355
96
Android Eclipse IDE IntelliJ IDE Java Linux Mac Scala Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
That may seem reasonable given the simplicity of the example. However, if you apply it to the Deitel example where the algorithm is more involved, you'll see that the responsibility of the visitor (PayAdjuster in this case) is to know the different algorithms that apply to various subclasses. Besides, bosses seldom trust employees enough to let them calculate their own pay raises.
 
Campbell Ritchie
Sheriff
Posts: 52055
90
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Junilu Lacar wrote:. . . bosses seldom trust employees . . .
 
Piet Souris
Rancher
Posts: 1577
33
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Junilu Lacar wrote:

It is a nice example, but I do not like this particular class. Each time a new Employee subclass is added, we must adjust this class. Now, something has to be adjusted, but I would prefer something like:
 
Junilu Lacar
Sheriff
Posts: 9355
96
Android Eclipse IDE IntelliJ IDE Java Linux Mac Scala Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks, Piet. Yes, it's the one apparent downside to this but if you think about it and really want to avoid breaking the Open-Closed Principle all you have to do is extend the class and add the new overloaded method(s). Combined with Dependency Injection you can still adhere to good design principles.
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!