0

I'm trying to write a method in Java that has a mixed return type (like you have in PHP).

This method is supposed take a simple user input string and, using Generics, convert it to the appropriate type and return that value.

@SuppressWarnings("unchecked")
public static <T> T askUser(String message, Class clazz) throws NumberFormatException {
    // get the user input
    String inputString = JOptionPane.showInputDialog(message),
            className = clazz.getCanonicalName();
    // return a Boolean, Integer or String
    if (className.equals(Boolean.class.getCanonicalName())) {
        return (T) Boolean.valueOf(inputString);
    } else if (className.equals(Integer.class.getCanonicalName())) {
        return (T) Integer.valueOf(inputString);
    } else /*return the String*/ {
        return (T) inputString;
    }
}

And I'm running the code like this:

    String userName = name_game.<String>askUser("Please enter your name.", String.class);
    Integer userAge = name_game.<Integer>askUser("Please enter your age.", Integer.class);
    tellUser("So your name is %s and your age is %d...", userName, userAge);
    Boolean confirm = name_game.<Boolean>askUser("Is this correct?", Boolean.class);
    tellUser("You said %b.", confirm);

But I can't figure out how to get it working without the (T) casting, and passing the Class parameter also seems as though it could be avoided.

How can I write this method without having to resort to casting to (T) ?

Ozzy
  • 8,244
  • 7
  • 55
  • 95

3 Answers3

3
public static <T> T askUser(String message, Class<T> clazz) throws NumberFormatException {
    // ...
}

Internal casts and generic in front of method invocation can be avoided too with this declaration.

ilmirons
  • 624
  • 6
  • 16
  • Internal casts can not be avoided by this, only the explicit generic parameters can. You still need to cast the return value to `(T)` – Sean Patrick Floyd Oct 08 '13 at 10:49
  • 1
    With the above, the call becomes `String userName = name_game.askUser("Please enter your name.", String.class);` – John B Oct 08 '13 at 10:53
2

Generics don't give you the ability to:

take a simple user input string and, using Generics, convert it to the appropriate type and return that value.

For example: What if someone puts a File and want to convert it to a Dog?

You can possibly create a interface CreatableFromString with a createFromString( String userData) signature, wich instantiate proper object using given String.

Then, you have full type-safety and even without using generics - and you don't have to worry about File<->Dog situation because only classes that implements CreatableFromString can be put into askUser parameter.

The cons of this solution is that askForUser will return CreatableFromString, but you can make this generic:

Dog dog = name_game.createByUserAskDialog( "Please enter dog name.", Dog.class );

and:

public static <T extends CreatableFromString> T askUser(String dialogTitle , Class<T> clazz) {
    ...get "data" from user...
    return T.createFromString( data ) ;
}

If you want to translate user string to existing types, you can pass not Class<T> as argument but your factory:

public interface CreatableFromStringFactory<T> {
  public T createFromString( String s );
}

...

public class BooleanFromStringFactory implements CreatableFromStringFactory<Boolean> {
  public Boolean createFromString( String s ) {
    if ( s.equals( "yes" ) ) {
      return Boolean.TRUE;
    }
    return Boolean.FALSE; 
  }
}

...
booleanFromStringFactory  = new BooleanFromStringFactory();
...
Boolean c = name_game.askUser("Is this correct?",booleanFromStringFactory );
Piotr Müller
  • 5,323
  • 5
  • 55
  • 82
1

I think it would be better to write some code to convert string to the appropriate data type and to have different methods for obtaining user's age, name and so on.

briarheart
  • 1,906
  • 2
  • 19
  • 33