6

I want to build a method that can convert a String value to a given Field object data type through Java Reflection.

Here is my code:

String value = ...;

Class<? extends MyObject> clazz = getClazz();

Field f = clazz.getDeclaredField("fieldName");
boolean fieldIsAccessible = f.isAccessible();
if (!fieldIsAccessible) {
   f.setAccessible(true);
}

f.getType().cast(value);

if (!fieldIsAccessible) {
    f.setAccessible(false);
}

When I run this code at firs attempt, I receive this exception java.lang.ClassCastException.

I want to convert value to class java.math.BigDecimal.

What is my code missing ?

EDIT: View the solution I came up with.

10
  • 6
    Strings are not "casted" to other types, they are "parsed". Commented Nov 26, 2012 at 16:43
  • Can't understand what you are really trying to do. Why are you casting String type to the type of Field that you are not sure what it can be? Commented Nov 26, 2012 at 16:43
  • You need to use methods of BigDecimal to parse the string. Commented Nov 26, 2012 at 16:45
  • 1
    @ppeterka.. Don't know why but you would again have to delete that comment, because you again went a little bit rude. lol :) Commented Nov 26, 2012 at 16:55
  • @ppeterka Actually I'm trying to parse a String value into a java class selected from a closed list of java classes from the JDK. I'm building a generic tool. Commented Nov 26, 2012 at 17:14

10 Answers 10

11

You could make this work for classes that have a string constructor like this: f.getType().getConstructor( String.class ).newInstance( value );

Sign up to request clarification or add additional context in comments.

3 Comments

+1 Yep, and for your own classes you can provide a static method valueOf(String) as a factory method. You then check (via reflection, or you could use a marker interface) if that method is present in the field's type and use it to create the instance.
@Axel It appears from a later comment that the classes are "a closed list of java classes from the JDK" so adding static methods or marker interfaces are not possible. Reviewing them for String constructors and providing special case code for any that lack one might be feasible.
Oh, I hadn't seen that. However even in that case the valueOf-approach has some kind of use - it works with enums and AFAIK also with all wrapper types.
3

In Java, there is no universal method for converting a String into an instance of an arbitrary class. Many classes simply don't support such a conversion. And there's no standard interface for those that do support it.

Your best bet is to look for a constructor that accepts a String as its sole argument. Of course, not every class provides such a constructor, and there's no guarantee that the semantics would be what you'd expect.

Comments

3

There is a Github project (MIT Licensed) called type-parser which does converting a string value to the desired data type

Here is the project description from GitHub

This is a light weight library that does nothing but parse a string to a given type. Supports all applicable java classes, such as Integer, File, Enum, Float etc., including generic Collection types/interfaces such as List, Set, Map, arrays and even custom made types. Also possible to register your own parsers.

Comments

1

As maerics said, you cant just cast a String to a data type. Is it possible you mean "how do I parse a BigDecimal from a String", to which the answer is...

fieldName = new BigDecimal(value);

Comments

1

The Class cast method throws the ClassCastException if the object is not null and it not assignable to type T. There are only a few types of variable to which a String reference is assignable, String, Object, Serializable, Comparable, and CharSequence.

Many, but not all, classes have ways of producing an object instance based on a String. In some cases, including BigDecimal, there is a constructor that takes a String representation of the new object's value. You could use the Class getDeclaredConstructor method specifying a single String argument, to get the Constructor object for such a constructor, if there is one. However, there is some risk that you will not get a useful object without e.g. calling some setXXX methods, and this approach is limited to those classes that have the right form of constructor.

You are presumably trying to solve some higher level problem, possibly related to serialization and deserialization. That problem may be much more easily solvable than your current problem.

2 Comments

Actually I'm trying to parse a String value into a java class selected from a closed list of java classes from the JDK.
If all the classes have a String-argument constructor, the method I stated above will work. You may have to special case classes that do not. For example, Character does not have a suitable constructor, but if the String is length 1 you could use the Character(char) constructor on its only character.
1

Perhaps not answering the question how to do convert a String into a java type, as there is no generic way of doing it. But there is a library that can help you with this. See type-parser library. Here's how the above code sniped could look like:

    String value = ...;

    Class<? extends MyObject> clazz = getClazz();

    Field f = clazz.getDeclaredField("fieldName");
    boolean fieldIsAccessible = f.isAccessible();
    if (!fieldIsAccessible) {
       f.setAccessible(true);
    }

    TypeParser parser = TypeParser.newBuilder().build();

    // parse value to whatever type f.getGenericType() returns
    Object o = parser.parseType(value, f.getGenericType());


    if (!fieldIsAccessible) {
        f.setAccessible(false);
    }
}

Comments

1

I also gone through same scenario. Pasting code which i have written after doing some research. Guys please give suggestion if you feel anything wrong.

 private <T extends Object> T convertStringToType(String value,Class type){

    if( type.equals(Double.class)){
        return (T) Double.valueOf(value);
    }
    else if(type.equals(Integer.class)){
        if(value.contains(".")){
            BigDecimal bigDecimal= new BigDecimal(value);
            return (T) (Integer)bigDecimal.intValue();
        }
        return (T) Integer.valueOf(value);
    }
    else{ // add other type which you need
        throw new Exception("Invalid type");
    }
}

1 Comment

You may remove the else keywords to make the code clearer.
1

You can create a small method using Supplier and read the String using Scanner:

    private static <T> T getTokenValue(Supplier<T> supplier) {

       return supplier.get();
    }

    String line = "12.0";

    Scanner scanner = new Scanner(line);
    double value = getTokenValue(scanner::nextDouble);

If we need to get the int, the call will be like,

getTokenValue(scanner::nextInt)

The code is a sample and can be improve using the vavr dependency and Try clause that can throw custom exceptions with more generic code.

Try.ofSupplier(supplier).getOrElseThrow(new RuntimeException("Custiome message")); 

varv has a lot of control logic that can be used to write more generic code.

1 Comment

Can you please add a sample code using vavr ?
0

Here is the solution I came up with:

public static Object parse(String value, Class<?> clazz) throws NotSupportedException {
    String canonClassName = clazz.getCanonicalName();

    if (canonClassName.equalsIgnoreCase("java.math.BigDecimal")) {
        return new BigDecimal(value);
    }

    // Add other supported classes here ...

    throw new NotSupportedException("The class [" + canonClassName  + "] is not supported.");
}

Comments

0

I found my answer in this thread: How to convert String object to Boolean Object? - you can parse strings to booleans using:

Boolean boolean1 = Boolean.valueOf("true");
boolean boolean2 = Boolean.parseBoolean("true");

Comments

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.