diff --git a/src/main/java/com/typesafe/config/ConfigObject.java b/src/main/java/com/typesafe/config/ConfigObject.java
index 21da5ae1..17f3106b 100644
--- a/src/main/java/com/typesafe/config/ConfigObject.java
+++ b/src/main/java/com/typesafe/config/ConfigObject.java
@@ -1,13 +1,13 @@
 package com.typesafe.config;
 
 import java.util.List;
-import java.util.Set;
+import java.util.Map;
 
 /**
  * A ConfigObject is a read-only configuration object, which may have nested
  * child objects. Implementations of ConfigObject should be immutable (at least
  * from the perspective of anyone using this interface).
- *
+ * 
  * The getters all have the same semantics; they throw ConfigException.Missing
  * if the value is entirely unset, and ConfigException.WrongType if you ask for
  * a type that the value can't be converted to. ConfigException.Null is a
@@ -16,14 +16,14 @@ import java.util.Set;
  * path "a.b.c" looks for key c in object b in object a in the root object. (The
  * syntax for paths is the same as in ${} substitution expressions in config
  * files, sometimes double quotes are needed around special characters.)
- *
+ * 
+ * ConfigObject implements the standard Java Map interface, but the mutator
+ * methods all throw UnsupportedOperationException.
+ * 
  * TODO add OrNull variants of all these getters? Or better to avoid convenience
  * API for that?
- *
- * TODO should it implement Map<String, ? extends ConfigValue> with the mutators
- * throwing ?
  */
-public interface ConfigObject extends ConfigValue {
+public interface ConfigObject extends ConfigValue, Map<String, ConfigValue> {
 
     boolean getBoolean(String path);
 
@@ -46,16 +46,14 @@ public interface ConfigObject extends ConfigValue {
     Object getAny(String path);
 
     /**
-     * Gets the value at the path as a ConfigValue.
-     *
-     * TODO conceptually if we want to match a read-only subset of the
-     * Map<String, ? extends ConfigValue> interface, we would need to take a key
-     * instead of a path here and return null instead of throwing an exception.
+     * Gets the value at the given path, unless the value is a null value or
+     * missing, in which case it throws just like the other getters. Use get()
+     * from the Map interface if you want an unprocessed value.
      *
      * @param path
      * @return
      */
-    ConfigValue get(String path);
+    ConfigValue getValue(String path);
 
     /** Get value as a size in bytes (parses special strings like "128M") */
     // rename getSizeInBytes ? clearer. allows a megabyte version
@@ -76,6 +74,7 @@ public interface ConfigObject extends ConfigValue {
      */
     Long getNanoseconds(String path);
 
+    /* TODO should this return an iterator instead? */
     List<? extends ConfigValue> getList(String path);
 
     List<Boolean> getBooleanList(String path);
@@ -100,7 +99,12 @@ public interface ConfigObject extends ConfigValue {
 
     List<Long> getNanosecondsList(String path);
 
-    boolean containsKey(String key);
-
-    Set<String> keySet();
+    /**
+     * Gets a ConfigValue at the given key, or returns null if there is no
+     * value. The returned ConfigValue may have ConfigValueType.NULL or any
+     * other type, and the passed-in key must be a key in this object, rather
+     * than a path expression.
+     */
+    @Override
+    ConfigValue get(Object key);
 }
diff --git a/src/main/java/com/typesafe/config/impl/AbstractConfigObject.java b/src/main/java/com/typesafe/config/impl/AbstractConfigObject.java
index 2799e95f..4c3a1ade 100644
--- a/src/main/java/com/typesafe/config/impl/AbstractConfigObject.java
+++ b/src/main/java/com/typesafe/config/impl/AbstractConfigObject.java
@@ -24,6 +24,9 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
         this.transformer = transformer;
     }
 
+    @Override
+    abstract public Map<String, Object> unwrapped();
+
     /**
      * This looks up the key with no transformation or type conversion of any
      * kind, and returns null if the key is not present.
@@ -244,7 +247,15 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
     }
 
     @Override
-    public ConfigValue get(String path) {
+    public ConfigValue get(Object key) {
+        if (key instanceof String)
+            return peek((String) key);
+        else
+            return null;
+    }
+
+    @Override
+    public ConfigValue getValue(String path) {
         return find(path, null, path);
     }
 
@@ -487,4 +498,30 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
         sb.append(")");
         return sb.toString();
     }
+
+    private static UnsupportedOperationException weAreImmutable(String method) {
+        return new UnsupportedOperationException(
+                "ConfigObject is immutable, you can't call Map.'" + method
+                        + "'");
+    }
+
+    @Override
+    public void clear() {
+        throw weAreImmutable("clear");
+    }
+
+    @Override
+    public ConfigValue put(String arg0, ConfigValue arg1) {
+        throw weAreImmutable("put");
+    }
+
+    @Override
+    public void putAll(Map<? extends String, ? extends ConfigValue> arg0) {
+        throw weAreImmutable("putAll");
+    }
+
+    @Override
+    public ConfigValue remove(Object arg0) {
+        throw weAreImmutable("remove");
+    }
 }
diff --git a/src/main/java/com/typesafe/config/impl/SimpleConfigObject.java b/src/main/java/com/typesafe/config/impl/SimpleConfigObject.java
index 970359d8..4ebea7de 100644
--- a/src/main/java/com/typesafe/config/impl/SimpleConfigObject.java
+++ b/src/main/java/com/typesafe/config/impl/SimpleConfigObject.java
@@ -1,8 +1,11 @@
 package com.typesafe.config.impl;
 
+import java.util.AbstractMap;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -10,6 +13,7 @@ import java.util.Set;
 import com.typesafe.config.ConfigException;
 import com.typesafe.config.ConfigObject;
 import com.typesafe.config.ConfigOrigin;
+import com.typesafe.config.ConfigValue;
 
 class SimpleConfigObject extends AbstractConfigObject {
 
@@ -31,16 +35,16 @@ class SimpleConfigObject extends AbstractConfigObject {
     }
 
     @Override
-    public Object unwrapped() {
+    public Map<String, Object> unwrapped() {
         Map<String, Object> m = new HashMap<String, Object>();
-        for (String k : value.keySet()) {
-            m.put(k, value.get(k).unwrapped());
+        for (Map.Entry<String, AbstractConfigValue> e : value.entrySet()) {
+            m.put(e.getKey(), e.getValue().unwrapped());
         }
         return m;
     }
 
     @Override
-    public boolean containsKey(String key) {
+    public boolean containsKey(Object key) {
         return value.containsKey(key);
     }
 
@@ -104,4 +108,37 @@ class SimpleConfigObject extends AbstractConfigObject {
         // note that "origin" is deliberately NOT part of equality
         return mapHash(this.value);
     }
+
+    @Override
+    public boolean containsValue(Object v) {
+        return value.containsValue(v);
+    }
+
+    @Override
+    public Set<Map.Entry<String, ConfigValue>> entrySet() {
+        // total bloat just to work around lack of type variance
+
+        HashSet<java.util.Map.Entry<String, ConfigValue>> entries = new HashSet<Map.Entry<String, ConfigValue>>();
+        for (Map.Entry<String, AbstractConfigValue> e : value.entrySet()) {
+            entries.add(new AbstractMap.SimpleImmutableEntry<String, ConfigValue>(
+                    e.getKey(), e
+                    .getValue()));
+        }
+        return entries;
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return value.isEmpty();
+    }
+
+    @Override
+    public int size() {
+        return value.size();
+    }
+
+    @Override
+    public Collection<ConfigValue> values() {
+        return new HashSet<ConfigValue>(value.values());
+    }
 }
diff --git a/src/main/java/com/typesafe/config/impl/TransformedConfigObject.java b/src/main/java/com/typesafe/config/impl/TransformedConfigObject.java
index 3cd52e77..d989f7cd 100644
--- a/src/main/java/com/typesafe/config/impl/TransformedConfigObject.java
+++ b/src/main/java/com/typesafe/config/impl/TransformedConfigObject.java
@@ -1,7 +1,11 @@
 package com.typesafe.config.impl;
 
+import java.util.Collection;
+import java.util.Map;
 import java.util.Set;
 
+import com.typesafe.config.ConfigValue;
+
 class TransformedConfigObject extends AbstractConfigObject {
 
     private AbstractConfigObject underlying;
@@ -13,7 +17,7 @@ class TransformedConfigObject extends AbstractConfigObject {
     }
 
     @Override
-    public boolean containsKey(String key) {
+    public boolean containsKey(Object key) {
         return underlying.containsKey(key);
     }
 
@@ -23,7 +27,7 @@ class TransformedConfigObject extends AbstractConfigObject {
     }
 
     @Override
-    public Object unwrapped() {
+    public Map<String, Object> unwrapped() {
         return underlying.unwrapped();
     }
 
@@ -31,4 +35,29 @@ class TransformedConfigObject extends AbstractConfigObject {
     protected AbstractConfigValue peek(String key) {
         return underlying.peek(key);
     }
+
+    @Override
+    public boolean containsValue(Object value) {
+        return underlying.containsValue(value);
+    }
+
+    @Override
+    public Set<java.util.Map.Entry<String, ConfigValue>> entrySet() {
+        return underlying.entrySet();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return underlying.isEmpty();
+    }
+
+    @Override
+    public int size() {
+        return underlying.size();
+    }
+
+    @Override
+    public Collection<ConfigValue> values() {
+        return underlying.values();
+    }
 }
diff --git a/src/test/scala/com/typesafe/config/impl/ConfigTest.scala b/src/test/scala/com/typesafe/config/impl/ConfigTest.scala
index 88b19b23..28d0770b 100644
--- a/src/test/scala/com/typesafe/config/impl/ConfigTest.scala
+++ b/src/test/scala/com/typesafe/config/impl/ConfigTest.scala
@@ -156,8 +156,15 @@ class ConfigTest extends TestUtils {
         assertEquals("null bar 42 baz true 3.14 hi", conf.getString("strings.concatenated"))
         assertEquals(true, conf.getBoolean("booleans.trueAgain"))
         assertEquals(false, conf.getBoolean("booleans.falseAgain"))
-        // FIXME need to add a way to get a null
-        //assertEquals(null, conf.getAny("nulls.null"))
+
+        // to get null we have to use the get() method from Map,
+        // which takes a key and not a path
+        assertEquals(nullValue(), conf.getObject("nulls").get("null"))
+        assertNull(conf.get("notinthefile"))
+
+        // get stuff with getValue
+        assertEquals(intValue(42), conf.getValue("ints.fortyTwo"))
+        assertEquals(stringValue("abcd"), conf.getValue("strings.abcd"))
 
         // get stuff with getAny
         assertEquals(42L, conf.getAny("ints.fortyTwo"))
diff --git a/src/test/scala/com/typesafe/config/impl/ConfigValueTest.scala b/src/test/scala/com/typesafe/config/impl/ConfigValueTest.scala
index 3f3da1bd..7f780beb 100644
--- a/src/test/scala/com/typesafe/config/impl/ConfigValueTest.scala
+++ b/src/test/scala/com/typesafe/config/impl/ConfigValueTest.scala
@@ -4,6 +4,8 @@ import org.junit.Assert._
 import org.junit._
 import com.typesafe.config.ConfigValue
 import java.util.Collections
+import scala.collection.JavaConverters._
+import com.typesafe.config.ConfigObject
 
 class ConfigValueTest extends TestUtils {
 
@@ -90,4 +92,49 @@ class ConfigValueTest extends TestUtils {
         subst("a").toString()
         substInString("b").toString()
     }
+
+    private def unsupported(body: => Unit) {
+        intercept[UnsupportedOperationException] {
+            body
+        }
+    }
+
+    @Test
+    def configObjectUnwraps() {
+        val m = new SimpleConfigObject(fakeOrigin(), null, configMap("a" -> 1, "b" -> 2, "c" -> 3))
+        assertEquals(Map("a" -> 1, "b" -> 2, "c" -> 3), m.unwrapped().asScala)
+    }
+
+    @Test
+    def configObjectImplementsMap() {
+        val m: ConfigObject = new SimpleConfigObject(fakeOrigin(), null, configMap("a" -> 1, "b" -> 2, "c" -> 3))
+
+        assertEquals(intValue(1), m.get("a"))
+        assertEquals(intValue(2), m.get("b"))
+        assertEquals(intValue(3), m.get("c"))
+        assertNull(m.get("d"))
+
+        assertTrue(m.containsKey("a"))
+        assertFalse(m.containsKey("z"))
+
+        assertTrue(m.containsValue(intValue(1)))
+        assertFalse(m.containsValue(intValue(10)))
+
+        assertFalse(m.isEmpty())
+
+        assertEquals(3, m.size())
+
+        val values = Set(intValue(1), intValue(2), intValue(3))
+        assertEquals(values, m.values().asScala.toSet)
+        assertEquals(values, m.entrySet().asScala map { _.getValue() } toSet)
+
+        val keys = Set("a", "b", "c")
+        assertEquals(keys, m.keySet().asScala.toSet)
+        assertEquals(keys, m.entrySet().asScala map { _.getKey() } toSet)
+
+        unsupported { m.clear() }
+        unsupported { m.put("hello", intValue(42)) }
+        unsupported { m.putAll(Collections.emptyMap[String, AbstractConfigValue]()) }
+        unsupported { m.remove("a") }
+    }
 }