I personally think we're over-thinking this thing. Keep it simple, folks: public interface Converter<F,T> { T convert(F from); }
You can auto-detect the F/T parameters when a Converter is registered. On Thu, Aug 15, 2013 at 4:42 AM, Jörg Schaible <joerg.schai...@scalaris.com> wrote: > 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 > --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org For additional commands, e-mail: dev-h...@commons.apache.org