What is the best way to cast/convert Map<String, SObject> to Map<Object, Object> and back again?

Looking in to this question, I realized that the type system is inherently broken. They have "fixed" Maps, it seems, but in a way that no longer lets you mix and match some types, especially Object.

The list class is still broken, however:

class c1 {

}
class c2 extends c1 {

}
c1[] s = new c2[0];

Generally speaking, unless/until they fix the problem, you won't be able to be polymorphic this way; your classes that intend to call this class would also need to use Map<Object, Object>, which will involve some casting.

One thought I had would be to create an interface like this:

public interface keyValueMap {
    Set<Object> getKeys();
    Object[] getValues();
    Object getKey(Object value);
    Object setKey(Object key, Object value);
}

And then create specific wrappers:

public class StringSObjectMap implements KeyValueMap {
    Map<String, SObject> values;
    public StringSObjectMap(Map<String, SObject> values) {
        this.values = values;
    }
    public StringSObjectMap() {
        values = new Map<String, SObject>();
    }
    public Set<Object> getKeys() {
        Set<Object> results = new Set<Object>();
        for(String value: values.keySet()) {
            results.add(value);
        }
        return results;
    }
    public Object[] getValues() {
        return (Object[])values.values();
    }
    public Object getKey(Object value) {
        return (Object)values.get((String)value);
    }
    public Object setKey(Object key, Object value) {
        return values.put((String)key, (SObject)value);
    }
}

Note that since we can't cast Set<SObject> to Set<Object>, we have to build a duplicate key set. Make sure you cache the results for performance reasons.

Finally, you can write your method to use the new interface:

public keyValueMap doSomething(keyValueMap inputMap) {

And to call it, you'd pass in the appropriate sub-wrapper:

keyValueMap results = doSomething(new StringSObjectMap(recordMap));

Depending on what you're doing, this may require additional post-processing to get the values back out.

You might also just leave the return type as Map<Object, Object> but you'll end up having to process the keys again, as demonstrated in the wrapper above. One additional step might help you here; you can pass in the type of return value you want:

public keyValueMap doSomething(keyValueMap inputMap, Type mapType) {
  keyValueMap results = (keyValueMap)mapType.newInstance();

Called as:

keyValueMap results = doSomething(new StringSObjectMap(recordMap), StringSObjectMap.class);

You could also create a custom Iterator, but this doesn't work in for-each loops (but you can use iter.next/iter.hasnext):

public class KeyValue {
  public Object key;
  public Object value;
  public KeyValue(Object k, Object v) {
    key = k;
    value = v;
  }
}

public class StringSObjectIter implements Iterable<KeyValue>, Iterator<KeyValue> {
    Map<String, SObject> values;
    String[] keys;
    public StringSObjectIter(Map<String, SObject> values) {
        this.values = values;
    }
    StringSObjectIter(StringSObjectIter copy) {
        values = copy.values;
        keys = new List<String>(values.keySet());
    }
    public Boolean hasNext() {
        return !keys.isEmpty();
    }
    public KeyValue next() {
        String k = keys.remove(0);
        return new KeyValue(k, values.get(k));
    }
    public Iterator<KeyValue> iterator() {
        return new StringSObjectIter(this);
    }
}

Which you can use in your method as:

public keyValueMap doSomething(Iterable<KeyValue> inputs) {
  Iterator<KeyValue> iter = inputs.iterator();
  while(iter.hasNext()) {
    KeyValue value = iter.next();
    ...

Calling method for this:

keyValueMap results = doSomething(new StringSObjectIter(recordMap));

No, there isn't a better way. With Set, you can hack around it to some extent. But with a Map, you care about the values, not just the keys, so you can't do it without looping.

Set<String> values = new Set<String>();
Set<Object> generic = new Set<Object>((List<Object>)new List<String>(values));

It's not immediately clear if this song and dance is even faster than looping anyway.