views:

116

answers:

8

Using Java Generics, I tried to implement a generic console input method.

public static <T> T readFromInput(String message, Class<?> c) throws Exception{
        System.out.println(message);
        Scanner scanner = new Scanner(System.in);
        try {
            if(c == Integer.class)
                return (T) Integer.valueOf(scanner.nextInt());
            if(c == String.class)
                return (T) scanner.nextLine();
            if(c == Double.class)
                return (T) Double.valueOf(scanner.nextDouble());
            if(c == Float.class)
                return (T) Float.valueOf(scanner.nextFloat());
        } catch (InputMismatchException e) {
            throw new Exception(e);
        }
        return null;
    }

I'm having a warning "Type safety: Unchecked cast from Integer to T". Aside from @SuppressWarnings, is it possible to avoid this warning?

Are there better ways to implement my method? Thanks in advance

+5  A: 

You can use the Class#castmethod instead, but should leave some comments, because even though cast does not create a warning, it can throw a ClassCastException at runtime if the cast is not possible.

public static <T> T readFromInput(String message, Class<T> c) throws Exception{
    System.out.println(message);
    Scanner scanner = new Scanner(System.in);
    try {
        if(c == Integer.class)
            // the next cast to Integer is safe
            return c.cast(Integer.valueOf(scanner.nextInt()));
        if(c == String.class)
            // the next cast to String is safe
            return c.cast(scanner.nextLine());
        if(c == Double.class)
            // the next cast to Double is safe
            return c.cast(Double.valueOf(scanner.nextDouble()));
        if(c == Float.class)
            // the next cast to Float is safe
            return c.cast(Float.valueOf(scanner.nextFloat()));
    } catch (InputMismatchException e) {
        throw new Exception(e);
    }
    return null;
}

Note that I've changed the method signature slightly - it should be Class<T> and not Class<?> to guarantee, that the Class instance is consistent with the type parameter.

Andreas_D
...which is the whole point of a generic method (when used like this). Using T as the datatype lets you "connect" the return type to the class that is passed as the second parameter.
f1sh
A: 

Reading from this post, http://stackoverflow.com/questions/1959022/java-generic-function-how-to-return-generic-type I got rid of my warning:

public static <T> T readFromInput(String message, Class<T> c) throws Exception{
        System.out.println(message);
        Scanner scanner = new Scanner(System.in);
        try {
            if(c == Integer.class)
                return c.cast(scanner.nextInt());
            if(c == String.class)
                return c.cast(scanner.nextLine());
            if(c == Double.class)
                return c.cast(scanner.nextDouble());
            if(c == Float.class)
                return c.cast(scanner.nextFloat());
        } catch (InputMismatchException e) {
            throw new Exception(e);
        }
        return null;
    }
thirdy
A: 

There's no general way to avoid "Unchecked cast" warning other than using @SuppressWarnings (unchecked) annotation.

In particular case you get this warning because there's no warranty that parameter Class<?> c may be cast to T since Java's generics are checked only in compilation and no checks may be done in runtime.

Vadim Fedorov
A: 

Do it like this:

public static <T> T readFromInput(String message, Class<T> c) throws Exception{
    System.out.println(message);
    Scanner scanner = new Scanner(System.in);
    try {
        if(c == Integer.class)
            return c.cast(scanner.nextInt());
        if(c == String.class)
            return c.cast(scanner.nextLine());
        if(c == Double.class)
            return c.cast(scanner.nextDouble());
        if(c == Float.class)
            return c.cast(scanner.nextFloat());
    } catch (InputMismatchException e) {
        throw new Exception(e);
    }
    return null;
}
abhin4v
A: 

You can do the following:

    public static <T> T readFromInput(String message, Class<T> c) throws Exception{ 
       System.out.println(message); 
       Scanner scanner = new Scanner(System.in); 
       try { 
           if(c == Integer.class) 
               return c.cast(scanner.nextInt()); 
           if(c == String.class) 
               return c.cast(scanner.nextLine()); 
           if(c == Double.class) 
               return c.cast(scanner.nextDouble()); 
           if(c == Float.class) 
               return c.cast(scanner.nextFloat()); 
       } catch (InputMismatchException e) { 
           throw new Exception(e); 
       } 
       return null; 
   } 

However, I strongly recommend not throwing Exception. Throw a more specific exception (either the original runtime exception or some appropriate checked exception).

Eyal Schneider
Most appropriate would be to define an application specific Exception right? thanks btw
thirdy
A: 

You can get rid of the warning by using the concrete class you pass in to cast the object:

    public static <T> T readFromInput(String message, Class<T> c) throws Exception{
        ..
            return c.cast(Integer.valueOf(scanner.nextInt()));
        ..
    }

I would tempted in this case to implement multiple readFromInput methods overridden with your desired types, e.g. public static Float readFromInput(String message, Class c) public static Integer readFromInput(String message, Class c) etc.

andrewmu
+2  A: 

Others have shown how you can do it with Class.cast, but how should you do it?

I suggest readInt, readString, readFloat and readDouble methods. Also, I suspect Scanner may buffer, which could lead you into trouble.

Tom Hawtin - tackline
+2  A: 

I think you might be trying to over abstract the problem. What's wrong with just doing this?

    Scanner scanner = new Scanner(System.in);

    System.out.println("Give me a boolean:");
    boolean bool = scanner.nextBoolean();

    System.out.println("Give me an integer:");
    int integer = scanner.nextInt();

No cast required and you still have to handle the exception either way........

Remember KISS, "Keep It Simple Stupid"...

S73417H
yes you are right, I know that. I just got interested into generics and thought about making this to see if it's possible in Java
thirdy
as with the exception, my idea was to force the caller of my method to always handle exceptions (since console input are error-prone). A little good practice I thought about.
thirdy