0

I am using a system in my program in which there is one central "Manager" class that keeps track of the other classes and then other classes that handle various features of the program (called "Feature" classes). I have an abstract Manager class in a common library that implements several basic "Feature" classes. One of these feature classes needs a generic argument and other feature classes need to be able to access said generic argument.

This is not an issue when I am directly accessing a single instance of this generic argument. The issue occurs when I need to access a Set of them.

Here is an example:

public abstract class AbstractManager<T extends User>{
    private UserManager<T> userManager;

    public UserManager<T> getUserManager(){
        return userManager;
    }
}

-

public class UserManager<T extends User>{
    private HashMap<String, T> userMap;

    public UserManager(){
        userMap = new HashMap<>();
    }

    public T getUserEntry(String name){
        return userMap.get(name);
    }

    public Set<Map.Entry<String, T>> getUserEntries(){
        return userMap.entrySet();
    }

}

-

public class AnotherFeatureClass{
    private AbstractManager manager;

    public AnotherFeatureClass(AbstractManager manager){
        this.manager = manager;
    }

    public void doSomething(){
        User user = manager.getUserManager().getUserEntry("username"); //this works fine
    }   

    public void doSomethingElse(){ //this method does something to every user

        //I would like something like this -- I don't imagine it is impossible since whatever T is it extends user
        for(Set<Map.Entry<String, T>> entry : manager.getUserManager().getUserEntries()){
            entry.getValue().doSomething();
        }

        //I'm using this right now -- it is very messy probably has many bugs that do who knows what and I can't help but assume there is a better way to do it
        for(Object obj : manager.getUserManager().getUserEntries()){
            try{
            @SuppressWarnings("unchecked")
            Set<Map.Entry<String, T>> entry = ((Set<Map.Entry<String, T>>) obj);
            entry.getValue().doSomething();
            }catch(ClassCastException e){
            e.printStackTrace();
            }
        }
    }

}
john01dav
  • 1,842
  • 1
  • 21
  • 40
  • 1
    Don't use raw types. Your field manager in AnotherFeatureClass should be of type AbstractManager, not of type AbstractManager. Since AnotherFeatureClass is not generic, T does not mean anything in its context. – JB Nizet Feb 21 '16 at 08:02
  • If I set an AbstractManager variable to a AbstractManager is that legal? – john01dav Feb 22 '16 at 00:36
  • No. http://stackoverflow.com/questions/2745265/is-listdog-a-subclass-of-listanimal-why-arent-javas-generics-implicitly-p – JB Nizet Feb 22 '16 at 06:45

1 Answers1

0

The basic rule is never use raw types. This doesn’t imply that you always have to specify a concrete type nor that you have to add a type parameter to every class.

That’s what wildcards are for:

public class AnotherFeatureClass {
    private AbstractManager<? extends User> manager;

    public AnotherFeatureClass(AbstractManager<? extends User> manager){
        this.manager = manager;
    }

    public void doSomething() {
        User user = manager.getUserManager().getUserEntry("username"); //still works fine
    }   

    public void doSomethingElse() {
        for(Map.Entry<String, ? extends User> entry:
                                              manager.getUserManager().getUserEntries()) {
            // assuming doSomething() is a method declared in User
            entry.getValue().doSomething();
            // e.g. you could also write
            User u = entry.getValue();
        }
    }
}

So AnotherFeatureClass isn’t generic and may work together with an arbitrarily typed AbstractManager, however, since AnotherFeatureClass has no knowledge about the actual type, it can not add new User instances (of the appropriate, unknown (sub)type) to the manager. But reading and processing users using the base type User works, as the actual type must be assignable to User.

Holger
  • 285,553
  • 42
  • 434
  • 765