开发者

building a generic initializer function in java

I'm trying to come up with a function that lets me pass in any object and a default value, and if the object cannot be casted to the type of the default value, it should return that default value. If the object is null, it should also return the default value.

It should be used like this:

Integer myVar = ConversionHelper.initialize( "123", 0 ) // should return Integer(123)
Integer myVar = ConversionHelper.initialize( "hello", 0 ) // s开发者_Go百科hould return Integer(0)

and so on

I tried with the following code:

public static <T> T initialize(Object o, T def) {
    T ret;

    try {
        ret = (T) o;
    } catch (Exception e) {
        ret = def;
    }

    return ret==null ? def : ret;
}

but it fails with basic conversions, like casting an Integer to a String.

EDIT: took many of the suggestions from the answers, and now I'm looking for a way to do it without the series of if.. elseif... elseif and the try catch block that appears in my answer


This is what you are looking for:

public static <T> T initialize(Object o, T def) {

    if ( o == null ) return def;

    if ( def == null ) throw new NullPointerException("Null def");

    if ( !def.getClass().isAssignableFrom(o.getClass()) ) {
        return def;
    } else {
        return (T) o;
    }

}

However, you cannot use it on primitive types as you mention in your question. You would need to box them into objects to use this method.

EDIT

If null is a valid option then:

public static <T> T initialize(Object o, T def) {

    if ( o == null ) return def;

    if ( def == null ) null;

    if ( !def.getClass().isAssignableFrom(o.getClass()) ) {
        return def;
    } else {
        return (T) o;
    }

}

If the attribute retrieving the returned value cannot hold null, your compiler will tell you so via an error. Else, this code will fly.


Wildcards are only valid as type parameters, not as types. In this case, as you noticed, plain old Object is just as good.

Another valid signature would be:

public static <IN, OUT> OUT initialize(IN o, OUT default) { ... }

But this looks to be unnecessary in your case, since the generic types are unbounded.

As for your strategy of conversion using casting within a try/catch, I'm not sure about it. Someone else should weigh in on that part.

EDIT: as you've discovered, casting is very limited when it comes to all but the simplest conversions of data. You may need to implement mappings to the appropriate parsing method calls depending on the in and out types.

EDIT2: as an example of what you're facing, here is a data conversion method somebody wrote for a project I'm on:

public static <T> T mapValueString(String valueString, Class<T> targetType) {

  if (valueString == null) {
     return null;
  }
  else if(targetType.equals(String.class)) {
     return (T)valueString;
  }
  else if (targetType.equals(Date.class)) {
     return (T)MyDateTime.parseDate(valueString);
  }
  else if (targetType.equals(Timestamp.class)) {
     return (T)MyDateTime.parseTimestamp(valueString);
  }
  else if (targetType.equals(Boolean.class)) {
     String upperVal = valueString.toUpperCase();
     if (upperVal.startsWith("T")) {
        return (T)Boolean.TRUE;
     }
     else if (upperVal.startsWith("F")) {
        return (T)Boolean.FALSE;
     }
     else {
        throw new RuntimeException("Failed to parse value string into Boolean object. String was " + valueString + ".");
     }
  }
  else if (targetType.equals(Integer.class)) {
     Integer i;
     try {
        i = Integer.parseInt(valueString);
     }
     catch (NumberFormatException nfe) {
        throw new RuntimeException("Failed to parse value string into Integer object. String was " + valueString + ".", nfe);
     }
     return (T)i;
  }
  else if (targetType.equals(Long.class)) {
    Long l;
    try {
       l = Long.parseLong(valueString);
    }
    catch (NumberFormatException nfe) {
       throw new RuntimeException("Failed to parse value string into Long object. String was " + valueString + ".", nfe);
    }
    return (T)l;
  }
  else if (targetType.equals(Double.class)) {
     Double d;
     try {
        d = Double.parseDouble(valueString);
     }
     catch (NumberFormatException nfe) {
        throw new RuntimeException("Failed to parse value string into Double object. String was " + valueString + ".", nfe);
     }
     return (T)d;
  }
  else {
     throw new RuntimeException("Unsupported java type " + targetType.getName() + ".");
  }
}

Note that this is only for mapping String to T, where you want to map T1 to T2. Is there a better way? Maybe. Can SO help you find the right strategy? Of course, but the ball's in your court to ask the right questions.

EDIT3: Apache Commons' beanutils.converters might be of help. I haven't tried it out though, so I can't remark on it.


public static <T> T initialize(Object o, T def) {
    T ret;

    try {
        ret = def.getClass().cast(o);
    } catch (Exception e) {
        ret = def;
    }

    return ret==null ? def : ret;
}


Thanks a lot for all the answers, but I faced the following problem

with a couple of basics test, I soon found that the casting capability of types is limited (hey, java newbie around here), and I'm mainly thinking using it for basic types (numbers, strings, dates, boolean, etc)

so I came up with the following

public static <T> T initialize(Object o, T def) {

    if (o==null) return def;

    try {
        if (def.getClass().isAssignableFrom(String.class)) {
            return (T) o.toString();
        } else if (def.getClass().isAssignableFrom(Integer.class)) {
                return (T) Integer.valueOf(o.toString());
        } else if (def.getClass().isAssignableFrom(Long.class)) {
            return (T) Long.valueOf(o.toString());
        } else if (def.getClass().isAssignableFrom(o.getClass())) {
            return (T) o;
        } else {
            return def;
        }
    } catch (Exception e) {
        return def;
    }
}

and I'll guess I'll just have to add many elseifs on the way...

oh, and these are the test I'm passing so far...

    assertEquals("trivial case, should return the value passed",
            "hello",
            initialize("hello", "default salutation"));

    assertEquals("should return the default value",
            "default salutation",
            initialize(null, "default salutation"));

    assertEquals("should convert to the default value type",
            "123",
            initialize(123, "0"));

    assertEquals("should convert to the default value type",
            (Long) 10L, 
            initialize("10", 0L));

    assertEquals("should return default value if it can't convert to the default value type",
            (Long) 0L,
            initialize("hola", 0L));

any better alternative???

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜