Re: Parameterized String Externalization
On Mon, 4 May 2009, Owen Jacobson wrote:
On 2009-04-30 16:33:19 -0400, Mark Space <markspace@sbc.global.net> said:
Owen Jacobson wrote:
(I habitually use positional specifiers -- %1$d, %2$s, etc -- in
format strings to permit localization to re-order the placeholders.
Using non-positional format specifiers means that the placeholders
MUST appear in the same order as the parameters to String.format. I'd
love it if there were named placeholders, as with Python's format
specifiers, but without a syntax for named parameters it'd be
painfully verbose to implement.)
Interesting. How does this help in Python? It seems to me that even with
named parameters, you still have to rejigger the order somehow. This should
be the same as a method call with parameters in a different order, I think.
In python, you have the following construction:
"foo: %(foo)s, bar: %(bar)d" % {'foo': 'hello', 'bar': 667}
which evaluates to "foo: hello, bar: 667". The placeholders can be
reordered however you like; as they contain a name, they're substituted
for only the named entry in the right-hand argument to %.
It is a good feature. I'm often annoyed when other languages don't have
it!
Doing something similar in Java would require a terse Map syntax.
Well, it wouldn't *require* it, but it would be a lot simpler if we had
it.
The closest I've ever come up with,
new HashMap<String, Object> () {{
put ("foo": "hello");
put ("bar": 667);
}}
plays havoc on serialization (if you care about that) and takes up many many
more characters to say almost the same thing, which makes it hard to use
inline in a string-formatting context. The other option would be a method
like format(String, Object...), with a documentation note to the effect that
the varargs parameter is expecting alternating names and values:
format("foo: %(foo)s, bar: %(bar)d", "foo", "hello", "bar" 667);
and that's, well, wrong.
I came up with a similar idea in a different context. Some of our unit
tests involve creating lots of little maps which have strings as both keys
and values, and i thought about writing a method like (untested):
public Map<String, String> map(String... args) {
if ((args.length % 2) != 0) throw new IllegalArgumentException();
Map<String, String> map = new HashMap<String, String>();
for (int i = 0; i < args.length; i += 2) {
map.put(args[i], args[i + 1]);
}
return map;
}
Used like:
machine.frob(map(
"foo", "hello",
"bar", "667"
))
A more flexible version which could take arbitrary key and value types
would be possible, but as you say, not comfortable. I'd want to pass in
class objects for the key and value types, and use them to cast the
arguments, to ensure type safety (or, yes Mark, use a CheckedMap), but
that starts to look painful. Although not that bad:
machine.frob(map(String.class, Integer.class
"foo", 3770,
"bar", 667
))
tom
--
Everything looks kind of OK