1

I have a question on how to call an objects base member when instantiated through an interface.

Suppose I have the following interface and concrete classes in a framework I am trying to build:

public interface UsedClass {
    public boolean getBool();
}

public class User implements UsedClass {

    private String userName;
    private String userRole;
    public User(String userName, String userRole){
       this.userName = userName;
       this.userRole = userRole;
     }
    public boolean getBool() {
        // some code
    }
    public int getUserName() {
        return userName;
    }
    public int getUserRole() {
        return userRole;
    }

And an implementing class:

public class Run implements UsedClass {        
    private String runName;
    private int runNumber;
    public Run(String runName, int runNumber){
       this.runName = runName;
       this.runNumber = runNumber;
     }
    public boolean getBool() {
        // some code
    }
    public String getRunName() {
        return runName;
    }
    public int getRunNumber() {
        return runNumber;
    }
}

But I cannot put methods getRunName() or getUserRole() into the interface!

The end goal is to create a FactoryClass to handle the objects passed from a database GUI.

I would like to know if there is a better way then using class reference be able to safely call methods of Run or User such as:

public class EntityFactory {
  public static Object getValueAt(int rowIndex, int columnIndex, UsedClass usedClass) {
        if (usedClass.getClass().getSimpleName().equals("User")) {

            switch (columnIndex) {
            case 0:
                return ((User) usedClass).getUserName();
            case 1:
                return ((User) usedClass).getUserRole();
            default:
                return null;
            }
        } else if (usedClass.getClass().getSimpleName().equals("Run")) {
            switch (columnIndex) {
            case 0:
                return ((Run) usedClass).getRunName();
            case 1:
                return ((Run) usedClass).getRunNumber();
            default:
                return null;
    }
}

I have read several SO posts type casting when objects are of interface references in Java and Java cast interface to class

where it is implied that reference casting is not advised, but since I cannot put all methods into the interface, what would be advised?

8
  • At least use usedClass instanceof User instead of usedClass.getClass().getSimpleName().equals("User"). Commented May 3, 2017 at 12:38
  • Maybe you could add some getValueAt(int columnIndex) method in the interface and implement it in subclasses ? Commented May 3, 2017 at 12:40
  • Jesper: Thanks. Berger: But its more OOP to have this getValueAt in a single class. At least that is what I thought. Commented May 3, 2017 at 12:43
  • Don't use strings to identify classes like this. You lose all type safety. This is not object-oriented programming. Your type system is wrong. You need an interface that allows client classes to use it without downcasting or any of that instanceof garbage. Commented May 3, 2017 at 16:00
  • 1
    There is no positive example without knowing more detail about why you are doing it this way. But @LewBloch is correct that this is not a good way to do things. Commented May 4, 2017 at 14:11

3 Answers 3

1
static interface ColumnSource<T> {
    String getColumn(T value, int index);
}
static Map<Class, ColumnSource> map = new HashMap();
static {
    map.put(User.class, new UserNameAndRoleSource<User>() {
        public String getColumn(User user, int index) { 
            switch (index) {
                case 0: return user.getUserName(); 
                case 1: return user.getUserRole(); 
                default: throw new RuntimeException();
            }
        }
    });
    map.put(Run.class, new ColumnSource<Run>() {
        public String getColumn(Run run, int index) { 
            switch (index) {
                case 0: return run.getRunName(); 
                case 1: return run.getRunNumer();
                default: throw new RuntimeException();
            }
        }
    });
}
public static Object getValueAt(int rowIndex, int columnIndex, Object o) {
   Class type = o.getClass();
   ColumnSource source = map.get(type);
   if (source == null) throw new RuntimeException(type.getName() + " not supported"); 
   return source.getColumn(o, columnIndex);
}
Sign up to request clarification or add additional context in comments.

3 Comments

This does not solve the problem as the interface will implement methods that Run or User do not use. For example Run has no method getUserRole().
Updated to ColumnSource.getColumn(T value, int index)
This is nice, but also there is a problem that some methods return a String and some an int. Therefore, even if adding another method into the interface of different return type, the getValueAt method could return some exceptions because there are no type in in User, but some in Run.
1

You should use instanceof rather than looking at the simpleName of the class.

Beyond that you are correct. You either need to have an interface containing the common methods which you can then call them in or you need to identify that the object is an instance of a specific class and then do the cast and make the method call.

You could consider using a Map<Class<? extends UsedClass>, Map<Integer, Function<___>>> handlers.

Then your processing would be

handlers.get(usedClass.getClass()).get(columnIndex).apply(usedClass);

Obviously you would want to consider how to handle the unexpected class/index case. The inner Map<Integer,... could potentially be a List<...> depending on how it is being used.

1 Comment

I think this Map is a great idea, but I am having trouble conceptualizing this. Once I get my head around it, I probably will mark this as the acceptable answer.
1

Two things:

  • if at all, you use instanceof instead of string / class name comparison
  • you build your interfaces / classes to be helpful. They are the base of all the things you are doing. If you start with broken abstractions, you are broken. Simple as that.

What I mean is: if there is "common" behavior; then you should express that using a common interface. If not, you start your efforts on an already broken base; and you will be need to create "creative workarounds" all over the place in order to fight the symptoms of that disease.

Maybe one small solution could be to have at least multiple interfaces, like

interface UsedClass { ...

interface SpecialUsedClassA extends UsedClass { ...

interface SpecialUsedClassB extends UsedClass { ...

than you can at least return UsedClass instead of Object.

2 Comments

The common behavior of this project is in an interface. However, I am trying to extract the uncommon behavior, such as run.getRunName() and user.getUserRole...etc for all the getters of all class that implement UsedClass. Your workaround is "creative" and practical if the number of implementations is small. However, for my project, I actually have 23 classes which are actually persistence entities. Therefore I feel that having 23 "special" interfaces is quite tedious and confusing to anyone new to this field.
I see. Still a nice, interesting question. Anyway; is there something I could do to make my answer at least upvote-worthy in your eyes? (and no, i dont have a great idea right now about approaching 23 interfaces).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.