Search code examples
javadowncast

How can I avoid Java downcasts?


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)


Solution

  • 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
        }
    }