2

I currently am working on a project where I have 3 user classes, lets say UserA, UserB, UserC, that inherit from a abstract User class.

The program is supposed to emulate a system wish requires users to login and logout.

The instances of those users are stored in 3 separate lists.

Only one user is "logged" at any time, so I have a User currentUser variable.

When I want to call a method specific to UserA, B or C, I do a cast. How can I avoid doing that cast while keeping the code elegant?

Code ex:

public abstract class User { } public class UserA extends User { public void clearAgenda(); } public class UserB extends User { } public class UserC extends User { } public class System { private User loggedUser; public void clearUserAgenda() { ((UserA) loggedUser).clearAgenda(); } } 

Note: I know before hand that if a operation is being used, the current user supports that operation. (ex: if System.clearUserAgenda() is called then the user is a instance of UserA)

4
  • Why three separate lists? Commented May 22, 2014 at 20:26
  • 1
    It sounds a bit dubious that you say that you have the User currentUser, but later want to call a method and know which type of user this currentUser is. How is the currentUser stored? And who is responsible for calling methods like the clearUserAgenda (or other methods that may only be called when a particular type of user is the currentUser)? That is, how does the caller know which type of user is currently logged in? Commented May 22, 2014 at 20:27
  • And what is the use cases you are trying to model? Commented May 22, 2014 at 20:27
  • You could have a general method in the User interface that System calls. Something sufficient vague, like doWork() or processRequest(). Then the UserA implementation would call clearAgenda(), and the other implementations would do the correct things for those classes. Commented May 22, 2014 at 20:38

3 Answers 3

4

If you want to avoid the cast, you have to declaire the method clearAgenda() in the Abstract Class User. If you dont do that only UserA can use this method so it must be casted.

Sign up to request clarification or add additional context in comments.

3 Comments

But in a system where those operations are called a lot of times wont doing those casts have a tremendous effect on performance?
Well tremendous is a big word to use here. But yes it will effect performance. However that's why if you introduce it in the abstract class User you can prevent downcasting. You can leave the method 'empty' in the User classes were you don't want to an implementation.
yes because of this look at the answer from mprivat, he illustrated the problem very well. If you put the method in the User class and make it abstract you can override it and implement it in your UserA, etc. Class. If all users do the same you could declaire the method in the User Class and all users (UserA, UserB, etc.) can use this method.
3

It's a clear cut case for polymorphism. You can declare the method in User (as Baalthasarr says), then override it in the subtypes (UserA, UserB, etc) as necessary. Then you just use loggedUser.clearAgenda() and the JRE will figure out which method to use automatically at runtime.

public abstract class User { public void clearAgenda() {} // do nothing (or or something if you want) } public class UserA extends User { @Override public void clearAgenda() { ... } // do stuff specific to A } public class UserB extends User { @Override public void clearAgenda() { ... } // do stuff specific to B } public class UserC extends User { } public class System { private User loggedUser; public void clearUserAgenda() { // This will use the most appropriate method. If the type is // UserA, UserA's `clearAgenda` method will be used. Similar // for UserB. For UserC, The abstract User class's `clearAgenda` // method is used, because it was not overridden in UserC. loggedUser.clearAgenda(); } } 

If there's stuff you want to do that's common to all User subclasses, you can put it in User and still run it even if you override the method. For example:

public class UserA extends User { @Override public void clearAgenda() { super.clearAgenda(); // do the stuff common to all User subclasses ... // do stuff specific to A } } 

2 Comments

Thank you, just a quick question: should the User.clearAgenda() method trow an exception in case its called (since in theory it should never be called)? ex: if UserC.clearAgenda() is called
You could, but it's not necessary (and I wouldn't go out of my way to defer an exception to runtime). If you want to force all subclasses to declare their own version of the method, use this syntax as previously mentioned: public abstract void clearAgenda();.
3

You pretty much have two options:

  1. Use reflection but that pretty much takes out the "keeping code clean" requirement.
  2. Make all User comply with a common interface (i.e. put clearAgenda() in User, provide a default blank implementation and override it in UserA). Do the same for all the methods.

I recommend option 2. You're running into this conundrum because you're giving the caller of System class the responsibility to know what sort of user you have. System then assumes the caller is right (which is a really bad assumption in the real world) and calls the method blindly. If System can't know, it's ok, but it needs to be able to deal with the user calling the wrong method on the wrong user. For that, make it not be wrong to call clearAgenda() on a UserB. In the superclass method, you can just log that the method was called and not overridden, this way you'll be able to detect problems without crashing the whole thing with a cast exception.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.