How to "dynamically" cast an instance of Object type into its specific data type?

30,186

Solution 1

The best way to handle this efficiently and cleanly is to have foo return a holder class for the object.

abstract class Holder<T> {
    private final T object;

    protected Holder(T object) { this.object = object; }
    public T get() { return object; }
    public abstract void doSomething();
}

public Holder foo(int opt) {
    if (opt == 0) return new Holder<String>("") {
        public void doSomething() { }
    };
    else if (opt == 1) return new Holder<Integer>(1) {
        public void doSomething() { }
    };
    else if (opt == 2) return new Holder<Double>(1.0) {
        public void doSomething() { }
    };
    // many more
}

public static void main(String... args) throws IOException {
    Holder h  = foo(x); //x is a value obtained during runtime, e.g. from user input

    //now I want to call doSomething method
    h.doSomething();
}

Solution 2

Basically you want overload resolution performed at execution time - you're not going to be able to do that very simply.

In some cases, the visitor pattern can help, but I don't think it would here. I think you're stuck with either the code that you've got here, or reflection. I've never been as keen on the visitor pattern as some of my colleagues - it always feels a little messy - but it's worth a thought.

Could you make foo call the right doSomething overload instead of just returning the value? That's the bit of code which knows what's being constructed - if you could pass it an object to call doSomething on with the appropriate overload, you'd end up with the type-specific logic all in one place.

In Java 7 it's possible that invokedynamic will be useful in this sort of situation - certainly the dynamic type in C# 4 would help - but I haven't looked into invokedynamic enough to say for sure.

Solution 3

The problem here may be one of separation of concerns. Java is an object-oriented language, and it may help to try and solve the problem in an object-oriented way. In this case, you might ask why main should care what kind of Object o is. Instead, you might consider having a set of classes, each of which knows how to do something in its own way.

abstract class Thing {
   abstract void doSomething();
}

class IntegerThing extends Thing {
  public void doSomething() {  /*whatever*/ };
}

class FloatThing extends Thing  {
  public void doSomething() { /*whatever*/ };
}


//Then later:

int foo(int type) {
  if(type == 0) return new IntegerThing(0);
  if(type == 1) return new FloatThing(7.5);
  if(type == 3) return new StringThing("Florence");
}

int main(String args[]) {
   Thing something = foo(x);
   something.doSomething();
}

Your foo() method effectively becomes a factory, and from that point forwards you no longer need to care about what kind of Thing has been returned by foo.

Solution 4

Java reflection helps somewhat, but there is a missing piece of data. Also, reflection typically throws MANY checked exceptions that you will need to catch. (I included a list after the code)

What is the object that holds the "doSomething" methods? In this example, I use the variable name "someObject" to represent the object holding the "doSomething" method. You need to substitute this for something more sensical.

Also, just a warning, this will not catch derived types, so if the method definition doesn't match the type given, you will get a method not found exception.

//now I want to call doSomething method
// (1)
Method method = someObject.getClass.getMethod("doSomething",new Class[] {o.getClass()});
method.invoke(someObject, new Object[] {o});
// (2)

Warning: You need to deal with the following exceptions when using reflection this way: (This is not an unusual list incidentally, reflection is typically very noisy in terms of exceptions)

NoSuchMethodException - if a matching method is not found or if the name is "<init>"or "<clinit>". 
NullPointerException - if name is null
SecurityException - if access to the information is denied.
IllegalAccessException - if this Method object enforces Java language access control and the underlying method is inaccessible.
IllegalArgumentException - if the method is an instance method and the specified object argument is not an instance of the class or interface declaring the underlying method (or of a subclass or implementor thereof); if the number of actual and formal parameters differ; if an unwrapping conversion for primitive arguments fails; or if, after possible unwrapping, a parameter value cannot be converted to the corresponding formal parameter type by a method invocation conversion.
InvocationTargetException - if the underlying method throws an exception.
NullPointerException - if the specified object is null and the method is an instance method.
ExceptionInInitializerError - if the initialization provoked by this method fails.
Share:
30,186
Pahlevi Fikri Auliya
Author by

Pahlevi Fikri Auliya

Think!

Updated on July 09, 2022

Comments

  • Pahlevi Fikri Auliya
    Pahlevi Fikri Auliya almost 2 years
    public Object foo(int opt){
      if (opt == 0) return new String();
      else if (opt == 1) return new Integer(1);
      else if (opt == 2) return new Double(1);
      else if ...
      .. and many more
    }
    
    public void doSomething(String s){..}
    public void doSomething(Integer i){..}
    public void doSomething(Double d){..}
    ... and many more doSomething method
    
    public static void main(String[] args){
      ...
      Object o = foo(x); //x is a value obtained during runtime, e.g. from user input
    
      //now I want to call doSomething method
      // (1)
      if (o instanceof String) doSomething((String) o);
      else if (o instanceof Integer) doSomething((Integer) o);
      else if (o instanceof Double) doSomething((Double) o);
      ...
      // (2)
    }
    

    Is there any better way to simplify statements enclosed by (1) ... (2)?
    Does Java Reflection help?

  • Joachim Sauer
    Joachim Sauer about 13 years
    As far as I know the Java language doesn't have any way to actually use the invokedynamic bytecode. I'd like to be proven wrong on this one, 'though.
  • Jon Skeet
    Jon Skeet about 13 years
    @Joachim: Ah, I thought that was one of the proposed language changes in Java 7. I've lost track of what's in and what's out, to be honest...
  • jeffry copps
    jeffry copps almost 7 years
    Return type of foo has to be Thingy. :)