Hi, Emmanuel Bourg wrote:
> Le 14/08/2013 17:39, Adrian Crum a écrit : > >> Instead of >> >> int columnInt = record.getValueAsInt(1); >> >> the developer would use >> >> Integer columnInt = Util.convertTo(record.getValue(1), Integer.class); > > +1 for the static method, that would allow the use of a static import > and a very concise syntax like: > > Integer columnInt = to(Integer.class, record.getValue(1)); > > > That being said, [convert] could offer several patterns to perform type > conversion, and the use of proxies could be one of them. I never had a look at [convert], but this proposed syntax reminds me strongly to an own little framework, where I have following methods in an interface to convert strings into arbitrary objects: ================= %< ============== <T> T get(Class<T> type, String key); <T> T get(ValueConverter<T> converter, String key); ================= %< ============== The value converter itself is very primitive: ================= %< ============== public interface ValueConverter<T> { T get(CharSequence value); } ================= %< ============== The question is now, how to know about existing converters. I was inspired by XStream and use a class ConverterLookup that has following method: ================= %< ============== public <T> ValueConverter<T> lookup(final Class<T> type) { ValueConverter<?> converter = converterCache.get(type); if (converter == null) { for (final Iterator<ConverterFactory> iter = converters.iterator(); converter == null && iter.hasNext();) { converter = iter.next().willConvert(type); } synchronized (converterCache) { if (converter != null) { converterCache.putIfAbsent(type, converter); } } } @SuppressWarnings("unchecked") final ValueConverter<T> checkedConverter = (ValueConverter<T>) converter; return checkedConverter; } ================= %< ============== I.e. the ConverterLookup has a (priorized) set of ConverterFactory implementations that can be requested for a ValueConverter of the given type. The ConverterLookup has additionally one static method "getDefaultLookup()" that returns an instance of the ConverterLookup where the set of ConverterFactory implementations is detected and instantiated using the Java SPI mechanism. That makes it very convenient to add new ConverterFactory implementations even to the default ConverterLookup. Therefore the implementation of the two get methods is quite simple: ================= %< ============== @Override public <T> T get(final Class<T> type, final String key) { final ValueConverter<T> converter = ConverterFactory.getDefaultLookup().lookup(type); return get(converter, key); } @Override public <T> T get(final ValueConverter<T> converter, final String key) { final String value = retrieveString(key); // get String to convert return converter.get(value); } ================= %< ============== You may ask, why there is an additional ConverterFactory to create the ValueConverter instances? Actually it can be useful for a converter to implement both interfaces: ================= %< ============== public abstract class AbstractFactoryConverter<T> implements ValueConverter<T>, ConverterFactory { private final Class<? super T> type; protected AbstractFactoryConverter(final Class<? super T> type) { this.type = type; } @Override public int getPriority() { return getClass().getAnnotation(Priority.class).value(); } @Override public ValueConverter<?> willConvert(final Class<?> type) { return this.type == type ? this : null; } } @Priority public class StringConverter extends AbstractFactoryConverter<String> { public StringConverter() { super(String.class); } @Override public String get(final CharSequence value) { return value == null ? null : value.toString(); } } ================= %< ============== However, to handle types in a generic way, the factory provides a much better possibility. See the implementation of my EnumConverterFactory: ================= %< ============== public class EnumConverterFactory implements ConverterFactory { @Override public ValueConverter<?> willConvert(final Class<?> type) { if (Enum.class.isAssignableFrom(type)) { return new ValueConverter<Enum<?>>() { @Override @SuppressWarnings({"rawtypes", "unchecked"}) public Enum<?> get(final CharSequence value) { return Enum.valueOf((Class<Enum>) type, value.toString()); } }; } return null; } @Override public int getPriority() { return Priority.LOW; } } ================= %< ============== Apart from those factories, I have one for all the primitive types, one for arrays and one based on reflection that uses a given type's constructor taking a single String. That allows me to write following code for an instance 'store' that owns the two get methods above: ================= %< ============== int i = store.get(int.class, "42"); Long l = store.get(Long.class, "42"); URL url = store.get(URL.class, "http://www.apache.org/"); Priority p = store.get(Priority.class, "LOW"); // an enum Priority[] pArray = store.get(Priority[].class, "LOW,HIGH"); ValueConverter<URL[].class> converter = new ArrayConverterFactory('|').willConvert(URL[].class); URL[] urlArray = store.get(converter, "http://www.apache.org/|http://commons.apache.org/"); ================= %< ============== The code above is a bit simplified (stripped exception handling), but the complete stuff contains just 2 interfaces, 1 annotation, one exception and 7 classes with not too much code. I always intended to add this to [lang] in a package 'converter', when I learned that we have a [convert] component. Now I am not sure what to do with it ... - Jörg --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org For additional commands, e-mail: dev-h...@commons.apache.org