Automated rollback of commit 1a5123d0

*** Original change description ***

Inline requests for map bindings

Under the hood, remove ContributionBinding.Kind.SYNTHETIC_MAP. Map<K,V> and Map<K, Produced<V>> bindings now act just like Map<K, FrameworkType<V>>, and have dependencies on their contributions directly instead of wrapping a another FrameworkType<Map<K, FrameworkType<V>>>

***

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=166785831
diff --git a/java/dagger/internal/MapBuilder.java b/java/dagger/internal/MapBuilder.java
deleted file mode 100644
index 1560491..0000000
--- a/java/dagger/internal/MapBuilder.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal;
-
-import static dagger.internal.DaggerCollections.newLinkedHashMapWithExpectedSize;
-
-import java.util.Collections;
-import java.util.Map;
-
-/**
- * A fluent builder class that returns a {@link Map}. Used in component implementations where a map
- * must be created in one fluent statement for inlined request fulfillments.
- */
-public final class MapBuilder<K, V> {
-  private final Map<K, V> contributions;
-
-  private MapBuilder(int size) {
-    contributions = newLinkedHashMapWithExpectedSize(size);
-  }
-
-  /**
-   * Creates a new {@link MapBuilder} with {@code size} elements.
-   */
-  public static <K, V> MapBuilder<K, V> newMapBuilder(int size) {
-    return new MapBuilder<>(size);
-  }
-
-  public MapBuilder<K, V> put(K key, V value) {
-    contributions.put(key, value);
-    return this;
-  }
-
-  public Map<K, V> build() {
-    switch (contributions.size()) {
-      case 0:
-        return Collections.emptyMap();
-      default:
-        return Collections.unmodifiableMap(contributions);
-    }
-  }
-}
diff --git a/java/dagger/internal/MapFactory.java b/java/dagger/internal/MapFactory.java
index b6f925a..d4b9189 100644
--- a/java/dagger/internal/MapFactory.java
+++ b/java/dagger/internal/MapFactory.java
@@ -17,11 +17,8 @@
 package dagger.internal;
 
 import static dagger.internal.DaggerCollections.newLinkedHashMapWithExpectedSize;
-import static dagger.internal.Preconditions.checkNotNull;
 import static java.util.Collections.unmodifiableMap;
 
-import java.util.Collections;
-import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Map.Entry;
 import javax.inject.Provider;
@@ -35,31 +32,21 @@
  *
  */
 public final class MapFactory<K, V> implements Factory<Map<K, V>> {
-  private static final Provider<Map<Object, Object>> EMPTY =
-      InstanceFactory.create(Collections.emptyMap());
-
   private final Map<K, Provider<V>> contributingMap;
 
-  /**
-   * Returns a new {@link Builder}
-   */
-  public static <K, V> Builder<K, V> builder(int size) {
-    return new Builder<>(size);
-  }
-
-  /**
-   * Returns a factory of an empty map.
-   */
-  @SuppressWarnings("unchecked") // safe contravariant cast
-  public static <K, V> Provider<Map<K, V>> emptyMapProvider() {
-    return (Provider<Map<K, V>>) (Provider) EMPTY;
-  }
-
   private MapFactory(Map<K, Provider<V>> map) {
     this.contributingMap = unmodifiableMap(map);
   }
 
   /**
+   * Returns a new MapFactory.
+   */
+  public static <K, V> MapFactory<K, V> create(Provider<Map<K, Provider<V>>> mapProviderFactory) {
+    Map<K, Provider<V>> map = mapProviderFactory.get();
+    return new MapFactory<K, V>(map);
+  }
+
+  /**
    * Returns a {@code Map<K, V>} whose iteration order is that of the elements
    * given by each of the providers, which are invoked in the order given at creation.
    */
@@ -71,25 +58,4 @@
     }
     return unmodifiableMap(result);
   }
-
-  // TODO(ronshapiro): can we merge the builders? Or maybe just use a (Immutable)MapBuilder?
-  /** A builder for {@link MapFactory}. */
-  public static final class Builder<K, V> {
-    private final LinkedHashMap<K, Provider<V>> map;
-
-    private Builder(int size) {
-      this.map = newLinkedHashMapWithExpectedSize(size);
-    }
-
-    /** Associates {@code key} with {@code providerOfValue}. */
-    public Builder<K, V> put(K key, Provider<V> providerOfValue) {
-      map.put(checkNotNull(key, "key"), checkNotNull(providerOfValue, "provider"));
-      return this;
-    }
-
-    /** Returns a new {@link MapProviderFactory}. */
-    public MapFactory<K, V> build() {
-      return new MapFactory<>(map);
-    }
-  }
 }
diff --git a/java/dagger/internal/MapProviderFactory.java b/java/dagger/internal/MapProviderFactory.java
index 2458a3c..c27beb2 100644
--- a/java/dagger/internal/MapProviderFactory.java
+++ b/java/dagger/internal/MapProviderFactory.java
@@ -17,10 +17,10 @@
 package dagger.internal;
 
 import static dagger.internal.DaggerCollections.newLinkedHashMapWithExpectedSize;
-import static dagger.internal.Preconditions.checkNotNull;
 import static java.util.Collections.unmodifiableMap;
 
 import dagger.Lazy;
+import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import javax.inject.Provider;
@@ -34,13 +34,24 @@
  */
 public final class MapProviderFactory<K, V>
     implements Factory<Map<K, Provider<V>>>, Lazy<Map<K, Provider<V>>> {
+  private static final MapProviderFactory<Object, Object> EMPTY =
+      new MapProviderFactory<Object, Object>(Collections.<Object, Provider<Object>>emptyMap());
+
   private final Map<K, Provider<V>> contributingMap;
 
   /**
    * Returns a new {@link Builder}
    */
   public static <K, V> Builder<K, V> builder(int size) {
-    return new Builder<>(size);
+    return new Builder<K, V>(size);
+  }
+
+  /**
+   * Returns a factory of an empty map.
+   */
+  @SuppressWarnings("unchecked") // safe contravariant cast
+  public static <K, V> MapProviderFactory<K, V> empty() {
+    return (MapProviderFactory<K, V>) EMPTY;
   }
 
   private MapProviderFactory(Map<K, Provider<V>> contributingMap) {
@@ -57,23 +68,37 @@
     return this.contributingMap;
   }
 
-  /** A builder for {@link MapProviderFactory}. */
+  /**
+   * A builder to help build the {@link MapProviderFactory}
+   */
   public static final class Builder<K, V> {
-    private final LinkedHashMap<K, Provider<V>> map;
+    private final LinkedHashMap<K, Provider<V>> mapBuilder;
 
     private Builder(int size) {
-      this.map = newLinkedHashMapWithExpectedSize(size);
+      // TODO(user): consider which way to initialize mapBuilder is better
+      this.mapBuilder = newLinkedHashMapWithExpectedSize(size);
     }
 
-    /** Associates {@code key} with {@code providerOfValue}. */
-    public Builder<K, V> put(K key, Provider<V> providerOfValue) {
-      map.put(checkNotNull(key, "key"), checkNotNull(providerOfValue, "provider"));
-      return this;
-    }
-
-    /** Returns a new {@link MapProviderFactory}. */
+    /**
+     * Returns a new {@link MapProviderFactory}
+     */
     public MapProviderFactory<K, V> build() {
-      return new MapProviderFactory<>(map);
+      return new MapProviderFactory<K, V>(this.mapBuilder);
+    }
+
+    /**
+     * Associate k with providerOfValue in {@code Builder}
+     */
+    public Builder<K, V> put(K key, Provider<V> providerOfValue) {
+      if (key == null) {
+        throw new NullPointerException("The key is null");
+      }
+      if (providerOfValue == null) {
+        throw new NullPointerException("The provider of the value is null");
+      }
+
+      this.mapBuilder.put(key, providerOfValue);
+      return this;
     }
   }
 }
diff --git a/java/dagger/internal/codegen/BindingExpression.java b/java/dagger/internal/codegen/BindingExpression.java
index 06c8e64..093e5c5 100644
--- a/java/dagger/internal/codegen/BindingExpression.java
+++ b/java/dagger/internal/codegen/BindingExpression.java
@@ -198,9 +198,6 @@
         case SYNTHETIC_MULTIBOUND_SET:
           return new SetBindingExpression(
               provisionBinding, graph, componentBindingExpressions, bindingExpression, elements);
-        case SYNTHETIC_MULTIBOUND_MAP:
-          return new MapBindingExpression(
-              provisionBinding, graph, componentBindingExpressions, bindingExpression, elements);
         case SYNTHETIC_OPTIONAL_BINDING:
           return new OptionalBindingExpression(
               provisionBinding, bindingExpression, componentBindingExpressions);
diff --git a/java/dagger/internal/codegen/BindingGraph.java b/java/dagger/internal/codegen/BindingGraph.java
index 14b9c7d..110d08e 100644
--- a/java/dagger/internal/codegen/BindingGraph.java
+++ b/java/dagger/internal/codegen/BindingGraph.java
@@ -35,6 +35,7 @@
 import com.google.auto.common.MoreTypes;
 import com.google.auto.value.AutoValue;
 import com.google.auto.value.extension.memoized.Memoized;
+import com.google.common.base.VerifyException;
 import com.google.common.collect.FluentIterable;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -518,6 +519,7 @@
 
             ImmutableSet.Builder<Optional<ContributionBinding>> maybeContributionBindings =
                 ImmutableSet.builder();
+            maybeContributionBindings.add(syntheticMapOfValuesBinding(requestKey));
             maybeContributionBindings.add(
                 syntheticMultibinding(
                     requestKey, multibindingContributions, multibindingDeclarations));
@@ -583,17 +585,71 @@
             owningResolver.componentDescriptor.subcomponentsByBuilderType().get(builderType));
       }
 
-      private ImmutableSet<Key> keysMatchingRequest(Key requestKey) {
+      private Iterable<Key> keysMatchingRequest(Key requestKey) {
         ImmutableSet.Builder<Key> keys = ImmutableSet.builder();
         keys.add(requestKey);
         keyFactory.unwrapSetKey(requestKey, Produced.class).ifPresent(keys::add);
         keyFactory.rewrapMapKey(requestKey, Producer.class, Provider.class).ifPresent(keys::add);
         keyFactory.rewrapMapKey(requestKey, Provider.class, Producer.class).ifPresent(keys::add);
-        keys.addAll(keyFactory.implicitFrameworkMapKeys(requestKey));
         return keys.build();
       }
 
       /**
+       * If {@code key} is a {@code Map<K, V>} or {@code Map<K, Produced<V>>}, and there are any
+       * multibinding contributions or declarations that apply to that map, returns a synthetic
+       * binding for the {@code key} that depends on an {@linkplain #syntheticMultibinding(Key,
+       * Iterable, Iterable) underlying synthetic multibinding}.
+       *
+       * <p>The returned binding has the same {@link BindingType} as the underlying synthetic
+       * multibinding.
+       */
+      private Optional<ContributionBinding> syntheticMapOfValuesBinding(final Key key) {
+        return syntheticMultibinding(
+                key,
+                multibindingContributionsForValueMap(key),
+                multibindingDeclarationsForValueMap(key))
+            .map(
+                syntheticMultibinding -> {
+                  switch (syntheticMultibinding.bindingType()) {
+                    case PROVISION:
+                      return provisionBindingFactory.syntheticMapOfValuesBinding(key);
+
+                    case PRODUCTION:
+                      return productionBindingFactory.syntheticMapOfValuesOrProducedBinding(key);
+
+                    default:
+                      throw new VerifyException(syntheticMultibinding.toString());
+                  }
+                });
+      }
+
+      /**
+       * If {@code key} is for {@code Map<K, V>} or {@code Map<K, Produced<V>>}, returns all
+       * multibinding contributions whose key is for {@code Map<K, Provider<V>>} or {@code Map<K,
+       * Producer<V>>} with the same qualifier and {@code K} and {@code V}.
+       */
+      private ImmutableSet<ContributionBinding> multibindingContributionsForValueMap(Key key) {
+        return keyFactory
+            .implicitFrameworkMapKeys(key)
+            .stream()
+            .flatMap(mapKey -> getExplicitMultibindings(mapKey).stream())
+            .collect(toImmutableSet());
+      }
+
+      /**
+       * If {@code key} is for {@code Map<K, V>} or {@code Map<K, Produced<V>>}, returns all
+       * multibinding declarations whose key is for {@code Map<K, Provider<V>>} or {@code Map<K,
+       * Producer<V>>} with the same qualifier and {@code K} and {@code V}.
+       */
+      private ImmutableSet<MultibindingDeclaration> multibindingDeclarationsForValueMap(Key key) {
+        return keyFactory
+            .implicitFrameworkMapKeys(key)
+            .stream()
+            .flatMap(mapKey -> getMultibindingDeclarations(mapKey).stream())
+            .collect(toImmutableSet());
+      }
+
+      /**
        * Returns a synthetic binding that depends on individual multibinding contributions.
        *
        * <p>If there are no {@code multibindingContributions} or {@code multibindingDeclarations},
@@ -1109,9 +1165,7 @@
                   .stream()
                   .map(ContributionBinding::bindingKind)
                   .anyMatch(SYNTHETIC_MULTIBOUND_KINDS::contains)
-              && keysMatchingRequest(resolvedBindings.key())
-                  .stream()
-                  .anyMatch(key -> !getLocalExplicitMultibindings(key).isEmpty());
+              && !getLocalExplicitMultibindings(resolvedBindings.key()).isEmpty();
         }
 
         /**
diff --git a/java/dagger/internal/codegen/BindingGraphValidator.java b/java/dagger/internal/codegen/BindingGraphValidator.java
index 32143d0..1bbcbdc 100644
--- a/java/dagger/internal/codegen/BindingGraphValidator.java
+++ b/java/dagger/internal/codegen/BindingGraphValidator.java
@@ -30,6 +30,7 @@
 import static dagger.internal.codegen.ConfigurationAnnotations.getComponentAnnotation;
 import static dagger.internal.codegen.ConfigurationAnnotations.getComponentDependencies;
 import static dagger.internal.codegen.ContributionBinding.Kind.INJECTION;
+import static dagger.internal.codegen.ContributionBinding.Kind.SYNTHETIC_MAP;
 import static dagger.internal.codegen.ContributionBinding.Kind.SYNTHETIC_MULTIBOUND_KINDS;
 import static dagger.internal.codegen.ContributionBinding.Kind.SYNTHETIC_MULTIBOUND_MAP;
 import static dagger.internal.codegen.ContributionBinding.indexMapBindingsByAnnotationType;
@@ -993,7 +994,8 @@
             .stream()
             .map(ContributionBinding::bindingKind)
             // TODO(dpb): Kill with fire.
-            .anyMatch(SYNTHETIC_MULTIBOUND_KINDS::contains)) {
+            .anyMatch(
+                kind -> SYNTHETIC_MULTIBOUND_KINDS.contains(kind) || SYNTHETIC_MAP.equals(kind))) {
           reportMultipleContributionTypes();
           return;
         }
diff --git a/java/dagger/internal/codegen/ContributionBinding.java b/java/dagger/internal/codegen/ContributionBinding.java
index 6706757..8891e67 100644
--- a/java/dagger/internal/codegen/ContributionBinding.java
+++ b/java/dagger/internal/codegen/ContributionBinding.java
@@ -73,6 +73,12 @@
    */
   enum Kind {
     /**
+     * The synthetic binding for {@code Map<K, V>} that depends on either
+     * {@code Map<K, Provider<V>>} or {@code Map<K, Producer<V>>}.
+     */
+    SYNTHETIC_MAP,
+
+    /**
      * A synthetic binding for a multibound set that depends on the individual multibinding
      * {@link Provides @Provides} or {@link Produces @Produces} methods.
      */
diff --git a/java/dagger/internal/codegen/DependencyRequest.java b/java/dagger/internal/codegen/DependencyRequest.java
index 5a4b4b6..bbd899a 100644
--- a/java/dagger/internal/codegen/DependencyRequest.java
+++ b/java/dagger/internal/codegen/DependencyRequest.java
@@ -344,26 +344,43 @@
      * Creates a synthetic dependency request for one individual {@code multibindingContribution}.
      */
     private DependencyRequest forMultibindingContribution(
-        ContributionBinding multibindingContribution, Kind requestKind) {
+        ContributionBinding multibindingContribution) {
       checkArgument(
           multibindingContribution.key().multibindingContributionIdentifier().isPresent(),
           "multibindingContribution's key must have a multibinding contribution identifier: %s",
           multibindingContribution);
       return DependencyRequest.builder()
-          .kind(requestKind)
+          .kind(multibindingContributionRequestKind(multibindingContribution))
           .key(multibindingContribution.key())
           .build();
     }
 
+    private Kind multibindingContributionRequestKind(ContributionBinding multibindingContribution) {
+      switch (multibindingContribution.contributionType()) {
+        case MAP:
+          return multibindingContribution.bindingType().equals(BindingType.PRODUCTION)
+              ? Kind.PRODUCER
+              : Kind.PROVIDER;
+        case SET:
+        case SET_VALUES:
+          return Kind.INSTANCE;
+        case UNIQUE:
+          throw new IllegalArgumentException(
+              "multibindingContribution must be a multibinding: " + multibindingContribution);
+        default:
+          throw new AssertionError(multibindingContribution.toString());
+      }
+    }
+
     /**
      * Creates synthetic dependency requests for each individual multibinding contribution in {@code
      * multibindingContributions}.
      */
     ImmutableSet<DependencyRequest> forMultibindingContributions(
-        Iterable<ContributionBinding> multibindingContributions, Kind requestKind) {
+        Iterable<ContributionBinding> multibindingContributions) {
       ImmutableSet.Builder<DependencyRequest> requests = ImmutableSet.builder();
       for (ContributionBinding multibindingContribution : multibindingContributions) {
-        requests.add(forMultibindingContribution(multibindingContribution, requestKind));
+        requests.add(forMultibindingContribution(multibindingContribution));
       }
       return requests.build();
     }
diff --git a/java/dagger/internal/codegen/FrameworkFieldInitializer.java b/java/dagger/internal/codegen/FrameworkFieldInitializer.java
index 0b22230..a8b5b16 100644
--- a/java/dagger/internal/codegen/FrameworkFieldInitializer.java
+++ b/java/dagger/internal/codegen/FrameworkFieldInitializer.java
@@ -29,6 +29,7 @@
 import static dagger.internal.codegen.ContributionBinding.Kind.INJECTION;
 import static dagger.internal.codegen.MapKeys.getMapKeyExpression;
 import static dagger.internal.codegen.MoreAnnotationMirrors.getTypeValue;
+import static dagger.internal.codegen.SourceFiles.frameworkMapFactoryClassName;
 import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
 import static dagger.internal.codegen.SourceFiles.mapFactoryClassName;
 import static dagger.internal.codegen.SourceFiles.membersInjectorNameForType;
@@ -47,7 +48,6 @@
 import com.google.auto.common.MoreElements;
 import com.google.auto.common.MoreTypes;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
@@ -328,6 +328,14 @@
               makeParametersCodeBlock(arguments));
         }
 
+      case SYNTHETIC_MAP:
+        FrameworkDependency frameworkDependency = getOnlyElement(binding.frameworkDependencies());
+        return CodeBlock.of(
+            "$T.create($L)",
+            mapFactoryClassName(binding),
+            componentBindingExpressions.getDependencyExpression(
+                frameworkDependency, componentName));
+
       case SYNTHETIC_MULTIBOUND_SET:
         return factoryForSetMultibindingInitialization(binding);
 
@@ -402,27 +410,16 @@
 
     ImmutableList.Builder<CodeBlock> codeBlocks = ImmutableList.builder();
     MapType mapType = MapType.from(binding.key().type());
-    CodeBlock.Builder builderCall = CodeBlock.builder().add("$T.", mapFactoryClassName(binding));
+    CodeBlock.Builder builderCall =
+        CodeBlock.builder().add("$T.", frameworkMapFactoryClassName(binding.bindingType()));
     boolean useRawTypes = useRawType();
     if (!useRawTypes) {
-      // TODO(ronshapiro): either inline this into mapFactoryClassName, or add a
-      // mapType.unwrappedValueType() method that doesn't require a framework type
-      TypeMirror valueType = mapType.valueType();
-      for (Class<?> frameworkClass :
-          ImmutableSet.of(Provider.class, Producer.class, Produced.class)) {
-        if (mapType.valuesAreTypeOf(frameworkClass)) {
-          valueType = mapType.unwrappedValueType(frameworkClass);
-          break;
-        }
-      }
-      builderCall.add("<$T, $T>", mapType.keyType(), valueType);
+      builderCall.add(
+          "<$T, $T>",
+          mapType.keyType(),
+          mapType.unwrappedValueType(binding.bindingType().frameworkClass()));
     }
-
-    if (binding.bindingType().equals(BindingType.PROVISION)) {
-      builderCall.add("builder($L)", frameworkDependencies.size());
-    } else {
-      builderCall.add("builder()");
-    }
+    builderCall.add("builder($L)", frameworkDependencies.size());
     codeBlocks.add(builderCall.build());
 
     for (FrameworkDependency frameworkDependency : frameworkDependencies) {
diff --git a/java/dagger/internal/codegen/Key.java b/java/dagger/internal/codegen/Key.java
index 4871e4b..3c4cecc 100644
--- a/java/dagger/internal/codegen/Key.java
+++ b/java/dagger/internal/codegen/Key.java
@@ -546,15 +546,12 @@
     }
 
     /**
-     * Optionally extract a {@link Key} for the underlying provision binding(s) if such a valid key
-     * can be inferred from the given key. Specifically, if the key represents a {@link Map}{@code
-     * <K, V>} or {@code Map<K, Producer<V>>}, a key of {@code Map<K, Provider<V>>} will be
-     * returned.
+     * Optionally extract a {@link Key} for the underlying provision binding(s) if such a
+     * valid key can be inferred from the given key.  Specifically, if the key represents a
+     * {@link Map}{@code <K, V>}, a key of {@code Map<K, Provider<V>>} will be returned.
      */
     Optional<Key> implicitMapProviderKeyFrom(Key possibleMapKey) {
-      return firstPresent(
-          rewrapMapKey(possibleMapKey, Produced.class, Provider.class),
-          wrapMapKey(possibleMapKey, Provider.class));
+      return wrapMapKey(possibleMapKey, Provider.class);
     }
 
     /**
diff --git a/java/dagger/internal/codegen/MapBindingExpression.java b/java/dagger/internal/codegen/MapBindingExpression.java
deleted file mode 100644
index 8e5746c..0000000
--- a/java/dagger/internal/codegen/MapBindingExpression.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
-import static dagger.internal.codegen.CodeBlocks.toParametersCodeBlock;
-import static dagger.internal.codegen.ContributionBinding.Kind.SYNTHETIC_MULTIBOUND_MAP;
-import static dagger.internal.codegen.MapKeys.getMapKeyExpression;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.CodeBlock;
-import dagger.internal.MapBuilder;
-import java.util.Collections;
-import java.util.Map;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.Elements;
-
-/** A {@link BindingExpression} for multibound maps. */
-final class MapBindingExpression extends SimpleInvocationBindingExpression {
-  /** Maximum number of key-value pairs that can be passed to ImmutableMap.of(K, V, K, V, ...). */
-  private static final int MAX_IMMUTABLE_MAP_OF_KEY_VALUE_PAIRS = 5;
-
-  private final ProvisionBinding binding;
-  private final ImmutableMap<DependencyRequest, ContributionBinding> dependencies;
-  private final ComponentBindingExpressions componentBindingExpressions;
-  private final Elements elements;
-
-  MapBindingExpression(
-      ProvisionBinding binding,
-      BindingGraph graph,
-      ComponentBindingExpressions componentBindingExpressions,
-      BindingExpression delegate,
-      Elements elements) {
-    super(delegate);
-    ContributionBinding.Kind bindingKind = binding.bindingKind();
-    checkArgument(bindingKind.equals(SYNTHETIC_MULTIBOUND_MAP), bindingKind);
-    this.binding = binding;
-    this.componentBindingExpressions = componentBindingExpressions;
-    this.elements = elements;
-    this.dependencies =
-        Maps.toMap(
-            binding.dependencies(),
-            dep -> graph.resolvedBindings().get(dep.bindingKey()).contributionBinding());
-  }
-
-  @Override
-  CodeBlock getInstanceDependencyExpression(
-      DependencyRequest.Kind requestKind, ClassName requestingClass) {
-    // TODO(ronshapiro): We should also make an ImmutableMap version of MapFactory
-    boolean isImmutableMapAvailable = isImmutableMapAvailable();
-    // TODO(ronshapiro, gak): Use Maps.immutableEnumMap() if it's available?
-    if (isImmutableMapAvailable && dependencies.size() <= MAX_IMMUTABLE_MAP_OF_KEY_VALUE_PAIRS) {
-      return CodeBlock.builder()
-          .add("$T.", ImmutableMap.class)
-          .add(maybeTypeParameters(requestingClass))
-          .add(
-              "of($L)",
-              dependencies
-                  .keySet()
-                  .stream()
-                  .map(dependency -> keyAndValueExpression(dependency, requestingClass))
-                  .collect(toParametersCodeBlock()))
-          .build();
-    }
-    switch (dependencies.size()) {
-      case 0:
-        return collectionsStaticFactoryInvocation(requestingClass, CodeBlock.of("emptyMap()"));
-      case 1:
-        return collectionsStaticFactoryInvocation(
-            requestingClass,
-            CodeBlock.of(
-                "singletonMap($L)",
-                keyAndValueExpression(getOnlyElement(dependencies.keySet()), requestingClass)));
-      default:
-        CodeBlock.Builder instantiation = CodeBlock.builder();
-        instantiation
-            .add("$T.", isImmutableMapAvailable ? ImmutableMap.class : MapBuilder.class)
-            .add(maybeTypeParameters(requestingClass));
-        if (isImmutableMapAvailable) {
-          // TODO(ronshapiro): builderWithExpectedSize
-          instantiation.add("builder()");
-        } else {
-          instantiation.add("newMapBuilder($L)", dependencies.size());
-        }
-        for (DependencyRequest dependency : dependencies.keySet()) {
-          instantiation.add(".put($L)", keyAndValueExpression(dependency, requestingClass));
-        }
-        return instantiation.add(".build()").build();
-    }
-  }
-
-  private CodeBlock keyAndValueExpression(DependencyRequest dependency, ClassName requestingClass) {
-    return CodeBlock.of(
-        "$L, $L",
-        getMapKeyExpression(dependencies.get(dependency).mapKey().get()),
-        componentBindingExpressions.getDependencyExpression(dependency, requestingClass));
-  }
-
-  private CodeBlock collectionsStaticFactoryInvocation(
-      ClassName requestingClass, CodeBlock methodInvocation) {
-    return CodeBlock.builder()
-        .add("$T.", Collections.class)
-        .add(maybeTypeParameters(requestingClass))
-        .add(methodInvocation)
-        .build();
-  }
-
-  private CodeBlock maybeTypeParameters(ClassName requestingClass) {
-    TypeMirror bindingKeyType = binding.key().type();
-    MapType mapType = MapType.from(binding.key());
-    return isTypeAccessibleFrom(bindingKeyType, requestingClass.packageName())
-        ? CodeBlock.of("<$T, $T>", mapType.keyType(), mapType.valueType())
-        : CodeBlock.of("");
-  }
-
-  private boolean isImmutableMapAvailable() {
-    return elements.getTypeElement(ImmutableMap.class.getCanonicalName()) != null;
-  }
-
-  @Override
-  protected CodeBlock explicitTypeParameter(ClassName requestingClass) {
-    if (isImmutableMapAvailable()) {
-      TypeMirror keyType = binding.key().type();
-      return CodeBlock.of(
-          "<$T>",
-          isTypeAccessibleFrom(keyType, requestingClass.packageName()) ? keyType : Map.class);
-    }
-    return CodeBlock.of("");
-  }
-}
diff --git a/java/dagger/internal/codegen/MemberSelect.java b/java/dagger/internal/codegen/MemberSelect.java
index fc0217a..5466292 100644
--- a/java/dagger/internal/codegen/MemberSelect.java
+++ b/java/dagger/internal/codegen/MemberSelect.java
@@ -20,19 +20,17 @@
 import static dagger.internal.codegen.Accessibility.isTypeAccessibleFrom;
 import static dagger.internal.codegen.CodeBlocks.toTypeNamesCodeBlock;
 import static dagger.internal.codegen.ContributionBinding.FactoryCreationStrategy.SINGLETON_INSTANCE;
+import static dagger.internal.codegen.ContributionBinding.Kind.INJECTION;
+import static dagger.internal.codegen.ContributionBinding.Kind.PROVISION;
 import static dagger.internal.codegen.SourceFiles.bindingTypeElementTypeVariableNames;
+import static dagger.internal.codegen.SourceFiles.frameworkMapFactoryClassName;
 import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
 import static dagger.internal.codegen.SourceFiles.setFactoryClassName;
 import static dagger.internal.codegen.TypeNames.FACTORY;
-import static dagger.internal.codegen.TypeNames.MAP_FACTORY;
 import static dagger.internal.codegen.TypeNames.MEMBERS_INJECTOR;
 import static dagger.internal.codegen.TypeNames.MEMBERS_INJECTORS;
-import static dagger.internal.codegen.TypeNames.PRODUCER;
-import static dagger.internal.codegen.TypeNames.PRODUCERS;
-import static dagger.internal.codegen.TypeNames.PROVIDER;
 import static javax.lang.model.type.TypeKind.DECLARED;
 
-import com.google.auto.common.MoreTypes;
 import com.google.common.collect.ImmutableList;
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.CodeBlock;
@@ -90,7 +88,7 @@
             && !contributionBinding.scope().isPresent()) {
           switch (contributionBinding.bindingKind()) {
             case SYNTHETIC_MULTIBOUND_MAP:
-              return Optional.of(emptyMapFactory(contributionBinding));
+              return Optional.of(emptyFrameworkMapFactory(contributionBinding));
 
             case SYNTHETIC_MULTIBOUND_SET:
               return Optional.of(emptySetFactory(contributionBinding));
@@ -168,19 +166,20 @@
         MEMBERS_INJECTOR);
   }
 
-  /** A {@link MemberSelect} for a factory of an empty map. */
-  private static MemberSelect emptyMapFactory(ContributionBinding contributionBinding) {
+  /**
+   * A {@link MemberSelect} for a factory of an empty map of factory types, where a factory can be
+   * either a {@link javax.inject.Provider} or {@link dagger.producers.Producer}.
+   */
+  private static MemberSelect emptyFrameworkMapFactory(ContributionBinding contributionBinding) {
     BindingType bindingType = contributionBinding.bindingType();
-    ImmutableList<TypeMirror> typeParameters =
-        ImmutableList.copyOf(
-            MoreTypes.asDeclared(contributionBinding.key().type()).getTypeArguments());
-    if (bindingType.equals(BindingType.PRODUCTION)) {
-      return new ParameterizedStaticMethod(
-          PRODUCERS, typeParameters, CodeBlock.of("emptyMapProducer()"), PRODUCER);
-    } else {
-      return new ParameterizedStaticMethod(
-          MAP_FACTORY, typeParameters, CodeBlock.of("emptyMapProvider()"), PROVIDER);
-    }
+    MapType mapType = MapType.from(contributionBinding.key());
+
+    return new ParameterizedStaticMethod(
+        frameworkMapFactoryClassName(bindingType),
+        ImmutableList.of(
+            mapType.keyType(), mapType.unwrappedValueType(bindingType.frameworkClass())),
+        CodeBlock.of("empty()"),
+        ClassName.get(bindingType.frameworkClass()));
   }
 
   /**
diff --git a/java/dagger/internal/codegen/ProductionBinding.java b/java/dagger/internal/codegen/ProductionBinding.java
index 91eb94d..4a71dea 100644
--- a/java/dagger/internal/codegen/ProductionBinding.java
+++ b/java/dagger/internal/codegen/ProductionBinding.java
@@ -18,7 +18,6 @@
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
-import static dagger.internal.codegen.DependencyRequest.Kind.PRODUCER;
 import static dagger.internal.codegen.MapKeys.getMapKey;
 import static dagger.internal.codegen.MoreAnnotationMirrors.wrapOptionalInEquivalence;
 import static dagger.internal.codegen.Util.toImmutableSet;
@@ -180,6 +179,28 @@
     }
 
     /**
+     * A synthetic binding of {@code Map<K, V>} or {@code Map<K, Produced<V>>} that depends on
+     * {@code Map<K, Producer<V>>}.
+     */
+    ProductionBinding syntheticMapOfValuesOrProducedBinding(Key mapOfValuesOrProducedKey) {
+      checkNotNull(mapOfValuesOrProducedKey);
+      Optional<Key> mapOfProducersKey =
+          keyFactory.implicitMapProducerKeyFrom(mapOfValuesOrProducedKey);
+      checkArgument(
+          mapOfProducersKey.isPresent(),
+          "%s is not a key for of Map<K, V> or Map<K, Produced<V>>",
+          mapOfValuesOrProducedKey);
+      DependencyRequest requestForMapOfProducers =
+          dependencyRequestFactory.producerForImplicitMapBinding(mapOfProducersKey.get());
+      return ProductionBinding.builder()
+          .contributionType(ContributionType.UNIQUE)
+          .key(mapOfValuesOrProducedKey)
+          .explicitDependencies(requestForMapOfProducers)
+          .bindingKind(Kind.SYNTHETIC_MAP)
+          .build();
+    }
+
+    /**
      * A synthetic binding that depends explicitly on a set of individual provision or production
      * multibinding contribution methods.
      *
@@ -191,8 +212,7 @@
           .contributionType(ContributionType.UNIQUE)
           .key(key)
           .explicitDependencies(
-              dependencyRequestFactory.forMultibindingContributions(
-                  multibindingContributions, PRODUCER))
+              dependencyRequestFactory.forMultibindingContributions(multibindingContributions))
           .bindingKind(Kind.forMultibindingKey(key))
           .build();
     }
diff --git a/java/dagger/internal/codegen/ProvisionBinding.java b/java/dagger/internal/codegen/ProvisionBinding.java
index 6774255..6d40e97 100644
--- a/java/dagger/internal/codegen/ProvisionBinding.java
+++ b/java/dagger/internal/codegen/ProvisionBinding.java
@@ -235,6 +235,21 @@
           .build();
     }
 
+    /** A synthetic binding of {@code Map<K, V>} that depends on {@code Map<K, Provider<V>>}. */
+    ProvisionBinding syntheticMapOfValuesBinding(Key mapOfValuesKey) {
+      checkNotNull(mapOfValuesKey);
+      Optional<Key> mapOfProvidersKey = keyFactory.implicitMapProviderKeyFrom(mapOfValuesKey);
+      checkArgument(mapOfProvidersKey.isPresent(), "%s is not a key for Map<K, V>", mapOfValuesKey);
+      DependencyRequest requestForMapOfProviders =
+          dependencyRequestFactory.providerForImplicitMapBinding(mapOfProvidersKey.get());
+      return ProvisionBinding.builder()
+          .contributionType(ContributionType.UNIQUE)
+          .key(mapOfValuesKey)
+          .provisionDependencies(requestForMapOfProviders)
+          .bindingKind(Kind.SYNTHETIC_MAP)
+          .build();
+    }
+
     /**
      * A synthetic binding that depends explicitly on a set of individual provision multibinding
      * contribution methods.
@@ -243,16 +258,11 @@
      */
     ProvisionBinding syntheticMultibinding(
         Key key, Iterable<ContributionBinding> multibindingContributions) {
-      DependencyRequest.Kind dependencyKind =
-          MapType.isMap(key) && MapType.from(key).valuesAreTypeOf(Provider.class)
-              ? DependencyRequest.Kind.PROVIDER
-              : DependencyRequest.Kind.INSTANCE;
       return ProvisionBinding.builder()
           .contributionType(ContributionType.UNIQUE)
           .key(key)
           .provisionDependencies(
-              dependencyRequestFactory.forMultibindingContributions(
-                  multibindingContributions, dependencyKind))
+              dependencyRequestFactory.forMultibindingContributions(multibindingContributions))
           .bindingKind(Kind.forMultibindingKey(key))
           .build();
     }
diff --git a/java/dagger/internal/codegen/SourceFiles.java b/java/dagger/internal/codegen/SourceFiles.java
index 858f184..ded5f6f 100644
--- a/java/dagger/internal/codegen/SourceFiles.java
+++ b/java/dagger/internal/codegen/SourceFiles.java
@@ -19,10 +19,8 @@
 import static com.google.common.base.CaseFormat.LOWER_CAMEL;
 import static com.google.common.base.CaseFormat.UPPER_CAMEL;
 import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.base.Verify.verify;
 import static dagger.internal.codegen.ContributionBinding.Kind.INJECTION;
-import static dagger.internal.codegen.ContributionBinding.Kind.SYNTHETIC_MULTIBOUND_MAP;
 import static dagger.internal.codegen.ContributionBinding.Kind.SYNTHETIC_MULTIBOUND_SET;
 import static dagger.internal.codegen.Optionals.optionalComparator;
 import static dagger.internal.codegen.TypeNames.DOUBLE_CHECK;
@@ -53,15 +51,17 @@
 import com.squareup.javapoet.ParameterizedTypeName;
 import com.squareup.javapoet.TypeName;
 import com.squareup.javapoet.TypeVariableName;
+import dagger.internal.MapFactory;
+import dagger.internal.MapProviderFactory;
 import dagger.internal.SetFactory;
 import dagger.producers.Produced;
-import dagger.producers.Producer;
+import dagger.producers.internal.MapOfProducerProducer;
+import dagger.producers.internal.MapProducer;
 import dagger.producers.internal.SetOfProducedProducer;
 import dagger.producers.internal.SetProducer;
 import java.util.Comparator;
 import java.util.Iterator;
 import java.util.List;
-import javax.inject.Provider;
 import javax.lang.model.SourceVersion;
 import javax.lang.model.element.Element;
 import javax.lang.model.element.TypeElement;
@@ -264,24 +264,41 @@
     }
   }
 
-  /** The {@link java.util.Map} factory class name appropriate for map bindings. */
+  /**
+   * The {@link java.util.Map}-of-value factory class name appropriate for map bindings.
+   *
+   * <ul>
+   * <li>{@link MapFactory} for provision bindings.
+   * <li>{@link MapProducer} for production bindings.
+   * </ul>
+   */
   static ClassName mapFactoryClassName(ContributionBinding binding) {
-    checkState(binding.bindingKind().equals(SYNTHETIC_MULTIBOUND_MAP), binding.bindingKind());
-    MapType mapType = MapType.from(binding.key());
     switch (binding.bindingType()) {
-      case PROVISION:
-        return mapType.valuesAreTypeOf(Provider.class) ? MAP_PROVIDER_FACTORY : MAP_FACTORY;
       case PRODUCTION:
-        return mapType.valuesAreFrameworkType()
-            ? mapType.valuesAreTypeOf(Producer.class)
-                ? MAP_OF_PRODUCER_PRODUCER
-                : MAP_OF_PRODUCED_PRODUCER
-            : MAP_PRODUCER;
+        return MapType.from(binding.key()).valuesAreTypeOf(Produced.class)
+            ? MAP_OF_PRODUCED_PRODUCER : MAP_PRODUCER;
+
+      case PROVISION:
+        return MAP_FACTORY;
+
       default:
-        throw new IllegalArgumentException(binding.bindingType().toString());
+        throw new AssertionError(binding.toString());
     }
   }
 
+  /**
+   * The {@link java.util.Map}-of-framework factory class name appropriate for map bindings.
+   *
+   * <ul>
+   * <li>{@link MapProviderFactory} for provision bindings.
+   * <li>{@link MapOfProducerProducer} for production bindings.
+   * </ul>
+   */
+  static ClassName frameworkMapFactoryClassName(BindingType bindingType) {
+    return bindingType.equals(BindingType.PRODUCTION)
+        ? MAP_OF_PRODUCER_PRODUCER : MAP_PROVIDER_FACTORY;
+  }
+
   private static String factoryPrefix(ContributionBinding binding) {
     switch (binding.bindingKind()) {
       case INJECTION:
diff --git a/java/dagger/producers/internal/MapOfProducedProducer.java b/java/dagger/producers/internal/MapOfProducedProducer.java
index 6a50b22..7fe2ce4 100644
--- a/java/dagger/producers/internal/MapOfProducedProducer.java
+++ b/java/dagger/producers/internal/MapOfProducedProducer.java
@@ -16,22 +16,20 @@
 
 package dagger.producers.internal;
 
-import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.util.concurrent.Futures.transform;
 import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
-import static dagger.producers.internal.Producers.producerFromProvider;
 
 import com.google.common.base.Function;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Maps;
+import com.google.common.util.concurrent.AsyncFunction;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import dagger.producers.Produced;
 import dagger.producers.Producer;
 import java.util.List;
 import java.util.Map;
-import javax.inject.Provider;
 
 /**
  * A {@link Producer} implementation used to implement {@link Map} bindings. This producer returns a
@@ -41,22 +39,48 @@
  * @author Jesse Beder
  */
 public final class MapOfProducedProducer<K, V> extends AbstractProducer<Map<K, Produced<V>>> {
-  private final Map<K, Producer<V>> mapOfProducers;
+  private final Producer<Map<K, Producer<V>>> mapProducerProducer;
 
-  private MapOfProducedProducer(Map<K, Producer<V>> mapOfProducers) {
-    this.mapOfProducers = mapOfProducers;
+  private MapOfProducedProducer(Producer<Map<K, Producer<V>>> mapProducerProducer) {
+    this.mapProducerProducer = mapProducerProducer;
+  }
+
+  /**
+   * Returns a producer of {@code Map<K, Produced<V>>}, where the map is derived from the given map
+   * of producers by waiting for those producers' resulting futures. The iteration order mirrors the
+   * order of the input map.
+   *
+   * <p>If any of the delegate producers, or their resulting values, are null, then this producer's
+   * future will succeed and the corresponding {@code Produced<V>} will fail with a
+   * {@link NullPointerException}.
+   *
+   * <p>Canceling this future will attempt to cancel all of the component futures, and if any of the
+   * component futures fails or is canceled, this one is, too.
+   */
+  public static <K, V> MapOfProducedProducer<K, V> create(
+      Producer<Map<K, Producer<V>>> mapProducerProducer) {
+    return new MapOfProducedProducer<K, V>(mapProducerProducer);
   }
 
   @Override
   public ListenableFuture<Map<K, Produced<V>>> compute() {
-    return Futures.transform(
-        Futures.allAsList(
-            Iterables.transform(
-                mapOfProducers.entrySet(), MapOfProducedProducer.<K, V>entryUnwrapper())),
-        new Function<List<Map.Entry<K, Produced<V>>>, Map<K, Produced<V>>>() {
+    return Futures.transformAsync(
+        mapProducerProducer.get(),
+        new AsyncFunction<Map<K, Producer<V>>, Map<K, Produced<V>>>() {
           @Override
-          public Map<K, Produced<V>> apply(List<Map.Entry<K, Produced<V>>> entries) {
-            return ImmutableMap.copyOf(entries);
+          public ListenableFuture<Map<K, Produced<V>>> apply(final Map<K, Producer<V>> map) {
+            // TODO(beder): Use Futures.whenAllComplete when Guava 20 is released.
+            return transform(
+                Futures.allAsList(
+                    Iterables.transform(
+                        map.entrySet(), MapOfProducedProducer.<K, V>entryUnwrapper())),
+                new Function<List<Map.Entry<K, Produced<V>>>, Map<K, Produced<V>>>() {
+                  @Override
+                  public Map<K, Produced<V>> apply(List<Map.Entry<K, Produced<V>>> entries) {
+                    return ImmutableMap.copyOf(entries);
+                  }
+                },
+                directExecutor());
           }
         },
         directExecutor());
@@ -90,35 +114,4 @@
           entryUnwrapper() {
     return (Function) ENTRY_UNWRAPPER;
   }
-
-  /** Returns a new {@link Builder}. */
-  public static <K, V> Builder<K, V> builder() {
-    return new Builder<>();
-  }
-
-  /** A builder for {@link MapOfProducedProducer}. */
-  public static final class Builder<K, V> {
-    private final ImmutableMap.Builder<K, Producer<V>> mapBuilder = ImmutableMap.builder();
-
-    /** Returns a new {@link MapOfProducedProducer}. */
-    public MapOfProducedProducer<K, V> build() {
-      return new MapOfProducedProducer<>(mapBuilder.build());
-    }
-
-    /** Associates {@code key} with {@code producerOfValue}. */
-    public Builder<K, V> put(K key, Producer<V> producerOfValue) {
-      checkNotNull(key, "key");
-      checkNotNull(producerOfValue, "producer of value");
-      mapBuilder.put(key, producerOfValue);
-      return this;
-    }
-
-    /** Associates {@code key} with {@code providerOfValue}. */
-    public Builder<K, V> put(K key, Provider<V> providerOfValue) {
-      checkNotNull(key, "key");
-      checkNotNull(providerOfValue, "provider of value");
-      mapBuilder.put(key, producerFromProvider(providerOfValue));
-      return this;
-    }
-  }
 }
diff --git a/java/dagger/producers/internal/MapOfProducerProducer.java b/java/dagger/producers/internal/MapOfProducerProducer.java
index 282cf86..5eef863 100644
--- a/java/dagger/producers/internal/MapOfProducerProducer.java
+++ b/java/dagger/producers/internal/MapOfProducerProducer.java
@@ -17,6 +17,7 @@
 package dagger.producers.internal;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Maps.newLinkedHashMapWithExpectedSize;
 import static dagger.producers.internal.Producers.producerFromProvider;
 
 import com.google.common.collect.ImmutableMap;
@@ -33,11 +34,20 @@
  * @author Jesse Beder
  */
 public final class MapOfProducerProducer<K, V> extends AbstractProducer<Map<K, Producer<V>>> {
+  private static final MapOfProducerProducer<Object, Object> EMPTY =
+      new MapOfProducerProducer<Object, Object>(ImmutableMap.<Object, Producer<Object>>of());
+
   private final ImmutableMap<K, Producer<V>> contributingMap;
 
   /** Returns a new {@link Builder}. */
-  public static <K, V> Builder<K, V> builder() {
-    return new Builder<>();
+  public static <K, V> Builder<K, V> builder(int size) {
+    return new Builder<K, V>(size);
+  }
+
+  /** Returns a producer of an empty map. */
+  @SuppressWarnings("unchecked") // safe contravariant cast
+  public static <K, V> MapOfProducerProducer<K, V> empty() {
+    return (MapOfProducerProducer<K, V>) EMPTY;
   }
 
   private MapOfProducerProducer(ImmutableMap<K, Producer<V>> contributingMap) {
@@ -49,11 +59,22 @@
     return Futures.<Map<K, Producer<V>>>immediateFuture(contributingMap);
   }
 
-  /** A builder for {@link MapOfProducerProducer} */
+  /**
+   * A builder to help build the {@link MapOfProducerProducer}
+   */
   public static final class Builder<K, V> {
-    private final ImmutableMap.Builder<K, Producer<V>> mapBuilder = ImmutableMap.builder();
+    private final Map<K, Producer<V>> mapBuilder;
 
-    /** Associates {@code key} with {@code producerOfValue}. */
+    private Builder(int size) {
+      this.mapBuilder = newLinkedHashMapWithExpectedSize(size);
+    }
+
+    /** Returns a new {@link MapOfProducerProducer}. */
+    public MapOfProducerProducer<K, V> build() {
+      return new MapOfProducerProducer<K, V>(ImmutableMap.copyOf(mapBuilder));
+    }
+
+    /** Associates key with producerOfValue in {@code Builder}. */
     public Builder<K, V> put(K key, Producer<V> producerOfValue) {
       checkNotNull(key, "key");
       checkNotNull(producerOfValue, "producer of value");
@@ -61,17 +82,12 @@
       return this;
     }
 
-    /** Associates {@code key} with {@code providerOfValue}. */
+    /** Associates key with providerOfValue in {@code Builder}. */
     public Builder<K, V> put(K key, Provider<V> providerOfValue) {
       checkNotNull(key, "key");
       checkNotNull(providerOfValue, "provider of value");
       mapBuilder.put(key, producerFromProvider(providerOfValue));
       return this;
     }
-
-    /** Returns a new {@link MapOfProducerProducer}. */
-    public MapOfProducerProducer<K, V> build() {
-      return new MapOfProducerProducer<>(mapBuilder.build());
-    }
   }
 }
diff --git a/java/dagger/producers/internal/MapProducer.java b/java/dagger/producers/internal/MapProducer.java
index ca1650e..1a13b9d 100644
--- a/java/dagger/producers/internal/MapProducer.java
+++ b/java/dagger/producers/internal/MapProducer.java
@@ -16,21 +16,19 @@
 
 package dagger.producers.internal;
 
-import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.util.concurrent.Futures.transform;
 import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
-import static dagger.producers.internal.Producers.producerFromProvider;
 
 import com.google.common.base.Function;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Maps;
+import com.google.common.util.concurrent.AsyncFunction;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import dagger.producers.Producer;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
-import javax.inject.Provider;
 
 /**
  * A {@link Producer} implementation used to implement {@link Map} bindings. This producer returns a
@@ -39,64 +37,73 @@
  * @author Jesse Beder
  */
 public final class MapProducer<K, V> extends AbstractProducer<Map<K, V>> {
-  private final ImmutableMap<K, Producer<V>> mapOfProducers;
+  private final Producer<Map<K, Producer<V>>> mapProducerProducer;
 
-  private MapProducer(ImmutableMap<K, Producer<V>> mapOfProducers) {
-    this.mapOfProducers = mapOfProducers;
+  private MapProducer(Producer<Map<K, Producer<V>>> mapProducerProducer) {
+    this.mapProducerProducer = mapProducerProducer;
   }
 
-  /** Returns a new {@link Builder}. */
-  public static <K, V> Builder<K, V> builder() {
-    return new Builder<>();
-  }
-
-  /** A builder for {@link MapProducer} */
-  public static final class Builder<K, V> {
-    private final ImmutableMap.Builder<K, Producer<V>> mapBuilder = ImmutableMap.builder();
-
-    /** Associates {@code key} with {@code producerOfValue}. */
-    public Builder<K, V> put(K key, Producer<V> producerOfValue) {
-      checkNotNull(key, "key");
-      checkNotNull(producerOfValue, "producer of value");
-      mapBuilder.put(key, producerOfValue);
-      return this;
-    }
-
-    /** Associates {@code key} with {@code providerOfValue}. */
-    public Builder<K, V> put(K key, Provider<V> providerOfValue) {
-      checkNotNull(key, "key");
-      checkNotNull(providerOfValue, "provider of value");
-      mapBuilder.put(key, producerFromProvider(providerOfValue));
-      return this;
-    }
-
-    /** Returns a new {@link MapProducer}. */
-    public MapProducer<K, V> build() {
-      return new MapProducer<>(mapBuilder.build());
-    }
+  /**
+   * Returns a producer of {@code Map<K, V>}, where the map is derived from the given map of
+   * producers by waiting for those producers' resulting futures. The iteration order mirrors the
+   * order of the input map.
+   *
+   * <p>If any of the delegate producers, or their resulting values, are null, then this producer's
+   * future will fail with a {@link NullPointerException}.
+   *
+   * <p>Canceling this future will attempt to cancel all of the component futures, and if any of the
+   * component futures fails or is canceled, this one is, too.
+   */
+  public static <K, V> MapProducer<K, V> create(Producer<Map<K, Producer<V>>> mapProducerProducer) {
+    return new MapProducer<K, V>(mapProducerProducer);
   }
 
   @Override
-  protected ListenableFuture<Map<K, V>> compute() {
-    final List<ListenableFuture<Map.Entry<K, V>>> listOfEntries = new ArrayList<>();
-    for (final Entry<K, Producer<V>> entry : mapOfProducers.entrySet()) {
-      listOfEntries.add(
-          Futures.transform(entry.getValue().get(), new Function<V, Entry<K, V>>() {
-            @Override
-            public Entry<K, V> apply(V computedValue) {
-              return Maps.immutableEntry(entry.getKey(), computedValue);
-            }
-          }, directExecutor()));
-    }
-
-    return Futures.transform(
-        Futures.allAsList(listOfEntries),
-        new Function<List<Map.Entry<K, V>>, Map<K, V>>() {
+  public ListenableFuture<Map<K, V>> compute() {
+    return Futures.transformAsync(
+        mapProducerProducer.get(),
+        new AsyncFunction<Map<K, Producer<V>>, Map<K, V>>() {
           @Override
-          public Map<K, V> apply(List<Map.Entry<K, V>> entries) {
-            return ImmutableMap.copyOf(entries);
+          public ListenableFuture<Map<K, V>> apply(final Map<K, Producer<V>> map) {
+            // TODO(beder): Use Futures.whenAllComplete when Guava 20 is released.
+            return transform(
+                Futures.allAsList(
+                    Iterables.transform(map.entrySet(), MapProducer.<K, V>entryUnwrapper())),
+                new Function<List<Map.Entry<K, V>>, Map<K, V>>() {
+                  @Override
+                  public Map<K, V> apply(List<Map.Entry<K, V>> entries) {
+                    return ImmutableMap.copyOf(entries);
+                  }
+                },
+                directExecutor());
           }
         },
         directExecutor());
   }
+
+  private static final Function<
+          Map.Entry<Object, Producer<Object>>, ListenableFuture<Map.Entry<Object, Object>>>
+      ENTRY_UNWRAPPER =
+          new Function<
+              Map.Entry<Object, Producer<Object>>, ListenableFuture<Map.Entry<Object, Object>>>() {
+            @Override
+            public ListenableFuture<Map.Entry<Object, Object>> apply(
+                final Map.Entry<Object, Producer<Object>> entry) {
+              return transform(
+                  entry.getValue().get(),
+                  new Function<Object, Map.Entry<Object, Object>>() {
+                    @Override
+                    public Map.Entry<Object, Object> apply(Object value) {
+                      return Maps.immutableEntry(entry.getKey(), value);
+                    }
+                  },
+                  directExecutor());
+            }
+          };
+
+  @SuppressWarnings({"unchecked", "rawtypes"}) // bivariate implementation
+  private static <K, V>
+      Function<Map.Entry<K, Producer<V>>, ListenableFuture<Map.Entry<K, V>>> entryUnwrapper() {
+    return (Function) ENTRY_UNWRAPPER;
+  }
 }
diff --git a/java/dagger/producers/internal/Producers.java b/java/dagger/producers/internal/Producers.java
index ee0bb8f..029bcb5 100644
--- a/java/dagger/producers/internal/Producers.java
+++ b/java/dagger/producers/internal/Producers.java
@@ -22,7 +22,6 @@
 import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
 
 import com.google.common.base.Function;
-import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.util.concurrent.AsyncFunction;
 import com.google.common.util.concurrent.Futures;
@@ -30,7 +29,6 @@
 import dagger.producers.Produced;
 import dagger.producers.Producer;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 import javax.inject.Provider;
 
@@ -153,13 +151,5 @@
     };
   }
 
-  private static final Producer<Map<Object, Object>> EMPTY_MAP_PRODUCER =
-      Producers.<Map<Object, Object>>immediateProducer(ImmutableMap.of());
-
-  @SuppressWarnings("unchecked") // safe contravariant cast
-  public static <K, V> Producer<Map<K, V>> emptyMapProducer() {
-    return (Producer<Map<K, V>>) (Producer) EMPTY_MAP_PRODUCER;
-  }
-
   private Producers() {}
 }
diff --git a/javatests/dagger/functional/LazyMaps.java b/javatests/dagger/functional/LazyMaps.java
deleted file mode 100644
index eeb1368..0000000
--- a/javatests/dagger/functional/LazyMaps.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.functional;
-
-import dagger.Component;
-import dagger.Lazy;
-import dagger.Module;
-import dagger.Provides;
-import dagger.multibindings.IntoMap;
-import dagger.multibindings.StringKey;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-import javax.inject.Provider;
-import javax.inject.Singleton;
-
-/**
- * Bindings that use {@code Lazy<T>} as the value in a multibound map. A regression was uncovered
- * when using {@code MapType.valuesAreFrameworkType()}, which treats {@link Lazy} as a framework
- * type and incorrectly suggested {@link dagger.internal.MapProviderFactory} for a {@code Map<K,
- * Lazy<V>>} instead of a plain {@link dagger.internal.MapFactory}. See b/65084589.
- */
-class LazyMaps {
-  @Module
-  abstract static class TestModule {
-    @Provides
-    @Singleton
-    static AtomicInteger provideAtomicInteger() {
-      return new AtomicInteger();
-    }
-
-    @Provides
-    static String provideString(AtomicInteger atomicInteger) {
-      return "value-" + atomicInteger.incrementAndGet();
-    }
-
-    @Provides
-    @IntoMap
-    @StringKey("key")
-    static Lazy<String> mapContribution(Lazy<String> lazy) {
-      return lazy;
-    }
-
-    /* TODO(b/65118638) Replace once @Binds @IntoMap Lazy<T> methods work properly.
-    @Binds
-    @IntoMap
-    @StringKey("binds-key")
-    abstract Lazy<String> mapContributionAsBinds(Lazy<String> lazy);
-    */
-  }
-
-  @Singleton
-  @Component(modules = TestModule.class)
-  interface TestComponent {
-    Map<String, Lazy<String>> mapOfLazy();
-
-    Map<String, Provider<Lazy<String>>> mapOfProviderOfLazy();
-
-    Provider<Map<String, Lazy<String>>> providerForMapOfLazy();
-
-    Provider<Map<String, Provider<Lazy<String>>>> providerForMapOfProviderOfLazy();
-  }
-}
diff --git a/javatests/dagger/functional/LazyMapsTest.java b/javatests/dagger/functional/LazyMapsTest.java
deleted file mode 100644
index a3e289a..0000000
--- a/javatests/dagger/functional/LazyMapsTest.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.functional;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import dagger.Lazy;
-import dagger.functional.LazyMaps.TestComponent;
-import java.util.Map;
-import javax.inject.Provider;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-/** Tests for {@link LazyMaps}. */
-@RunWith(JUnit4.class)
-public class LazyMapsTest {
-  @Test
-  public void mapOfLazies() {
-    TestComponent component = DaggerLazyMaps_TestComponent.create();
-    Map<String, Lazy<String>> laziesMap = component.mapOfLazy();
-
-    String firstGet = laziesMap.get("key").get();
-    assertThat(firstGet).isEqualTo("value-1");
-    assertThat(firstGet).isSameAs(laziesMap.get("key").get());
-
-    assertThat(component.mapOfLazy().get("key").get()).isEqualTo("value-2");
-  }
-
-  @Test
-  public void mapOfProviderOfLaziesReturnsDifferentLazy() {
-    TestComponent component = DaggerLazyMaps_TestComponent.create();
-    Map<String, Provider<Lazy<String>>> providersOfLaziesMap = component.mapOfProviderOfLazy();
-
-    assertThat(providersOfLaziesMap.get("key").get().get())
-        .isNotEqualTo(providersOfLaziesMap.get("key").get().get());
-  }
-}
diff --git a/javatests/dagger/internal/codegen/Compilers.java b/javatests/dagger/internal/codegen/Compilers.java
index 3eee251..a42d1a2 100644
--- a/javatests/dagger/internal/codegen/Compilers.java
+++ b/javatests/dagger/internal/codegen/Compilers.java
@@ -16,12 +16,8 @@
 
 package dagger.internal.codegen;
 
-import static com.google.common.base.StandardSystemProperty.JAVA_CLASS_PATH;
-import static com.google.common.base.StandardSystemProperty.PATH_SEPARATOR;
 import static com.google.testing.compile.Compiler.javac;
-import static java.util.stream.Collectors.joining;
 
-import com.google.common.base.Splitter;
 import com.google.testing.compile.Compiler;
 
 /** {@link Compiler} instances for testing Dagger. */
@@ -31,18 +27,4 @@
   static Compiler daggerCompiler() {
     return javac().withProcessors(new ComponentProcessor());
   }
-
-  static Compiler daggerCompilerWithoutGuava() {
-    return daggerCompiler().withOptions("-classpath", classpathWithoutGuava());
-  }
-
-  private static final String GUAVA = "guava";
-
-  private static String classpathWithoutGuava() {
-    return Splitter.on(PATH_SEPARATOR.value())
-        .splitToList(JAVA_CLASS_PATH.value())
-        .stream()
-        .filter(jar -> !jar.contains(GUAVA))
-        .collect(joining(PATH_SEPARATOR.value()));
-  }
 }
diff --git a/javatests/dagger/internal/codegen/MapBindingComponentProcessorTest.java b/javatests/dagger/internal/codegen/MapBindingComponentProcessorTest.java
index cc22bae..6aba380 100644
--- a/javatests/dagger/internal/codegen/MapBindingComponentProcessorTest.java
+++ b/javatests/dagger/internal/codegen/MapBindingComponentProcessorTest.java
@@ -107,7 +107,7 @@
         "",
         "@Component(modules = {MapModuleOne.class, MapModuleTwo.class})",
         "interface TestComponent {",
-        "  Provider<Map<PathEnum, Provider<Handler>>> dispatcher();",
+        "  Map<PathEnum, Provider<Handler>> dispatcher();",
         "}");
     JavaFileObject generatedComponent =
         JavaFileObjects.forSourceLines(
@@ -153,8 +153,8 @@
             "  }",
             "",
             "  @Override",
-            "  public Provider<Map<PathEnum, Provider<Handler>>> dispatcher() {",
-            "    return mapOfPathEnumAndProviderOfHandlerProvider;",
+            "  public Map<PathEnum, Provider<Handler>> dispatcher() {",
+            "    return mapOfPathEnumAndProviderOfHandlerProvider.get();",
             "  }",
             "",
             "  public static final class Builder {",
@@ -259,7 +259,7 @@
         "",
         "@Component(modules = {MapModuleOne.class, MapModuleTwo.class})",
         "interface TestComponent {",
-        "  Provider<Map<String, Provider<Handler>>> dispatcher();",
+        "  Map<String, Provider<Handler>> dispatcher();",
         "}");
     JavaFileObject generatedComponent =
         JavaFileObjects.forSourceLines(
@@ -305,8 +305,8 @@
             "  }",
             "",
             "  @Override",
-            "  public Provider<Map<String, Provider<Handler>>> dispatcher() {",
-            "    return mapOfStringAndProviderOfHandlerProvider;",
+            "  public Map<String, Provider<Handler>> dispatcher() {",
+            "    return mapOfStringAndProviderOfHandlerProvider.get();",
             "  }",
             "",
             "  public static final class Builder {",
@@ -420,7 +420,7 @@
         "",
         "@Component(modules = {MapModuleOne.class, MapModuleTwo.class})",
         "interface TestComponent {",
-        "  Provider<Map<WrappedClassKey, Provider<Handler>>> dispatcher();",
+        "  Map<WrappedClassKey, Provider<Handler>> dispatcher();",
         "}");
     JavaFileObject generatedComponent =
         JavaFileObjects.forSourceLines(
@@ -468,8 +468,8 @@
             "  }",
             "",
             "  @Override",
-            "  public Provider<Map<WrappedClassKey, Provider<Handler>>> dispatcher() {",
-            "    return mapOfWrappedClassKeyAndProviderOfHandlerProvider;",
+            "  public Map<WrappedClassKey, Provider<Handler>> dispatcher() {",
+            "    return mapOfWrappedClassKeyAndProviderOfHandlerProvider.get();",
             "  }",
             "",
             "  public static final class Builder {",
@@ -585,7 +585,7 @@
         "",
         "@Component(modules = {MapModuleOne.class, MapModuleTwo.class})",
         "interface TestComponent {",
-        "  Provider<Map<PathEnum, Handler>> dispatcher();",
+        "  Map<PathEnum, Handler> dispatcher();",
         "}");
     JavaFileObject generatedComponent =
         JavaFileObjects.forSourceLines(
@@ -593,6 +593,7 @@
             "package test;",
             "",
             "import dagger.internal.MapFactory;",
+            "import dagger.internal.MapProviderFactory;",
             "import dagger.internal.Preconditions;",
             "import java.util.Map;",
             "import javax.annotation.Generated;",
@@ -602,6 +603,8 @@
             "public final class DaggerTestComponent implements TestComponent {",
             "  private Provider<Handler> provideAdminHandlerProvider;",
             "  private Provider<Handler> provideLoginHandlerProvider;",
+            "  private Provider<Map<PathEnum, Provider<Handler>>>",
+            "      mapOfPathEnumAndProviderOfHandlerProvider;",
             "  private Provider<Map<PathEnum, Handler>> mapOfPathEnumAndHandlerProvider;",
             "",
             "  private DaggerTestComponent(Builder builder) {",
@@ -622,16 +625,18 @@
             "        MapModuleOne_ProvideAdminHandlerFactory.create(builder.mapModuleOne);",
             "    this.provideLoginHandlerProvider =",
             "        MapModuleTwo_ProvideLoginHandlerFactory.create(builder.mapModuleTwo);",
-            "    this.mapOfPathEnumAndHandlerProvider =",
-            "        MapFactory.<PathEnum, Handler>builder(2)",
+            "    this.mapOfPathEnumAndProviderOfHandlerProvider =",
+            "        MapProviderFactory.<PathEnum, Handler>builder(2)",
             "            .put(PathEnum.ADMIN, provideAdminHandlerProvider)",
             "            .put(PathEnum.LOGIN, provideLoginHandlerProvider)",
             "            .build();",
+            "    this.mapOfPathEnumAndHandlerProvider =",
+            "        MapFactory.create(mapOfPathEnumAndProviderOfHandlerProvider);",
             "  }",
             "",
             "  @Override",
-            "  public Provider<Map<PathEnum, Handler>> dispatcher() {",
-            "    return mapOfPathEnumAndHandlerProvider;",
+            "  public Map<PathEnum, Handler> dispatcher() {",
+            "    return mapOfPathEnumAndHandlerProvider.get();",
             "  }",
             "",
             "  public static final class Builder {",
diff --git a/javatests/dagger/internal/codegen/MapBindingExpressionTest.java b/javatests/dagger/internal/codegen/MapBindingExpressionTest.java
deleted file mode 100644
index 7759419..0000000
--- a/javatests/dagger/internal/codegen/MapBindingExpressionTest.java
+++ /dev/null
@@ -1,362 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.testing.compile.CompilationSubject.assertThat;
-import static dagger.internal.codegen.Compilers.daggerCompilerWithoutGuava;
-import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
-import static dagger.internal.codegen.GeneratedLines.NPE_FROM_PROVIDES_METHOD;
-
-import com.google.testing.compile.Compilation;
-import com.google.testing.compile.JavaFileObjects;
-import com.squareup.javapoet.CodeBlock;
-import javax.tools.JavaFileObject;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class MapBindingExpressionTest {
-  public static final CodeBlock NPE_FROM_PROVIDES =
-      CodeBlocks.stringLiteral(ErrorMessages.CANNOT_RETURN_NULL_FROM_NON_NULLABLE_PROVIDES_METHOD);
-
-  @Test
-  public void mapBindings() {
-    JavaFileObject mapModuleFile = JavaFileObjects.forSourceLines("test.MapModule",
-        "package test;",
-        "",
-        "import dagger.Module;",
-        "import dagger.Provides;",
-        "import dagger.multibindings.IntKey;",
-        "import dagger.multibindings.IntoMap;",
-        "import dagger.multibindings.LongKey;",
-        "import dagger.multibindings.Multibinds;",
-        "import java.util.Map;",
-        "",
-        "@Module",
-        "interface MapModule {",
-        "  @Multibinds Map<String, String> stringMap();",
-        "  @Provides @IntoMap @IntKey(0) static int provideInt() { return 0; }",
-        "  @Provides @IntoMap @LongKey(0) static long provideLong0() { return 0; }",
-        "  @Provides @IntoMap @LongKey(1) static long provideLong1() { return 1; }",
-        "  @Provides @IntoMap @LongKey(2) static long provideLong2() { return 2; }",
-        "}");
-    JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
-        "package test;",
-        "",
-        "import dagger.Component;",
-        "import java.util.Map;",
-        "import javax.inject.Provider;",
-        "",
-        "@Component(modules = MapModule.class)",
-        "interface TestComponent {",
-        "  Map<String, String> strings();",
-        "  Map<String, Provider<String>> providerStrings();",
-        "",
-        "  Map<Integer, Integer> ints();",
-        "  Map<Integer, Provider<Integer>> providerInts();",
-        "  Map<Long, Long> longs();",
-        "  Map<Long, Provider<Long>> providerLongs();",
-        "}");
-    JavaFileObject generatedComponent =
-        JavaFileObjects.forSourceLines(
-            "test.DaggerTestComponent",
-            "package test;",
-            "",
-            "import dagger.internal.MapBuilder;",
-            "import java.util.Collections;",
-            "import java.util.Map;",
-            "import javax.annotation.Generated;",
-            "import javax.inject.Provider;",
-            "",
-            GENERATED_ANNOTATION,
-            "public final class DaggerTestComponent implements TestComponent {",
-            "  private DaggerTestComponent(Builder builder) {}",
-            "",
-            "  public static Builder builder() {",
-            "    return new Builder();",
-            "  }",
-            "",
-            "  public static TestComponent create() {",
-            "    return new Builder().build();",
-            "  }",
-            "",
-            "  @Override",
-            "  public Map<String, String> strings() {",
-            "    return Collections.<String, String>emptyMap();",
-            "  }",
-            "",
-            "  @Override",
-            "  public Map<String, Provider<String>> providerStrings() {",
-            "    return Collections.<String, Provider<String>>emptyMap();",
-            "  }",
-            "",
-            "  @Override",
-            "  public Map<Integer, Integer> ints() {",
-            "    return Collections.<Integer, Integer>singletonMap(0, MapModule.provideInt());",
-            "  }",
-            "",
-            "  @Override",
-            "  public Map<Integer, Provider<Integer>> providerInts() {",
-            "    return Collections.<Integer, Provider<Integer>>singletonMap(",
-            "        0, MapModule_ProvideIntFactory.create());",
-            "  }",
-            "",
-            "  @Override",
-            "  public Map<Long, Long> longs() {",
-            "    return MapBuilder.<Long, Long>newMapBuilder(3)",
-            "      .put(0L, MapModule.provideLong0())",
-            "      .put(1L, MapModule.provideLong1())",
-            "      .put(2L, MapModule.provideLong2())",
-            "      .build();",
-            "  }",
-            "",
-            "  @Override",
-            "  public Map<Long, Provider<Long>> providerLongs() {",
-            "    return MapBuilder.<Long, Provider<Long>>newMapBuilder(3)",
-            "      .put(0L, MapModule_ProvideLong0Factory.create())",
-            "      .put(1L, MapModule_ProvideLong1Factory.create())",
-            "      .put(2L, MapModule_ProvideLong2Factory.create())",
-            "      .build();",
-            "  }",
-            "",
-            "  public static final class Builder {",
-            "    private Builder() {",
-            "    }",
-            "",
-            "    public TestComponent build() {",
-            "      return new DaggerTestComponent(this);",
-            "    }",
-            "  }",
-            "}");
-    Compilation compilation = daggerCompilerWithoutGuava().compile(mapModuleFile, componentFile);
-    assertThat(compilation).succeeded();
-    assertThat(compilation)
-        .generatedSourceFile("test.DaggerTestComponent")
-        .hasSourceEquivalentTo(generatedComponent);
-  }
-
-  @Test
-  public void inaccessible() {
-    JavaFileObject inaccessible =
-        JavaFileObjects.forSourceLines(
-            "other.Inaccessible",
-            "package other;",
-            "",
-            "class Inaccessible {}");
-    JavaFileObject usesInaccessible =
-        JavaFileObjects.forSourceLines(
-            "other.UsesInaccessible",
-            "package other;",
-            "",
-            "import java.util.Map;",
-            "import javax.inject.Inject;",
-            "",
-            "public class UsesInaccessible {",
-            "  @Inject UsesInaccessible(Map<Integer, Inaccessible> map) {}",
-            "}");
-
-    JavaFileObject module =
-        JavaFileObjects.forSourceLines(
-            "other.TestModule",
-            "package other;",
-            "",
-            "import dagger.Module;",
-            "import dagger.multibindings.Multibinds;",
-            "import java.util.Map;",
-            "",
-            "@Module",
-            "public abstract class TestModule {",
-            "  @Multibinds abstract Map<Integer, Inaccessible> ints();",
-            "}");
-    JavaFileObject componentFile =
-        JavaFileObjects.forSourceLines(
-            "test.TestComponent",
-            "package test;",
-            "",
-            "import dagger.Component;",
-            "import java.util.Map;",
-            "import javax.inject.Provider;",
-            "import other.TestModule;",
-            "import other.UsesInaccessible;",
-            "",
-            "@Component(modules = TestModule.class)",
-            "interface TestComponent {",
-            "  UsesInaccessible usesInaccessible();",
-            "}");
-    JavaFileObject generatedComponent =
-        JavaFileObjects.forSourceLines(
-            "test.DaggerTestComponent",
-            "package test;",
-            "",
-            "import java.util.Collections;",
-            "import java.util.Map;",
-            "import javax.annotation.Generated;",
-            "import other.UsesInaccessible;",
-            "import other.UsesInaccessible_Factory;",
-            "",
-            GENERATED_ANNOTATION,
-            "public final class DaggerTestComponent implements TestComponent {",
-            "  private DaggerTestComponent(Builder builder) {}",
-            "",
-            "  public static Builder builder() {",
-            "    return new Builder();",
-            "  }",
-            "",
-            "  public static TestComponent create() {",
-            "    return new Builder().build();",
-            "  }",
-            "",
-            "  @Override",
-            "  public UsesInaccessible usesInaccessible() {",
-            "    return UsesInaccessible_Factory.newUsesInaccessible(",
-            "        (Map) Collections.emptyMap());",
-            "  }",
-            "",
-            "  public static final class Builder {",
-            "    private Builder() {}",
-            "",
-            "    public TestComponent build() {",
-            "      return new DaggerTestComponent(this);",
-            "    }",
-            "  }",
-            "}");
-    Compilation compilation =
-        daggerCompilerWithoutGuava().compile(module, inaccessible, usesInaccessible, componentFile);
-    assertThat(compilation).succeeded();
-    assertThat(compilation)
-        .generatedSourceFile("test.DaggerTestComponent")
-        .hasSourceEquivalentTo(generatedComponent);
-  }
-
-  @Test
-  public void subcomponentOmitsInheritedBindings() {
-    JavaFileObject parent =
-        JavaFileObjects.forSourceLines(
-            "test.Parent",
-            "package test;",
-            "",
-            "import dagger.Component;",
-            "",
-            "@Component(modules = ParentModule.class)",
-            "interface Parent {",
-            "  Child child();",
-            "}");
-    JavaFileObject parentModule =
-        JavaFileObjects.forSourceLines(
-            "test.ParentModule",
-            "package test;",
-            "",
-            "import dagger.Module;",
-            "import dagger.Provides;",
-            "import dagger.multibindings.IntoMap;",
-            "import dagger.multibindings.StringKey;",
-            "",
-            "@Module",
-            "class ParentModule {",
-            "  @Provides @IntoMap @StringKey(\"parent key\") Object parentKeyObject() {",
-            "    return \"parent value\";",
-            "  }",
-            "}");
-    JavaFileObject child =
-        JavaFileObjects.forSourceLines(
-            "test.Child",
-            "package test;",
-            "",
-            "import dagger.Subcomponent;",
-            "import java.util.Map;",
-            "import java.util.Map;",
-            "",
-            "@Subcomponent",
-            "interface Child {",
-            "  Map<String, Object> objectMap();",
-            "}");
-    JavaFileObject expected =
-        JavaFileObjects.forSourceLines(
-            "test.DaggerParent",
-            "package test;",
-            "",
-            "import dagger.internal.Preconditions;",
-            "import java.util.Collections;",
-            "import java.util.Map;",
-            "import javax.annotation.Generated;",
-            "",
-            GENERATED_ANNOTATION,
-            "public final class DaggerParent implements Parent {",
-            "  private ParentModule parentModule;",
-            "",
-            "  private DaggerParent(Builder builder) {",
-            "    initialize(builder);",
-            "  }",
-            "",
-            "  public static Builder builder() {",
-            "    return new Builder();",
-            "  }",
-            "",
-            "  public static Parent create() {",
-            "    return new Builder().build();",
-            "  }",
-            "",
-            "  @SuppressWarnings(\"unchecked\")",
-            "  private void initialize(final Builder builder) {",
-            "    this.parentModule = builder.parentModule;",
-            "  }",
-            "",
-            "  @Override",
-            "  public Child child() {",
-            "    return new ChildImpl();",
-            "  }",
-            "",
-            "  public static final class Builder {",
-            "    private ParentModule parentModule;",
-            "",
-            "    private Builder() {}",
-            "",
-            "    public Parent build() {",
-            "      if (parentModule == null) {",
-            "        this.parentModule = new ParentModule();",
-            "      }",
-            "      return new DaggerParent(this);",
-            "    }",
-            "",
-            "    public Builder parentModule(ParentModule parentModule) {",
-            "      this.parentModule = Preconditions.checkNotNull(parentModule);",
-            "      return this;",
-            "    }",
-            "  }",
-            "",
-            "  private final class ChildImpl implements Child {",
-            "    private ChildImpl() {}",
-            "",
-            "    @Override",
-            "    public Map<String, Object> objectMap() {",
-            "      return Collections.<String, Object>singletonMap(",
-            "          \"parent key\",",
-            "          Preconditions.checkNotNull(",
-            "              DaggerParent.this.parentModule.parentKeyObject(),",
-            "              " + NPE_FROM_PROVIDES_METHOD + ");",
-            "    }",
-            "  }",
-            "}");
-    Compilation compilation = daggerCompilerWithoutGuava().compile(parent, parentModule, child);
-    assertThat(compilation).succeeded();
-    assertThat(compilation)
-        .generatedSourceFile("test.DaggerParent")
-        .hasSourceEquivalentTo(expected);
-  }
-
-}
diff --git a/javatests/dagger/internal/codegen/MapBindingExpressionWithGuavaTest.java b/javatests/dagger/internal/codegen/MapBindingExpressionWithGuavaTest.java
deleted file mode 100644
index 0b10fa4..0000000
--- a/javatests/dagger/internal/codegen/MapBindingExpressionWithGuavaTest.java
+++ /dev/null
@@ -1,502 +0,0 @@
-/*
- * Copyright (C) 2017 The Dagger Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dagger.internal.codegen;
-
-import static com.google.testing.compile.CompilationSubject.assertThat;
-import static dagger.internal.codegen.Compilers.daggerCompiler;
-import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
-import static dagger.internal.codegen.GeneratedLines.NPE_FROM_PROVIDES_METHOD;
-
-import com.google.testing.compile.Compilation;
-import com.google.testing.compile.JavaFileObjects;
-import javax.tools.JavaFileObject;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class MapBindingExpressionWithGuavaTest {
-  @Test
-  public void mapBindings() {
-    JavaFileObject mapModuleFile =
-        JavaFileObjects.forSourceLines(
-            "test.MapModule",
-            "package test;",
-            "",
-            "import dagger.Module;",
-            "import dagger.Provides;",
-            "import dagger.multibindings.IntKey;",
-            "import dagger.multibindings.IntoMap;",
-            "import dagger.multibindings.LongKey;",
-            "import dagger.multibindings.Multibinds;",
-            "import java.util.Map;",
-            "",
-            "@Module",
-            "interface MapModule {",
-            "  @Multibinds Map<String, String> stringMap();",
-            "  @Provides @IntoMap @IntKey(0) static int provideInt() { return 0; }",
-            "  @Provides @IntoMap @LongKey(0) static long provideLong0() { return 0; }",
-            "  @Provides @IntoMap @LongKey(1) static long provideLong1() { return 1; }",
-            "  @Provides @IntoMap @LongKey(2) static long provideLong2() { return 2; }",
-            "}");
-    JavaFileObject subcomponentModuleFile =
-        JavaFileObjects.forSourceLines(
-            "test.SubcomponentMapModule",
-            "package test;",
-            "",
-            "import dagger.Module;",
-            "import dagger.Provides;",
-            "import dagger.multibindings.IntKey;",
-            "import dagger.multibindings.IntoMap;",
-            "import dagger.multibindings.LongKey;",
-            "import dagger.multibindings.Multibinds;",
-            "import java.util.Map;",
-            "",
-            "@Module",
-            "interface SubcomponentMapModule {",
-            "  @Provides @IntoMap @LongKey(3) static long provideLong3() { return 3; }",
-            "  @Provides @IntoMap @LongKey(4) static long provideLong4() { return 4; }",
-            "  @Provides @IntoMap @LongKey(5) static long provideLong5() { return 5; }",
-            "}");
-    JavaFileObject componentFile =
-        JavaFileObjects.forSourceLines(
-            "test.TestComponent",
-            "package test;",
-            "",
-            "import dagger.Component;",
-            "import java.util.Map;",
-            "import javax.inject.Provider;",
-            "",
-            "@Component(modules = MapModule.class)",
-            "interface TestComponent {",
-            "  Map<String, String> strings();",
-            "  Map<String, Provider<String>> providerStrings();",
-            "",
-            "  Map<Integer, Integer> ints();",
-            "  Map<Integer, Provider<Integer>> providerInts();",
-            "  Map<Long, Long> longs();",
-            "  Map<Long, Provider<Long>> providerLongs();",
-            "",
-            "  Sub sub();",
-            "}");
-    JavaFileObject subcomponent =
-        JavaFileObjects.forSourceLines(
-            "test.Sub",
-            "package test;",
-            "",
-            "import dagger.Subcomponent;",
-            "import java.util.Map;",
-            "import javax.inject.Provider;",
-            "",
-            "@Subcomponent(modules = SubcomponentMapModule.class)",
-            "interface Sub {",
-            "  Map<Long, Long> longs();",
-            "  Map<Long, Provider<Long>> providerLongs();",
-            "}");
-    JavaFileObject generatedComponent =
-        JavaFileObjects.forSourceLines(
-            "test.DaggerTestComponent",
-            "package test;",
-            "",
-            "import com.google.common.collect.ImmutableMap;",
-            "import java.util.Map;",
-            "import javax.annotation.Generated;",
-            "import javax.inject.Provider;",
-            "",
-            GENERATED_ANNOTATION,
-            "public final class DaggerTestComponent implements TestComponent {",
-            "  private DaggerTestComponent(Builder builder) {}",
-            "",
-            "  public static Builder builder() {",
-            "    return new Builder();",
-            "  }",
-            "",
-            "  public static TestComponent create() {",
-            "    return new Builder().build();",
-            "  }",
-            "",
-            "  @Override",
-            "  public Map<String, String> strings() {",
-            "    return ImmutableMap.<String, String>of();",
-            "  }",
-            "",
-            "  @Override",
-            "  public Map<String, Provider<String>> providerStrings() {",
-            "    return ImmutableMap.<String, Provider<String>>of();",
-            "  }",
-            "",
-            "  @Override",
-            "  public Map<Integer, Integer> ints() {",
-            "    return ImmutableMap.<Integer, Integer>of(0, MapModule.provideInt());",
-            "  }",
-            "",
-            "  @Override",
-            "  public Map<Integer, Provider<Integer>> providerInts() {",
-            "    return ImmutableMap.<Integer, Provider<Integer>>of(",
-            "        0, MapModule_ProvideIntFactory.create());",
-            "  }",
-            "",
-            "  @Override",
-            "  public Map<Long, Long> longs() {",
-            "    return ImmutableMap.<Long, Long>of(",
-            "      0L, MapModule.provideLong0(),",
-            "      1L, MapModule.provideLong1(),",
-            "      2L, MapModule.provideLong2());",
-            "  }",
-            "",
-            "  @Override",
-            "  public Map<Long, Provider<Long>> providerLongs() {",
-            "    return ImmutableMap.<Long, Provider<Long>>of(",
-            "      0L, MapModule_ProvideLong0Factory.create(),",
-            "      1L, MapModule_ProvideLong1Factory.create(),",
-            "      2L, MapModule_ProvideLong2Factory.create());",
-            "  }",
-            "",
-            "  @Override",
-            "  public Sub sub() {",
-            "    return new SubImpl();",
-            "  }",
-            "",
-            "  public static final class Builder {",
-            "    private Builder() {",
-            "    }",
-            "",
-            "    public TestComponent build() {",
-            "      return new DaggerTestComponent(this);",
-            "    }",
-            "  }",
-            "  private final class SubImpl implements Sub {",
-            "    private SubImpl() {}",
-            "",
-            "    @Override",
-            "    public Map<Long, Long> longs() {",
-            "      return ImmutableMap.<Long, Long>builder()",
-            "          .put(0L, MapModule.provideLong0())",
-            "          .put(1L, MapModule.provideLong1())",
-            "          .put(2L, MapModule.provideLong2())",
-            "          .put(3L, SubcomponentMapModule.provideLong3())",
-            "          .put(4L, SubcomponentMapModule.provideLong4())",
-            "          .put(5L, SubcomponentMapModule.provideLong5())",
-            "          .build();",
-            "    }",
-            "",
-            "    @Override",
-            "    public Map<Long, Provider<Long>> providerLongs() {",
-            "      return ImmutableMap.<Long, Provider<Long>>builder()",
-            "          .put(0L, MapModule_ProvideLong0Factory.create())",
-            "          .put(1L, MapModule_ProvideLong1Factory.create())",
-            "          .put(2L, MapModule_ProvideLong2Factory.create())",
-            "          .put(3L, SubcomponentMapModule_ProvideLong3Factory.create())",
-            "          .put(4L, SubcomponentMapModule_ProvideLong4Factory.create())",
-            "          .put(5L, SubcomponentMapModule_ProvideLong5Factory.create())",
-            "          .build();",
-            "    }",
-            "  }",
-            "}");
-    Compilation compilation =
-        daggerCompiler()
-            .compile(mapModuleFile, componentFile, subcomponentModuleFile, subcomponent);
-    assertThat(compilation).succeeded();
-    assertThat(compilation)
-        .generatedSourceFile("test.DaggerTestComponent")
-        .hasSourceEquivalentTo(generatedComponent);
-  }
-
-  @Test
-  public void inaccessible() {
-    JavaFileObject inaccessible =
-        JavaFileObjects.forSourceLines(
-            "other.Inaccessible", "package other;", "", "class Inaccessible {}");
-    JavaFileObject usesInaccessible =
-        JavaFileObjects.forSourceLines(
-            "other.UsesInaccessible",
-            "package other;",
-            "",
-            "import java.util.Map;",
-            "import javax.inject.Inject;",
-            "",
-            "public class UsesInaccessible {",
-            "  @Inject UsesInaccessible(Map<Integer, Inaccessible> map) {}",
-            "}");
-
-    JavaFileObject module =
-        JavaFileObjects.forSourceLines(
-            "other.TestModule",
-            "package other;",
-            "",
-            "import dagger.Module;",
-            "import dagger.multibindings.Multibinds;",
-            "import java.util.Map;",
-            "",
-            "@Module",
-            "public abstract class TestModule {",
-            "  @Multibinds abstract Map<Integer, Inaccessible> ints();",
-            "}");
-    JavaFileObject componentFile =
-        JavaFileObjects.forSourceLines(
-            "test.TestComponent",
-            "package test;",
-            "",
-            "import dagger.Component;",
-            "import java.util.Map;",
-            "import javax.inject.Provider;",
-            "import other.TestModule;",
-            "import other.UsesInaccessible;",
-            "",
-            "@Component(modules = TestModule.class)",
-            "interface TestComponent {",
-            "  UsesInaccessible usesInaccessible();",
-            "}");
-    JavaFileObject generatedComponent =
-        JavaFileObjects.forSourceLines(
-            "test.DaggerTestComponent",
-            "package test;",
-            "",
-            "import com.google.common.collect.ImmutableMap;",
-            "import java.util.Map;",
-            "import javax.annotation.Generated;",
-            "import other.UsesInaccessible;",
-            "import other.UsesInaccessible_Factory;",
-            "",
-            GENERATED_ANNOTATION,
-            "public final class DaggerTestComponent implements TestComponent {",
-            "  private DaggerTestComponent(Builder builder) {}",
-            "",
-            "  public static Builder builder() {",
-            "    return new Builder();",
-            "  }",
-            "",
-            "  public static TestComponent create() {",
-            "    return new Builder().build();",
-            "  }",
-            "",
-            "  @Override",
-            "  public UsesInaccessible usesInaccessible() {",
-            "    return UsesInaccessible_Factory.newUsesInaccessible((Map) ImmutableMap.of());",
-            "  }",
-            "",
-            "  public static final class Builder {",
-            "    private Builder() {}",
-            "",
-            "    public TestComponent build() {",
-            "      return new DaggerTestComponent(this);",
-            "    }",
-            "  }",
-            "}");
-    Compilation compilation =
-        daggerCompiler().compile(module, inaccessible, usesInaccessible, componentFile);
-    assertThat(compilation).succeeded();
-    assertThat(compilation)
-        .generatedSourceFile("test.DaggerTestComponent")
-        .hasSourceEquivalentTo(generatedComponent);
-  }
-
-  @Test
-  public void subcomponentOmitsInheritedBindings() {
-    JavaFileObject parent =
-        JavaFileObjects.forSourceLines(
-            "test.Parent",
-            "package test;",
-            "",
-            "import dagger.Component;",
-            "",
-            "@Component(modules = ParentModule.class)",
-            "interface Parent {",
-            "  Child child();",
-            "}");
-    JavaFileObject parentModule =
-        JavaFileObjects.forSourceLines(
-            "test.ParentModule",
-            "package test;",
-            "",
-            "import dagger.Module;",
-            "import dagger.Provides;",
-            "import dagger.multibindings.IntoMap;",
-            "import dagger.multibindings.StringKey;",
-            "",
-            "@Module",
-            "class ParentModule {",
-            "  @Provides @IntoMap @StringKey(\"parent key\") Object parentKeyObject() {",
-            "    return \"parent value\";",
-            "  }",
-            "}");
-    JavaFileObject child =
-        JavaFileObjects.forSourceLines(
-            "test.Child",
-            "package test;",
-            "",
-            "import dagger.Subcomponent;",
-            "import java.util.Map;",
-            "",
-            "@Subcomponent",
-            "interface Child {",
-            "  Map<String, Object> objectMap();",
-            "}");
-    JavaFileObject expected =
-        JavaFileObjects.forSourceLines(
-            "test.DaggerParent",
-            "package test;",
-            "",
-            "import com.google.common.collect.ImmutableMap;",
-            "import dagger.internal.Preconditions;",
-            "import java.util.Map;",
-            "import javax.annotation.Generated;",
-            "",
-            GENERATED_ANNOTATION,
-            "public final class DaggerParent implements Parent {",
-            "  private ParentModule parentModule;",
-            "",
-            "  private DaggerParent(Builder builder) {",
-            "    initialize(builder);",
-            "  }",
-            "",
-            "  public static Builder builder() {",
-            "    return new Builder();",
-            "  }",
-            "",
-            "  public static Parent create() {",
-            "    return new Builder().build();",
-            "  }",
-            "",
-            "  @SuppressWarnings(\"unchecked\")",
-            "  private void initialize(final Builder builder) {",
-            "    this.parentModule = builder.parentModule;",
-            "  }",
-            "",
-            "  @Override",
-            "  public Child child() {",
-            "    return new ChildImpl();",
-            "  }",
-            "",
-            "  public static final class Builder {",
-            "    private ParentModule parentModule;",
-            "",
-            "    private Builder() {}",
-            "",
-            "    public Parent build() {",
-            "      if (parentModule == null) {",
-            "        this.parentModule = new ParentModule();",
-            "      }",
-            "      return new DaggerParent(this);",
-            "    }",
-            "",
-            "    public Builder parentModule(ParentModule parentModule) {",
-            "      this.parentModule = Preconditions.checkNotNull(parentModule);",
-            "      return this;",
-            "    }",
-            "  }",
-            "",
-            "  private final class ChildImpl implements Child {",
-            "    private ChildImpl() {}",
-            "",
-            "    @Override",
-            "    public Map<String, Object> objectMap() {",
-            "      return ImmutableMap.<String, Object>of(",
-            "          \"parent key\",",
-            "          Preconditions.checkNotNull(",
-            "              DaggerParent.this.parentModule.parentKeyObject(),",
-            "              " + NPE_FROM_PROVIDES_METHOD + ");",
-            "    }",
-            "  }",
-            "}");
-    Compilation compilation = daggerCompiler().compile(parent, parentModule, child);
-    assertThat(compilation).succeeded();
-    assertThat(compilation)
-        .generatedSourceFile("test.DaggerParent")
-        .hasSourceEquivalentTo(expected);
-  }
-
-  @Test
-  public void productionComponents() {
-    JavaFileObject mapModuleFile =
-        JavaFileObjects.forSourceLines(
-            "test.MapModule",
-            "package test;",
-            "",
-            "import dagger.Module;",
-            "import dagger.multibindings.Multibinds;",
-            "import java.util.Map;",
-            "",
-            "@Module",
-            "interface MapModule {",
-            "  @Multibinds Map<String, String> stringMap();",
-            "}");
-    JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
-        "package test;",
-        "",
-        "import com.google.common.util.concurrent.ListenableFuture;",
-        "import dagger.producers.ProductionComponent;",
-        "import java.util.Map;",
-        "",
-        "@ProductionComponent(modules = MapModule.class)",
-        "interface TestComponent {",
-        "  ListenableFuture<Map<String, String>> stringMap();",
-        "}");
-    JavaFileObject generatedComponent =
-        JavaFileObjects.forSourceLines(
-            "test.DaggerTestComponent",
-            "package test;",
-            "",
-            "import com.google.common.collect.ImmutableMap;",
-            "import com.google.common.util.concurrent.Futures;",
-            "import com.google.common.util.concurrent.ListenableFuture;",
-            "import dagger.internal.Preconditions;",
-            "import java.util.Map;",
-            "import javax.annotation.Generated;",
-            "",
-            GENERATED_ANNOTATION,
-            "public final class DaggerTestComponent implements TestComponent {",
-            "  private DaggerTestComponent(Builder builder) {}",
-            "",
-            "  public static Builder builder() {",
-            "    return new Builder();",
-            "  }",
-            "",
-            "  public static TestComponent create() {",
-            "    return new Builder().build();",
-            "  }",
-            "",
-            "  @Override",
-            "  public ListenableFuture<Map<String, String>> stringMap() {",
-            "    return Futures.<Map<String, String>>immediateFuture(",
-            "        ImmutableMap.<String, String>of());",
-            "  }",
-            "",
-            "  public static final class Builder {",
-            "    private Builder() {}",
-            "",
-            "    public TestComponent build() {",
-            "      return new DaggerTestComponent(this);",
-            "    }",
-            "",
-            "    @Deprecated",
-            "    public Builder testComponent_ProductionExecutorModule(",
-            "        TestComponent_ProductionExecutorModule",
-            "            testComponent_ProductionExecutorModule) {",
-            "      Preconditions.checkNotNull(testComponent_ProductionExecutorModule);",
-            "      return this;",
-            "    }",
-            "  }",
-            "}");
-    Compilation compilation =
-        daggerCompiler().compile(mapModuleFile, componentFile);
-    assertThat(compilation).succeeded();
-    assertThat(compilation)
-        .generatedSourceFile("test.DaggerTestComponent")
-        .hasSourceEquivalentTo(generatedComponent);
-  }
-}
diff --git a/javatests/dagger/internal/codegen/MapKeyProcessorTest.java b/javatests/dagger/internal/codegen/MapKeyProcessorTest.java
index 11d9db4..4a72a5f 100644
--- a/javatests/dagger/internal/codegen/MapKeyProcessorTest.java
+++ b/javatests/dagger/internal/codegen/MapKeyProcessorTest.java
@@ -212,7 +212,7 @@
             "test.DaggerTestComponent",
             "package test;",
             "",
-            "import com.google.common.collect.ImmutableMap;",
+            "import dagger.internal.MapProviderFactory;",
             "import dagger.internal.Preconditions;",
             "import java.util.Map;",
             "import javax.annotation.Generated;",
@@ -222,6 +222,8 @@
             "public final class DaggerTestComponent implements TestComponent {",
             "  private Provider<Handler> provideAdminHandlerProvider;",
             "  private Provider<Handler> provideLoginHandlerProvider;",
+            "  private Provider<Map<PathKey, Provider<Handler>>>",
+            "      mapOfPathKeyAndProviderOfHandlerProvider;",
             "",
             "  private DaggerTestComponent(Builder builder) {",
             "    initialize(builder);",
@@ -241,15 +243,18 @@
             "        MapModuleOne_ProvideAdminHandlerFactory.create(builder.mapModuleOne);",
             "    this.provideLoginHandlerProvider =",
             "        MapModuleTwo_ProvideLoginHandlerFactory.create(builder.mapModuleTwo);",
+            "    this.mapOfPathKeyAndProviderOfHandlerProvider =",
+            "        MapProviderFactory.<PathKey, Handler>builder(2)",
+            "            .put(PathKeyCreator.createPathKey(PathEnum.ADMIN, \"AdminPath\"),",
+            "                provideAdminHandlerProvider)",
+            "            .put(PathKeyCreator.createPathKey(PathEnum.LOGIN, \"LoginPath\"),",
+            "                provideLoginHandlerProvider)",
+            "            .build();",
             "  }",
             "",
             "  @Override",
             "  public Map<PathKey, Provider<Handler>> dispatcher() {",
-            "    return ImmutableMap.<PathKey, Provider<Handler>>of(",
-            "        PathKeyCreator.createPathKey(PathEnum.ADMIN, \"AdminPath\"),",
-            "        provideAdminHandlerProvider,",
-            "        PathKeyCreator.createPathKey(PathEnum.LOGIN, \"LoginPath\"),",
-            "        provideLoginHandlerProvider);",
+            "    return mapOfPathKeyAndProviderOfHandlerProvider.get();",
             "  }",
             "",
             "  public static final class Builder {",
@@ -381,7 +386,7 @@
             "test.DaggerTestComponent",
             "package test;",
             "",
-            "import com.google.common.collect.ImmutableMap;",
+            "import dagger.internal.MapProviderFactory;",
             "import dagger.internal.Preconditions;",
             "import java.util.Map;",
             "import javax.annotation.Generated;",
@@ -391,6 +396,8 @@
             "public final class DaggerTestComponent implements TestComponent {",
             "  private Provider<Handler> provideAdminHandlerProvider;",
             "  private Provider<Handler> provideLoginHandlerProvider;",
+            "  private Provider<Map<PathKey, Provider<Handler>>>",
+            "      mapOfPathKeyAndProviderOfHandlerProvider;",
             "",
             "  private DaggerTestComponent(Builder builder) {",
             "    initialize(builder);",
@@ -410,15 +417,18 @@
             "        MapModuleOne_ProvideAdminHandlerFactory.create(builder.mapModuleOne);",
             "    this.provideLoginHandlerProvider =",
             "        MapModuleTwo_ProvideLoginHandlerFactory.create(builder.mapModuleTwo);",
+            "    this.mapOfPathKeyAndProviderOfHandlerProvider =",
+            "        MapProviderFactory.<PathKey, Handler>builder(2)",
+            "            .put(PathKeyCreator.createPathKey(PathEnum.ADMIN, \"DefaultPath\"),",
+            "                provideAdminHandlerProvider)",
+            "            .put(PathKeyCreator.createPathKey(PathEnum.LOGIN, \"LoginPath\"),",
+            "                provideLoginHandlerProvider)",
+            "            .build();",
             "  }",
             "",
             "  @Override",
             "  public Map<PathKey, Provider<Handler>> dispatcher() {",
-            "    return ImmutableMap.<PathKey, Provider<Handler>>of(",
-            "        PathKeyCreator.createPathKey(PathEnum.ADMIN, \"DefaultPath\"),",
-            "        provideAdminHandlerProvider,",
-            "        PathKeyCreator.createPathKey(PathEnum.LOGIN, \"LoginPath\"),",
-            "        provideLoginHandlerProvider);",
+            "    return mapOfPathKeyAndProviderOfHandlerProvider.get();",
             "  }",
             "",
             "  public static final class Builder {",
diff --git a/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentTest.java b/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentTest.java
index 226972c..1cadddb 100644
--- a/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentTest.java
+++ b/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentTest.java
@@ -16,11 +16,16 @@
 
 package dagger.internal.codegen;
 
+import static com.google.common.base.StandardSystemProperty.JAVA_CLASS_PATH;
+import static com.google.common.base.StandardSystemProperty.PATH_SEPARATOR;
 import static com.google.testing.compile.CompilationSubject.assertThat;
-import static dagger.internal.codegen.Compilers.daggerCompilerWithoutGuava;
+import static dagger.internal.codegen.Compilers.daggerCompiler;
 import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
+import static java.util.stream.Collectors.joining;
 
+import com.google.common.base.Splitter;
 import com.google.testing.compile.Compilation;
+import com.google.testing.compile.Compiler;
 import com.google.testing.compile.JavaFileObjects;
 import com.squareup.javapoet.CodeBlock;
 import javax.tools.JavaFileObject;
@@ -131,8 +136,7 @@
             "    }",
             "  }",
             "}");
-    Compilation compilation =
-        daggerCompilerWithoutGuava().compile(emptySetModuleFile, setModuleFile, componentFile);
+    Compilation compilation = compiler().compile(emptySetModuleFile, setModuleFile, componentFile);
     assertThat(compilation).succeeded();
     assertThat(compilation)
         .generatedSourceFile("test.DaggerTestComponent")
@@ -247,8 +251,7 @@
             "  }",
             "}");
     Compilation compilation =
-        daggerCompilerWithoutGuava()
-            .compile(module, inaccessible, inaccessible2, usesInaccessible, componentFile);
+        compiler().compile(module, inaccessible, inaccessible2, usesInaccessible, componentFile);
     assertThat(compilation).succeeded();
     assertThat(compilation)
         .generatedSourceFile("test.DaggerTestComponent")
@@ -276,6 +279,7 @@
             "import dagger.Module;",
             "import dagger.Provides;",
             "import dagger.multibindings.IntoSet;",
+            "import dagger.multibindings.IntoMap;",
             "import dagger.multibindings.StringKey;",
             "",
             "@Module",
@@ -283,6 +287,10 @@
             "  @Provides @IntoSet static Object parentObject() {",
             "    return \"parent object\";",
             "  }",
+            "",
+            "  @Provides @IntoMap @StringKey(\"parent key\") Object parentKeyObject() {",
+            "    return \"parent value\";",
+            "  }",
             "}");
     JavaFileObject child =
         JavaFileObjects.forSourceLines(
@@ -290,25 +298,35 @@
             "package test;",
             "",
             "import dagger.Subcomponent;",
+            "import java.util.Map;",
             "import java.util.Set;",
             "",
             "@Subcomponent",
             "interface Child {",
             "  Set<Object> objectSet();",
+            "  Map<String, Object> objectMap();",
             "}");
     JavaFileObject expected =
         JavaFileObjects.forSourceLines(
             "test.DaggerParent",
             "package test;",
             "",
+            "import dagger.internal.MapFactory;",
+            "import dagger.internal.MapProviderFactory;",
             "import dagger.internal.Preconditions;",
             "import java.util.Collections;",
+            "import java.util.Map;",
             "import java.util.Set;",
             "import javax.annotation.Generated;",
+            "import javax.inject.Provider;",
             "",
             GENERATED_ANNOTATION,
             "public final class DaggerParent implements Parent {",
-            "  private DaggerParent(Builder builder) {}",
+            "  private Provider<Object> parentKeyObjectProvider;",
+            "",
+            "  private DaggerParent(Builder builder) {",
+            "    initialize(builder);",
+            "  }",
             "",
             "  public static Builder builder() {",
             "    return new Builder();",
@@ -318,40 +336,84 @@
             "    return new Builder().build();",
             "  }",
             "",
+            "  @SuppressWarnings(\"unchecked\")",
+            "  private void initialize(final Builder builder) {",
+            "    this.parentKeyObjectProvider =",
+            "        ParentModule_ParentKeyObjectFactory.create(builder.parentModule);",
+            "  }",
+            "",
             "  @Override",
             "  public Child child() {",
             "    return new ChildImpl();",
             "  }",
             "",
             "  public static final class Builder {",
+            "    private ParentModule parentModule;",
+            "",
             "    private Builder() {}",
             "",
             "    public Parent build() {",
+            "      if (parentModule == null) {",
+            "        this.parentModule = new ParentModule();",
+            "      }",
             "      return new DaggerParent(this);",
             "    }",
             "",
-            "    @Deprecated",
             "    public Builder parentModule(ParentModule parentModule) {",
-            "      Preconditions.checkNotNull(parentModule);",
+            "      this.parentModule = Preconditions.checkNotNull(parentModule);",
             "      return this;",
             "    }",
             "  }",
             "",
             "  private final class ChildImpl implements Child {",
-            "    private ChildImpl() {}",
+            "    private Provider<Map<String, Provider<Object>>>",
+            "        mapOfStringAndProviderOfObjectProvider;",
+            "    private Provider<Map<String, Object>> mapOfStringAndObjectProvider;",
+            "",
+            "    private ChildImpl() {",
+            "      initialize();",
+            "    }",
+            "",
+            "    @SuppressWarnings(\"unchecked\")",
+            "    private void initialize() {",
+            "      this.mapOfStringAndProviderOfObjectProvider =",
+            "          MapProviderFactory.<String, Object>builder(1)",
+            "              .put(\"parent key\", DaggerParent.this.parentKeyObjectProvider)",
+            "              .build();",
+            "      this.mapOfStringAndObjectProvider = MapFactory.create(",
+            "          mapOfStringAndProviderOfObjectProvider);",
+            "    }",
             "",
             "    @Override",
             "    public Set<Object> objectSet() {",
             "      return Collections.<Object>singleton(Preconditions.checkNotNull(",
             "          ParentModule.parentObject(), " + NPE_FROM_PROVIDES + "));",
             "    }",
+            "",
+            "    @Override",
+            "    public Map<String, Object> objectMap() {",
+            "      return mapOfStringAndObjectProvider.get();",
+            "    }",
             "  }",
             "}");
-    Compilation compilation = daggerCompilerWithoutGuava().compile(parent, parentModule, child);
+    Compilation compilation = compiler().compile(parent, parentModule, child);
     assertThat(compilation).succeeded();
     assertThat(compilation)
         .generatedSourceFile("test.DaggerParent")
         .hasSourceEquivalentTo(expected);
   }
 
+  private Compiler compiler() {
+    return daggerCompiler().withOptions("-classpath", classpathWithoutGuava());
+  }
+
+  private static final String GUAVA = "guava";
+
+  private String classpathWithoutGuava() {
+    return Splitter.on(PATH_SEPARATOR.value())
+        .splitToList(JAVA_CLASS_PATH.value())
+        .stream()
+        .filter(jar -> !jar.contains(GUAVA))
+        .collect(joining(":"));
+  }
 }
diff --git a/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentWithGuavaTest.java b/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentWithGuavaTest.java
index 8de07ec..1312e09 100644
--- a/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentWithGuavaTest.java
+++ b/javatests/dagger/internal/codegen/SetBindingRequestFulfillmentWithGuavaTest.java
@@ -286,6 +286,7 @@
             "import dagger.Module;",
             "import dagger.Provides;",
             "import dagger.multibindings.IntoSet;",
+            "import dagger.multibindings.IntoMap;",
             "import dagger.multibindings.StringKey;",
             "",
             "@Module",
@@ -293,6 +294,10 @@
             "  @Provides @IntoSet static Object parentObject() {",
             "    return \"parent object\";",
             "  }",
+            "",
+            "  @Provides @IntoMap @StringKey(\"parent key\") Object parentKeyObject() {",
+            "    return \"parent value\";",
+            "  }",
             "}");
     JavaFileObject child =
         JavaFileObjects.forSourceLines(
@@ -300,11 +305,13 @@
             "package test;",
             "",
             "import dagger.Subcomponent;",
+            "import java.util.Map;",
             "import java.util.Set;",
             "",
             "@Subcomponent",
             "interface Child {",
             "  Set<Object> objectSet();",
+            "  Map<String, Object> objectMap();",
             "}");
     JavaFileObject expected =
         JavaFileObjects.forSourceLines(
@@ -312,13 +319,21 @@
             "package test;",
             "",
             "import com.google.common.collect.ImmutableSet;",
+            "import dagger.internal.MapFactory;",
+            "import dagger.internal.MapProviderFactory;",
             "import dagger.internal.Preconditions;",
+            "import java.util.Map;",
             "import java.util.Set;",
             "import javax.annotation.Generated;",
+            "import javax.inject.Provider;",
             "",
             GENERATED_ANNOTATION,
             "public final class DaggerParent implements Parent {",
-            "  private DaggerParent(Builder builder) {}",
+            "  private Provider<Object> parentKeyObjectProvider;",
+            "",
+            "  private DaggerParent(Builder builder) {",
+            "    initialize(builder);",
+            "  }",
             "",
             "  public static Builder builder() {",
             "    return new Builder();",
@@ -328,33 +343,64 @@
             "    return new Builder().build();",
             "  }",
             "",
+            "  @SuppressWarnings(\"unchecked\")",
+            "  private void initialize(final Builder builder) {",
+            "    this.parentKeyObjectProvider =",
+            "        ParentModule_ParentKeyObjectFactory.create(builder.parentModule);",
+            "  }",
+            "",
             "  @Override",
             "  public Child child() {",
             "    return new ChildImpl();",
             "  }",
             "",
             "  public static final class Builder {",
+            "    private ParentModule parentModule;",
+            "",
             "    private Builder() {}",
             "",
             "    public Parent build() {",
+            "      if (parentModule == null) {",
+            "        this.parentModule = new ParentModule();",
+            "      }",
             "      return new DaggerParent(this);",
             "    }",
             "",
-            "    @Deprecated",
             "    public Builder parentModule(ParentModule parentModule) {",
-            "      Preconditions.checkNotNull(parentModule);",
+            "      this.parentModule = Preconditions.checkNotNull(parentModule);",
             "      return this;",
             "    }",
             "  }",
             "",
             "  private final class ChildImpl implements Child {",
-            "    private ChildImpl() {}",
+            "    private Provider<Map<String, Provider<Object>>>",
+            "        mapOfStringAndProviderOfObjectProvider;",
+            "    private Provider<Map<String, Object>> mapOfStringAndObjectProvider;",
+            "",
+            "    private ChildImpl() {",
+            "      initialize();",
+            "    }",
+            "",
+            "    @SuppressWarnings(\"unchecked\")",
+            "    private void initialize() {",
+            "      this.mapOfStringAndProviderOfObjectProvider =",
+            "          MapProviderFactory.<String, Object>builder(1)",
+            "              .put(\"parent key\", DaggerParent.this.parentKeyObjectProvider)",
+            "              .build();",
+            "      this.mapOfStringAndObjectProvider = MapFactory.create(",
+            "          mapOfStringAndProviderOfObjectProvider);",
+            "    }",
             "",
             "    @Override",
             "    public Set<Object> objectSet() {",
             "      return ImmutableSet.<Object>of(Preconditions.checkNotNull(",
             "          ParentModule.parentObject(), " + NPE_FROM_PROVIDES + "));",
             "    }",
+            "",
+            "    @Override",
+            "    public Map<String, Object> objectMap() {",
+            "      return mapOfStringAndObjectProvider.get();",
+            "    }",
             "  }",
             "}");
     Compilation compilation = daggerCompiler().compile(parent, parentModule, child);
diff --git a/javatests/dagger/producers/internal/MapOfProducerProducerTest.java b/javatests/dagger/producers/internal/MapOfProducerProducerTest.java
index 645a016..fe285ed 100644
--- a/javatests/dagger/producers/internal/MapOfProducerProducerTest.java
+++ b/javatests/dagger/producers/internal/MapOfProducerProducerTest.java
@@ -31,7 +31,7 @@
   @Test
   public void success() throws Exception {
     MapOfProducerProducer<Integer, String> mapOfProducerProducer =
-        MapOfProducerProducer.<Integer, String>builder()
+        MapOfProducerProducer.<Integer, String>builder(2)
             .put(15, Producers.<String>immediateProducer("fifteen"))
             .put(42, Producers.<String>immediateProducer("forty two"))
             .build();
@@ -47,7 +47,7 @@
   public void failingContributionDoesNotFailMap() throws Exception {
     RuntimeException cause = new RuntimeException("monkey");
     MapOfProducerProducer<Integer, String> mapOfProducerProducer =
-        MapOfProducerProducer.<Integer, String>builder()
+        MapOfProducerProducer.<Integer, String>builder(2)
             .put(15, Producers.<String>immediateProducer("fifteen"))
             .put(42, Producers.<String>immediateFailedProducer(cause))
             .build();
diff --git a/javatests/dagger/producers/internal/MapProducerTest.java b/javatests/dagger/producers/internal/MapProducerTest.java
index ee7978b..3a8f19d 100644
--- a/javatests/dagger/producers/internal/MapProducerTest.java
+++ b/javatests/dagger/producers/internal/MapProducerTest.java
@@ -19,6 +19,7 @@
 import static com.google.common.truth.Truth.assertThat;
 import static org.junit.Assert.fail;
 
+import com.google.common.collect.ImmutableMap;
 import dagger.producers.Producer;
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
@@ -31,10 +32,13 @@
   @Test
   public void success() throws Exception {
     Producer<Map<Integer, String>> mapProducer =
-        MapProducer.<Integer, String>builder()
-            .put(15, Producers.immediateProducer("fifteen"))
-            .put(42, Producers.immediateProducer("forty two"))
-            .build();
+        MapProducer.create(
+            Producers.<Map<Integer, Producer<String>>>immediateProducer(
+                ImmutableMap.<Integer, Producer<String>>of(
+                    15,
+                    Producers.<String>immediateProducer("fifteen"),
+                    42,
+                    Producers.<String>immediateProducer("forty two"))));
     Map<Integer, String> map = mapProducer.get().get();
     assertThat(map).hasSize(2);
     assertThat(map).containsEntry(15, "fifteen");
@@ -45,11 +49,27 @@
   public void failingContribution() throws Exception {
     RuntimeException cause = new RuntimeException("monkey");
     Producer<Map<Integer, String>> mapProducer =
-        MapProducer.<Integer, String>builder()
-            .put(15, Producers.immediateProducer("fifteen"))
-            // TODO(ronshapiro): remove the type parameter when we drop java7 support
-            .put(42, Producers.<String>immediateFailedProducer(cause))
-            .build();
+        MapProducer.create(
+            Producers.<Map<Integer, Producer<String>>>immediateProducer(
+                ImmutableMap.<Integer, Producer<String>>of(
+                    15,
+                    Producers.<String>immediateProducer("fifteen"),
+                    42,
+                    Producers.<String>immediateFailedProducer(cause))));
+    try {
+      mapProducer.get().get();
+      fail();
+    } catch (ExecutionException e) {
+      assertThat(e.getCause()).isSameAs(cause);
+    }
+  }
+
+  @Test
+  public void failingInput() throws Exception {
+    RuntimeException cause = new RuntimeException("monkey");
+    Producer<Map<Integer, String>> mapProducer =
+        MapProducer.create(
+            Producers.<Map<Integer, Producer<String>>>immediateFailedProducer(cause));
     try {
       mapProducer.get().get();
       fail();