Adds a new OptionalBinder.  OptionalBinder allows frameworks to setup bindings for items that user code may or may not bind.  It also allows frameworks to set default values that users can override.

The API is:
OptionalBinder.newOptionalBinder(Binder, Class|TypeLiteral|Key) -> OptionalBinder
optionalBinder.setDefault -> LinkedBindingBuilder
optionalBinder.setBinding -> LinkedBindingBuilder

By way of example, this will do:

newOptionalBinder(..) -> @Inject Thing -> exception: neither setDefault nor setBinding called
newOptionalBinder(..) -> @Inject Optional<Thing> -> isPresent() == false
newOptionalBinder(..).setDefault().to("a") -> @Inject Thing -> "a"
newOptionalBinder(..).setDefault().to("a") -> @Inject Optional<Thing> --> get() == "a"
newOptionalBinder(..).setDefault().to("a") + newOptionalBinder(..).setBinding().to("b") -> @Inject Thing -> "b"
newOptionalBinder(..).setDefault().to("a") + newOptionalBinder(..).setBinding().to("b") -> @Inject Optional<Thing> -> get() == "b"
newOptionalBinder(..).setBinding().to("b") -> @Inject Thing -> "b"
newOptionalBinder(..).setBinding().to("b") -> @Inject Optional<Thing> -> get() == "b"
newOptionalBinder(..).setDefault().to("a") + newOptionalBinder(..).setDefault().to("b") -> configuration exception
newOptionalBinder(..).setBinding().to("a") + newOptionalBinder(..).setBinding().to("b") -> configuration exception

(This also adds the jsr305 jar for build time, because doclava wanted it. Frustrating.)

(This also fixes users that implemented MultibindingsTargetVisitor, because I can't use default methods yet.)
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=63873859
diff --git a/build.xml b/build.xml
index 4bba6fe..203d504 100644
--- a/build.xml
+++ b/build.xml
@@ -185,7 +185,7 @@
              docletpath="lib/build/doclava.jar"
              bootclasspath="${java.home}/lib/rt.jar"
              maxmemory="512M"
-             classpath="lib/javax.inject.jar${path.separator}lib/aopalliance.jar${path.separator}lib/guava-16.0.1.jar">
+             classpath="lib/javax.inject.jar${path.separator}lib/aopalliance.jar${path.separator}lib/guava-16.0.1.jar{$path.separator}lib/build/jsr305.jar">
       <fileset dir="${src.dir}" defaultexcludes="yes">
         <include name="com/google/**"/>
         <exclude name="com/google/inject/internal/**"/>
@@ -198,7 +198,7 @@
       <fileset dir="${throwingproviders.src.dir}"/>
       <fileset dir="${multibindings.src.dir}"/>
       <fileset dir="${persist.src.dir}"/>
-        <fileset dir="${grapher.src.dir}"/>
+      <fileset dir="${grapher.src.dir}"/>
       <!-- TODO: this breaks Doclava for some reason
       <fileset dir="${struts2.src.dir}"/> -->
 
diff --git a/extensions/multibindings/src/com/google/inject/multibindings/Element.java b/extensions/multibindings/src/com/google/inject/multibindings/Element.java
index 3f68068..88a9048 100644
--- a/extensions/multibindings/src/com/google/inject/multibindings/Element.java
+++ b/extensions/multibindings/src/com/google/inject/multibindings/Element.java
@@ -38,7 +38,8 @@
 
   enum Type {
     MAPBINDER,
-    MULTIBINDER;
+    MULTIBINDER,
+    OPTIONALBINDER;
   }
 
   String setName();
diff --git a/extensions/multibindings/src/com/google/inject/multibindings/MultibindingsTargetVisitor.java b/extensions/multibindings/src/com/google/inject/multibindings/MultibindingsTargetVisitor.java
index 49e8e49..9378e19 100644
--- a/extensions/multibindings/src/com/google/inject/multibindings/MultibindingsTargetVisitor.java
+++ b/extensions/multibindings/src/com/google/inject/multibindings/MultibindingsTargetVisitor.java
@@ -22,8 +22,9 @@
  * A visitor for the multibinder extension.
  * <p>
  * If your {@link BindingTargetVisitor} implements this interface, bindings created by using
- * {@link Multibinder} or {@link MapBinder} will be visited through this interface.
- * 
+ * {@link Multibinder}, {@link MapBinder} or {@link OptionalBinderBinding} will be visited through
+ * this interface.
+ *
  * @since 3.0
  * @author sameb@google.com (Sam Berlin)
  */
@@ -38,5 +39,12 @@
    * Visits a binding created through {@link MapBinder}.
    */
   V visit(MapBinderBinding<? extends T> mapbinding);
+  
+  /**
+   * Visits a binding created through {@link OptionalBinder}.
+   * 
+   * @since 4.0
+   */
+  V visit(OptionalBinderBinding<? extends T> optionalbinding);
 
 }
diff --git a/extensions/multibindings/src/com/google/inject/multibindings/OptionalBinder.java b/extensions/multibindings/src/com/google/inject/multibindings/OptionalBinder.java
new file mode 100644
index 0000000..5d4447d
--- /dev/null
+++ b/extensions/multibindings/src/com/google/inject/multibindings/OptionalBinder.java
@@ -0,0 +1,542 @@
+/**
+ * Copyright (C) 2014 Google Inc.
+ *
+ * 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 com.google.inject.multibindings;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.inject.internal.RehashableKeys.Keys.needsRehashing;
+import static com.google.inject.internal.RehashableKeys.Keys.rehash;
+import static com.google.inject.multibindings.Multibinder.checkConfiguration;
+import static com.google.inject.util.Types.newParameterizedType;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.inject.Binder;
+import com.google.inject.Binding;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Module;
+import com.google.inject.Provider;
+import com.google.inject.TypeLiteral;
+import com.google.inject.binder.LinkedBindingBuilder;
+import com.google.inject.internal.Errors;
+import com.google.inject.multibindings.Element.Type;
+import com.google.inject.multibindings.MapBinder.RealMapBinder;
+import com.google.inject.spi.BindingTargetVisitor;
+import com.google.inject.spi.Dependency;
+import com.google.inject.spi.HasDependencies;
+import com.google.inject.spi.ProviderInstanceBinding;
+import com.google.inject.spi.ProviderLookup;
+import com.google.inject.spi.ProviderWithDependencies;
+import com.google.inject.spi.ProviderWithExtensionVisitor;
+import com.google.inject.spi.Toolable;
+import com.google.inject.util.Types;
+
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * An API to bind optional values, optionally with a default value.
+ * OptionalBinder fulfills two roles: <ol>
+ * <li>It allows a framework to define an injection point that may or
+ *     may not be bound by users.
+ * <li>It allows a framework to supply a default value that can be changed
+ *     by users.
+ * </ol>
+ * 
+ * <p>When an OptionalBinder is added, it will always supply the bindings:
+ * {@code Optional<T>} and {@code Optional<Provider<T>>}.  If
+ * {@link #setBinding} or {@link #setDefault} are called, it will also
+ * bind {@code T}.
+ * 
+ * <p>{@code setDefault} is intended for use by frameworks that need a default
+ * value.  User code can call {@code setBinding} to override the default.
+ * <b>Warning: Even if setBinding is called, the default binding
+ * will still exist in the object graph.  If it is a singleton, it will be
+ * instantiated in {@code Stage.PRODUCTION}.</b>
+ * 
+ * <p>If setDefault or setBinding are linked to Providers, the Provider may return
+ * {@code null}.  If it does, the Optional bindings will be absent.  Binding
+ * setBinding to a Provider that returns null will not cause OptionalBinder
+ * to fall back to the setDefault binding.
+ * 
+ * <p>If neither setDefault nor setBinding are called, the optionals will be
+ * absent.  Otherwise, the optionals will return present if they are bound
+ * to a non-null value.
+ *
+ * <p>Values are resolved at injection time. If a value is bound to a
+ * provider, that provider's get method will be called each time the optional
+ * is injected (unless the binding is also scoped, or an optional of provider is
+ * injected).
+ * 
+ * <p>Annotations are used to create different optionals of the same key/value
+ * type. Each distinct annotation gets its own independent binding.
+ *  
+ * <pre><code>
+ * public class FrameworkModule extends AbstractModule {
+ *   protected void configure() {
+ *     OptionalBinder.newOptionalBinder(binder(), Renamer.class);
+ *   }
+ * }</code></pre>
+ *
+ * <p>With this module, an {@link Optional}{@code <Renamer>} can now be
+ * injected.  With no other bindings, the optional will be absent.  However,
+ * once a user adds a binding:
+ * 
+ * <pre><code>
+ * public class UserRenamerModule extends AbstractModule {
+ *   protected void configure() {
+ *     OptionalBinder.newOptionalBinder(binder(), Renamer.class)
+ *         .setBinding().to(ReplacingRenamer.class);
+ *   }
+ * }</code></pre>
+ * .. then the {@code Optional<Renamer>} will be present and supply the
+ * ReplacingRenamer.
+ * 
+ * <p>Default values can be supplied using:
+ * <pre><code>
+ * public class FrameworkModule extends AbstractModule {
+ *   protected void configure() {
+ *     OptionalBinder.newOptionalBinder(binder(), Key.get(String.class, LookupUrl.class))
+ *         .setDefault().to(DEFAULT_LOOKUP_URL);
+ *   }
+ * }</code></pre>
+ * With the above module, code can inject an {@code @LookupUrl String} and it
+ * will supply the DEFAULT_LOOKUP_URL.  A user can change this value by binding
+ * <pre><code>
+ * public class UserLookupModule extends AbstractModule {
+ *   protected void configure() {
+ *     OptionalBinder.newOptionalBinder(binder(), Key.get(String.class, LookupUrl.class))
+ *         .setBinding().to(CUSTOM_LOOKUP_URL);
+ *   }
+ * }</code></pre>
+ * ... which will override the default value.
+ *
+ * @author sameb@google.com (Sam Berlin)
+ */
+public abstract class OptionalBinder<T> {
+  private OptionalBinder() {}
+
+  public static <T> OptionalBinder<T> newOptionalBinder(Binder binder, Class<T> type) {
+    return newOptionalBinder(binder, Key.get(type));
+  }
+  
+  public static <T> OptionalBinder<T> newOptionalBinder(Binder binder, TypeLiteral<T> type) {
+    return newOptionalBinder(binder, Key.get(type));
+  }
+  
+  public static <T> OptionalBinder<T> newOptionalBinder(Binder binder, Key<T> type) {
+    binder = binder.skipSources(OptionalBinder.class, RealOptionalBinder.class);
+    RealOptionalBinder<T> optionalBinder = new RealOptionalBinder<T>(binder, type);
+    binder.install(optionalBinder);
+    return optionalBinder;
+  }
+
+  @SuppressWarnings("unchecked")
+  static <T> TypeLiteral<Optional<T>> optionalOf(
+      TypeLiteral<T> type) {
+    return (TypeLiteral<Optional<T>>) TypeLiteral.get(
+        Types.newParameterizedType(Optional.class,  type.getType()));
+  }
+
+  @SuppressWarnings("unchecked")
+  static <T> TypeLiteral<Optional<javax.inject.Provider<T>>> optionalOfJavaxProvider(
+      TypeLiteral<T> type) {
+    return (TypeLiteral<Optional<javax.inject.Provider<T>>>) TypeLiteral.get(
+        Types.newParameterizedType(Optional.class,
+            newParameterizedType(javax.inject.Provider.class, type.getType())));
+  }
+
+  @SuppressWarnings("unchecked")
+  static <T> TypeLiteral<Optional<Provider<T>>> optionalOfProvider(TypeLiteral<T> type) {
+    return (TypeLiteral<Optional<Provider<T>>>) TypeLiteral.get(Types.newParameterizedType(
+        Optional.class, newParameterizedType(Provider.class, type.getType())));
+  }
+
+  /**
+   * Returns a binding builder used to set the default value that will be injected.
+   * The binding set by this method will be ignored if {@link #setBinding} is called.
+   * 
+   * <p>It is an error to call this method without also calling one of the {@code to}
+   * methods on the returned binding builder. 
+   */
+  public abstract LinkedBindingBuilder<T> setDefault();
+
+
+  /**
+   * Returns a binding builder used to set the actual value that will be injected.
+   * This overrides any binding set by {@link #setDefault}.
+   * 
+   * <p>It is an error to call this method without also calling one of the {@code to}
+   * methods on the returned binding builder. 
+   */
+  public abstract LinkedBindingBuilder<T> setBinding();
+  
+  enum Source { DEFAULT, ACTUAL }
+
+  /**
+   * The actual OptionalBinder plays several roles.  It implements Module to hide that
+   * fact from the public API, and installs the various bindings that are exposed to the user.
+   */
+  static final class RealOptionalBinder<T> extends OptionalBinder<T> implements Module {
+    private final Key<T> typeKey;
+    private final Key<Optional<T>> optionalKey;
+    private final Key<Optional<javax.inject.Provider<T>>> optionalJavaxProviderKey;
+    private final Key<Optional<Provider<T>>> optionalProviderKey;
+    private final Key<Map<Source, Provider<T>>> mapKey;
+    private final RealMapBinder<Source, T> mapBinder;
+    private final Set<Dependency<?>> dependencies;
+    private final Provider<Optional<Provider<T>>> optionalProviderT;
+    
+
+    /** the target injector's binder. non-null until initialization, null afterwards */
+    private Binder binder;
+    /** the default binding, for the SPI. */
+    private Binding<?> defaultBinding;
+    /** the actual binding, for the SPI */
+    private Binding<?> actualBinding;
+
+    private RealOptionalBinder(Binder binder, Key<T> typeKey) {
+      this.binder = binder;
+      this.typeKey = checkNotNull(typeKey);
+      TypeLiteral<T> literal = typeKey.getTypeLiteral();
+      this.optionalKey = typeKey.ofType(optionalOf(literal));
+      this.optionalJavaxProviderKey = typeKey.ofType(optionalOfJavaxProvider(literal));
+      this.optionalProviderKey = typeKey.ofType(optionalOfProvider(literal));
+      this.mapKey =
+          typeKey.ofType(MapBinder.mapOfProviderOf(TypeLiteral.get(Source.class), literal));
+      this.dependencies = ImmutableSet.<Dependency<?>>of(Dependency.get(mapKey));
+      this.optionalProviderT = binder.getProvider(optionalProviderKey);
+      if (typeKey.getAnnotation() != null) {
+        this.mapBinder = (RealMapBinder<Source, T>) MapBinder.newMapBinder(binder,
+            TypeLiteral.get(Source.class), typeKey.getTypeLiteral(), typeKey.getAnnotation());
+      } else if (typeKey.getAnnotationType() != null) {
+        this.mapBinder = (RealMapBinder<Source, T>) MapBinder.newMapBinder(binder,
+            TypeLiteral.get(Source.class), typeKey.getTypeLiteral(), typeKey.getAnnotationType());
+      } else {
+        this.mapBinder = (RealMapBinder<Source, T>) MapBinder.newMapBinder(binder,
+            TypeLiteral.get(Source.class), typeKey.getTypeLiteral());
+      }
+      mapBinder.updateDuplicateKeyMessage(Source.DEFAULT, "OptionalBinder for "
+          + Errors.convert(typeKey)
+          + " called with different setDefault values, from bindings:\n");
+      mapBinder.updateDuplicateKeyMessage(Source.ACTUAL, "OptionalBinder for "
+          + Errors.convert(typeKey)
+          + " called with different setBinding values, from bindings:\n");
+    }
+    
+    /**
+     * Adds a binding for T. Multiple calls to this are safe, and will be collapsed as duplicate
+     * bindings.
+     */
+    private void addDirectTypeBinding(Binder binder) {
+      binder.bind(typeKey).toProvider(new RealOptionalBinderProviderWithDependencies<T>(typeKey) {
+        public T get() {
+          Optional<Provider<T>> optional = optionalProviderT.get();
+          if (optional.isPresent()) {
+            return optional.get().get();
+          }
+          // Let Guice handle blowing up if the injection point doesn't have @Nullable
+          // (If it does have @Nullable, that's fine.  This would only happen if
+          //  setBinding/setDefault themselves were bound to 'null').
+          return null; 
+        }
+
+        public Set<Dependency<?>> getDependencies() {
+          return dependencies;
+        }
+      });
+    }
+
+    @Override public LinkedBindingBuilder<T> setDefault() {
+      checkConfiguration(!isInitialized(), "already initialized");
+      
+      addDirectTypeBinding(binder);
+
+      RealElement.BindingBuilder<T> valueBinding = RealElement.addBinding(binder,
+          Element.Type.OPTIONALBINDER, typeKey.getTypeLiteral(), RealElement.nameOf(typeKey));
+      Key<T> valueKey = Key.get(typeKey.getTypeLiteral(), valueBinding.getAnnotation());
+      mapBinder.addBinding(Source.DEFAULT).toProvider(
+          new ValueProvider<T>(valueKey, binder.getProvider(valueKey)));
+      return valueBinding;
+    }
+
+    @Override public LinkedBindingBuilder<T> setBinding() {
+      checkConfiguration(!isInitialized(), "already initialized");
+      
+      addDirectTypeBinding(binder);
+
+      RealElement.BindingBuilder<T> valueBinding = RealElement.addBinding(binder,
+          Element.Type.OPTIONALBINDER, typeKey.getTypeLiteral(), RealElement.nameOf(typeKey));
+      Key<T> valueKey = Key.get(typeKey.getTypeLiteral(), valueBinding.getAnnotation());
+      mapBinder.addBinding(Source.ACTUAL).toProvider(
+          new ValueProvider<T>(valueKey, binder.getProvider(valueKey)));
+      return valueBinding;
+    }
+    
+    /**
+     * Traverses through the dependencies of the providers in order to get to the user's binding.
+     */
+    private Binding<?> getBindingFromMapProvider(Injector injector, Provider<T> mapProvider) {
+      HasDependencies deps = (HasDependencies) mapProvider;
+      Key<?> depKey = Iterables.getOnlyElement(deps.getDependencies()).getKey();
+      // The dep flow is (and will stay this way, until we change the internals) --
+      //    Key[type=Provider<java.lang.String>, annotation=@Element(type=MAPBINDER)]
+      // -> Key[type=String, annotation=@Element(type=MAPBINDER)]
+      // -> Key[type=Provider<String>, annotation=@Element(type=OPTIONALBINDER)]
+      // -> Key[type=String, annotation=@Element(type=OPTIONALBINDER)]
+      // The last one points to the user's binding.
+      for (int i = 0; i < 3; i++) {
+        deps = (HasDependencies) injector.getBinding(depKey);
+        depKey = Iterables.getOnlyElement(deps.getDependencies()).getKey();
+      }
+      return injector.getBinding(depKey);
+    }
+
+    public void configure(Binder binder) {
+      checkConfiguration(!isInitialized(), "OptionalBinder was already initialized");
+
+      final Provider<Map<Source, Provider<T>>> mapProvider = binder.getProvider(mapKey);
+      binder.bind(optionalProviderKey).toProvider(
+          new RealOptionalBinderProviderWithDependencies<Optional<Provider<T>>>(typeKey) {
+        private Optional<Provider<T>> optional;
+
+        @Toolable @Inject void initialize(Injector injector) {
+          RealOptionalBinder.this.binder = null;
+          Map<Source, Provider<T>> map = mapProvider.get();
+          // Map might be null if duplicates prevented MapBinder from initializing
+          if (map != null) {
+            if (map.containsKey(Source.ACTUAL)) {
+              // TODO(sameb): Consider exposing an option that will allow
+              // ACTUAL to fallback to DEFAULT if ACTUAL's provider returns null.
+              // Right now, an ACTUAL binding can convert from present -> absent
+              // if it's bound to a provider that returns null.
+              optional = Optional.fromNullable(map.get(Source.ACTUAL)); 
+            } else if (map.containsKey(Source.DEFAULT)) {
+              optional = Optional.fromNullable(map.get(Source.DEFAULT));
+            } else {
+              optional = Optional.absent();
+            }
+            
+            // Also set up the bindings for the SPI.
+            if (map.containsKey(Source.ACTUAL)) {
+              actualBinding = getBindingFromMapProvider(injector, map.get(Source.ACTUAL));
+            }
+            if (map.containsKey(Source.DEFAULT)) {
+              defaultBinding = getBindingFromMapProvider(injector, map.get(Source.DEFAULT));
+            }
+          }
+        }
+        
+        public Optional<Provider<T>> get() {
+          return optional;
+        }
+
+        public Set<Dependency<?>> getDependencies() {
+          return dependencies;
+        }
+      });
+      
+      // Optional is immutable, so it's safe to expose Optional<Provider<T>> as
+      // Optional<javax.inject.Provider<T>> (since Guice provider implements javax Provider).
+      @SuppressWarnings({"unchecked", "cast"})
+      Key massagedOptionalProviderKey = (Key) optionalProviderKey;
+      binder.bind(optionalJavaxProviderKey).to(massagedOptionalProviderKey);
+
+      binder.bind(optionalKey).toProvider(new RealOptionalKeyProvider());
+    }
+
+    private class RealOptionalKeyProvider
+        extends RealOptionalBinderProviderWithDependencies<Optional<T>>
+        implements ProviderWithExtensionVisitor<Optional<T>>,
+            OptionalBinderBinding<Optional<T>>,
+            Provider<Optional<T>> {
+      RealOptionalKeyProvider() {
+        super(mapKey);
+      }
+      
+      public Optional<T> get() {
+        Optional<Provider<T>> optional = optionalProviderT.get();
+        if (optional.isPresent()) {
+          return Optional.fromNullable(optional.get().get());
+        } else {
+          return Optional.absent();
+        }
+      }
+
+      public Set<Dependency<?>> getDependencies() {
+        return dependencies;
+      }
+
+      @SuppressWarnings("unchecked")
+      public <B, R> R acceptExtensionVisitor(BindingTargetVisitor<B, R> visitor,
+          ProviderInstanceBinding<? extends B> binding) {
+        if (visitor instanceof MultibindingsTargetVisitor) {
+          return ((MultibindingsTargetVisitor<Optional<T>, R>) visitor).visit(this);
+        } else {
+          return visitor.visit(binding);
+        }
+      }
+
+      public Key<Optional<T>> getKey() {
+        return optionalKey;
+      }
+      
+      public Binding<?> getActualBinding() {
+        if (isInitialized()) {
+          return actualBinding;
+        } else {
+          throw new UnsupportedOperationException(
+              "getActualBinding() not supported from Elements.getElements, requires an Injector.");
+        }
+      }
+      
+      public Binding<?> getDefaultBinding() {
+        if (isInitialized()) {
+          return defaultBinding;
+        } else {
+          throw new UnsupportedOperationException(
+              "getDefaultBinding() not supported from Elements.getElements, requires an Injector.");
+        }
+      }
+
+      public boolean containsElement(com.google.inject.spi.Element element) {
+        if (mapBinder.containsElement(element)) {
+          return true;
+        } else {
+          Key<?> elementKey;
+          if (element instanceof Binding) {
+            elementKey = ((Binding<?>) element).getKey();
+          } else if (element instanceof ProviderLookup) {
+            elementKey = ((ProviderLookup<?>) element).getKey();
+          } else {
+            return false; // cannot match;
+          }
+
+          return elementKey.equals(optionalKey)
+              || elementKey.equals(optionalProviderKey)
+              || elementKey.equals(optionalJavaxProviderKey)
+              || matchesTypeKey(element, elementKey)
+              || matchesUserBinding(elementKey);
+        }
+      }
+    }
+    
+    /** Returns true if the key & element indicate they were bound by this OptionalBinder. */
+    private boolean matchesTypeKey(com.google.inject.spi.Element element, Key<?> elementKey) {
+      // Just doing .equals(typeKey) isn't enough, because the user can bind that themselves.
+      return elementKey.equals(typeKey)
+          && element instanceof ProviderInstanceBinding
+          && (((ProviderInstanceBinding) element)
+              .getUserSuppliedProvider() instanceof RealOptionalBinderProviderWithDependencies);
+    }
+
+    /** Returns true if the key indicates this is a user bound value for the optional binder. */
+    private boolean matchesUserBinding(Key<?> elementKey) {
+      return elementKey.getAnnotation() instanceof Element
+          && ((Element) elementKey.getAnnotation()).setName().equals(RealElement.nameOf(typeKey))
+          && ((Element) elementKey.getAnnotation()).type() == Type.OPTIONALBINDER
+          && elementKey.getTypeLiteral().equals(typeKey.getTypeLiteral());
+    }
+
+    private boolean isInitialized() {
+      return binder == null;
+    }
+
+    @Override public boolean equals(Object o) {
+      return o instanceof RealOptionalBinder
+          && ((RealOptionalBinder<?>) o).mapKey.equals(mapKey);
+    }
+
+    @Override public int hashCode() {
+      return mapKey.hashCode();
+    }
+
+    /** A Provider that bases equality & hashcodes off another key. */
+    private static final class ValueProvider<T> implements ProviderWithDependencies<T> {
+      private final Provider<T> provider;
+      private volatile Key<T> key;
+
+      private ValueProvider(Key<T> key, Provider<T> provider) {
+        this.key = key;
+        this.provider = provider;
+      }
+      
+      public T get() {
+        return provider.get();
+      }
+      
+      public Set<Dependency<?>> getDependencies() {
+        return ((HasDependencies) provider).getDependencies();
+      }
+
+      private Key<T> getCurrentKey() {
+        // Every time, check if the key needs rehashing.
+        // If so, update the field as an optimization for next time.
+        Key<T> currentKey = key;
+        if (needsRehashing(currentKey)) {
+          currentKey = rehash(currentKey);
+          key = currentKey;
+        }
+        return currentKey;
+      }
+
+      /**
+       * Equality is based on the key (which includes the target information, because of how
+       * RealElement works). This lets duplicate bindings collapse.
+       */
+      @Override public boolean equals(Object obj) {
+        return obj instanceof ValueProvider
+            && getCurrentKey().equals(((ValueProvider) obj).getCurrentKey());
+      }
+
+      /** We only use the hashcode of the typeliteral, which can't change. */
+      @Override public int hashCode() {
+        return key.getTypeLiteral().hashCode();
+      }
+
+      @Override public String toString() {
+        return provider.toString();
+      }
+    }
+
+    /**
+     * A base class for ProviderWithDependencies that need equality based on a specific object.
+     */
+    private abstract static class RealOptionalBinderProviderWithDependencies<T> implements
+        ProviderWithDependencies<T> {
+      private final Object equality;
+
+      public RealOptionalBinderProviderWithDependencies(Object equality) {
+        this.equality = equality;
+      }
+
+      @Override
+      public boolean equals(Object obj) {
+        return this.getClass() == obj.getClass()
+            && equality.equals(((RealOptionalBinderProviderWithDependencies<?>) obj).equality);
+      }
+
+      @Override
+      public int hashCode() {
+        return equality.hashCode();
+      }
+    }
+  }
+}
diff --git a/extensions/multibindings/src/com/google/inject/multibindings/OptionalBinderBinding.java b/extensions/multibindings/src/com/google/inject/multibindings/OptionalBinderBinding.java
new file mode 100644
index 0000000..fbbbc36
--- /dev/null
+++ b/extensions/multibindings/src/com/google/inject/multibindings/OptionalBinderBinding.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright (C) 2014 Google Inc.
+ *
+ * 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 com.google.inject.multibindings;
+
+import com.google.inject.Binding;
+import com.google.inject.Key;
+import com.google.inject.spi.Element;
+import com.google.inject.spi.Elements;
+
+/**
+ * A binding for a OptionalBinder.
+ * 
+ * <p>Although OptionalBinders may be injected through a variety of types
+ * {@code T}, {@code Optional<T>}, {@code Optional<Provider<T>>}, etc..), an
+ * OptionalBinderBinding exists only on the Binding associated with the
+ * {@code Optional<T>} key.  Other bindings can be validated to be derived from this
+ * OptionalBinderBinding using {@link #containsElement}.
+ * 
+ * @param <T> The fully qualified type of the optional binding, including Optional.
+ *        For example: {@code Optional<String>}.
+ * 
+ * @since 4.0
+ * @author sameb@google.com (Sam Berlin)
+ */
+public interface OptionalBinderBinding<T> {
+
+  /** Returns the {@link Key} for this binding. */
+  Key<T> getKey();
+  
+  /**
+   * Returns the default binding (set by {@link OptionalBinder#setDefault}) if one exists or null
+   * if no default binding is set. This will throw {@link UnsupportedOperationException} if it is
+   * called on an element retrieved from {@link Elements#getElements}.
+   * <p>
+   * The Binding's type will always match the type Optional's generic type. For example, if getKey
+   * returns a key of <code>Optional&lt;String></code>, then this will always return a
+   * <code>Binding&lt;String></code>.
+   */
+  Binding<?> getDefaultBinding();
+  
+  /**
+   * Returns the actual binding (set by {@link OptionalBinder#setBinding}) or null if not set.
+   * This will throw {@link UnsupportedOperationException} if it is called on an element retrieved
+   * from {@link Elements#getElements}.
+   * <p>
+   * The Binding's type will always match the type Optional's generic type. For example, if getKey
+   * returns a key of <code>Optional&lt;String></code>, then this will always return a
+   * <code>Binding&lt;String></code>.
+   */
+  Binding<?> getActualBinding();
+
+  /**
+   * Returns true if this OptionalBinder contains the given Element in order to build the optional
+   * binding or uses the given Element in order to support building and injecting its data. This
+   * will work for OptionalBinderBinding retrieved from an injector and
+   * {@link Elements#getElements}. Usually this is only necessary if you are working with elements
+   * retrieved from modules (without an Injector), otherwise {@link #getDefaultBinding} and
+   * {@link #getActualBinding} are better options.
+   */
+  boolean containsElement(Element element);
+}
diff --git a/extensions/multibindings/test/com/google/inject/multibindings/Collector.java b/extensions/multibindings/test/com/google/inject/multibindings/Collector.java
index 6287d75..18f2527 100644
--- a/extensions/multibindings/test/com/google/inject/multibindings/Collector.java
+++ b/extensions/multibindings/test/com/google/inject/multibindings/Collector.java
@@ -18,10 +18,11 @@
 
 import com.google.inject.spi.DefaultBindingTargetVisitor;
 
-class Collector extends DefaultBindingTargetVisitor<Object, Object>
-    implements MultibindingsTargetVisitor<Object, Object> {
+class Collector extends DefaultBindingTargetVisitor<Object, Object> implements
+    MultibindingsTargetVisitor<Object, Object> {
   MapBinderBinding<? extends Object> mapbinding;
   MultibinderBinding<? extends Object> setbinding;
+  OptionalBinderBinding<? extends Object> optionalbinding;
 
   @Override
   public Object visit(MapBinderBinding<? extends Object> mapbinding) {
@@ -34,4 +35,10 @@
    this.setbinding = multibinding;
    return null;
   }
+  
+  @Override
+  public Object visit(OptionalBinderBinding<? extends Object> optionalbinding) {
+    this.optionalbinding = optionalbinding;
+    return null;
+  }
 }
diff --git a/extensions/multibindings/test/com/google/inject/multibindings/MultibinderTest.java b/extensions/multibindings/test/com/google/inject/multibindings/MultibinderTest.java
index 70be338..9494e5e 100644
--- a/extensions/multibindings/test/com/google/inject/multibindings/MultibinderTest.java
+++ b/extensions/multibindings/test/com/google/inject/multibindings/MultibinderTest.java
@@ -25,7 +25,11 @@
 import static com.google.inject.name.Names.named;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
+import com.google.common.base.Optional;
+import com.google.common.base.Predicates;
+import com.google.common.collect.FluentIterable;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
@@ -47,7 +51,6 @@
 import com.google.inject.internal.RehashableKeys;
 import com.google.inject.name.Named;
 import com.google.inject.name.Names;
-import com.google.inject.spi.DefaultBindingTargetVisitor;
 import com.google.inject.spi.Dependency;
 import com.google.inject.spi.Element;
 import com.google.inject.spi.Elements;
@@ -83,6 +86,8 @@
  */
 public class MultibinderTest extends TestCase {
 
+  final TypeLiteral<Optional<String>> optionalOfString =
+      new TypeLiteral<Optional<String>>() {};
   final TypeLiteral<Map<String, String>> mapOfStringString =
       new TypeLiteral<Map<String, String>>() {};
   final TypeLiteral<Set<String>> setOfString = new TypeLiteral<Set<String>>() {};
@@ -1056,10 +1061,17 @@
 
         MapBinder.newMapBinder(binder(), String.class, String.class)
             .addBinding("B").toInstance("b");
+
+        OptionalBinder.newOptionalBinder(binder(), String.class)
+            .setDefault().toInstance("C");
+        OptionalBinder.newOptionalBinder(binder(), String.class)
+            .setBinding().toInstance("D");
       }
     });
 
-    assertEquals(ImmutableSet.<String>of("A"), injector.getInstance(Key.get(setOfString)));
+    assertEquals(ImmutableSet.of("A"), injector.getInstance(Key.get(setOfString)));
+    assertEquals(ImmutableMap.of("B", "b"), injector.getInstance(Key.get(mapOfStringString)));
+    assertEquals(Optional.of("D"), injector.getInstance(Key.get(optionalOfString)));
   }
 
   // See issue 670
@@ -1071,6 +1083,9 @@
 
         MapBinder.newMapBinder(binder(), String.class, String.class)
             .addBinding("B").toInstance("b");
+        
+        OptionalBinder.newOptionalBinder(binder(), String.class)
+            .setDefault().toInstance("C");
       }
     });
     Collector collector = new Collector();
@@ -1082,40 +1097,36 @@
     setbinding.acceptTargetVisitor(collector);
     assertNotNull(collector.setbinding);
 
-    // Capture the value bindings and assert we have them right --
-    // they'll always be in the right order because we preserve
-    // binding order.
-    List<Binding<String>> bindings = injector.findBindingsByType(stringType);
-    assertEquals(2, bindings.size());
+    Binding<Optional<String>> optionalbinding = injector.getBinding(Key.get(optionalOfString));
+    optionalbinding.acceptTargetVisitor(collector);
+    assertNotNull(collector.optionalbinding);
+
+    // There should only be three instance bindings for string types
+    // (but because of the OptionalBinder, there's 2 ProviderInstanceBindings also).
+    // We also know the InstanceBindings will be in the order: A, b, C because that's
+    // how we bound them, and binding order is preserved.
+    List<Binding<String>> bindings = FluentIterable.from(injector.findBindingsByType(stringType))
+        .filter(Predicates.instanceOf(InstanceBinding.class))
+        .toList();
+    assertEquals(bindings.toString(), 3, bindings.size());
     Binding<String> a = bindings.get(0);
     Binding<String> b = bindings.get(1);
-    assertEquals("A", ((InstanceBinding<String>)a).getInstance());
-    assertEquals("b", ((InstanceBinding<String>)b).getInstance());
+    Binding<String> c = bindings.get(2);
+    assertEquals("A", ((InstanceBinding<String>) a).getInstance());
+    assertEquals("b", ((InstanceBinding<String>) b).getInstance());
+    assertEquals("C", ((InstanceBinding<String>) c).getInstance());
 
-    // Now make sure "A" belongs only to the set binding,
-    // and "b" only belongs to the map binding.
+    // Make sure the correct elements belong to their own sets.
     assertFalse(collector.mapbinding.containsElement(a));
     assertTrue(collector.mapbinding.containsElement(b));
+    assertFalse(collector.mapbinding.containsElement(c));
 
     assertTrue(collector.setbinding.containsElement(a));
     assertFalse(collector.setbinding.containsElement(b));
-  }
-
-  private static class Collector extends DefaultBindingTargetVisitor<Object, Object> implements
-      MultibindingsTargetVisitor<Object, Object> {
-    MapBinderBinding<? extends Object> mapbinding;
-    MultibinderBinding<? extends Object> setbinding;
-
-    @Override
-    public Object visit(MapBinderBinding<? extends Object> mapbinding) {
-      this.mapbinding = mapbinding;
-      return null;
-    }
-
-    @Override
-    public Object visit(MultibinderBinding<? extends Object> multibinding) {
-     this.setbinding = multibinding;
-     return null;
-    }
+    assertFalse(collector.setbinding.containsElement(c));
+    
+    assertFalse(collector.optionalbinding.containsElement(a));
+    assertFalse(collector.optionalbinding.containsElement(b));
+    assertTrue(collector.optionalbinding.containsElement(c));
   }
 }
diff --git a/extensions/multibindings/test/com/google/inject/multibindings/OptionalBinderTest.java b/extensions/multibindings/test/com/google/inject/multibindings/OptionalBinderTest.java
new file mode 100644
index 0000000..1393e0c
--- /dev/null
+++ b/extensions/multibindings/test/com/google/inject/multibindings/OptionalBinderTest.java
@@ -0,0 +1,918 @@
+/**
+ * Copyright (C) 2014 Google Inc.
+ *
+ * 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 com.google.inject.multibindings;
+
+import static com.google.inject.Asserts.assertContains;
+import static com.google.inject.multibindings.SpiUtils.assertOptionalVisitor;
+import static com.google.inject.multibindings.SpiUtils.instance;
+import static com.google.inject.multibindings.SpiUtils.linked;
+import static com.google.inject.multibindings.SpiUtils.providerInstance;
+import static com.google.inject.multibindings.SpiUtils.providerKey;
+import static com.google.inject.name.Names.named;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.inject.AbstractModule;
+import com.google.inject.Binding;
+import com.google.inject.BindingAnnotation;
+import com.google.inject.CreationException;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Module;
+import com.google.inject.Provider;
+import com.google.inject.Provides;
+import com.google.inject.Scopes;
+import com.google.inject.TypeLiteral;
+import com.google.inject.internal.RehashableKeys;
+import com.google.inject.multibindings.SpiUtils.VisitType;
+import com.google.inject.name.Named;
+import com.google.inject.name.Names;
+import com.google.inject.spi.Dependency;
+import com.google.inject.spi.Elements;
+import com.google.inject.spi.HasDependencies;
+import com.google.inject.spi.InstanceBinding;
+import com.google.inject.util.Modules;
+import com.google.inject.util.Providers;
+
+import junit.framework.TestCase;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * @author sameb@google.com (Sam Berlin)
+ */
+public class OptionalBinderTest extends TestCase {
+
+  final Key<String> stringKey = Key.get(String.class);
+  final TypeLiteral<Optional<String>> optionalOfString = new TypeLiteral<Optional<String>>() {};
+  final TypeLiteral<Optional<Provider<String>>> optionalOfProviderString =
+      new TypeLiteral<Optional<Provider<String>>>() {};
+  final TypeLiteral<Optional<javax.inject.Provider<String>>> optionalOfJavaxProviderString =
+      new TypeLiteral<Optional<javax.inject.Provider<String>>>() {};
+
+  final Key<Integer> intKey = Key.get(Integer.class);
+  final TypeLiteral<Optional<Integer>> optionalOfInteger = new TypeLiteral<Optional<Integer>>() {};
+  final TypeLiteral<Optional<Provider<Integer>>> optionalOfProviderInteger =
+      new TypeLiteral<Optional<Provider<Integer>>>() {};
+  final TypeLiteral<Optional<javax.inject.Provider<Integer>>> optionalOfJavaxProviderInteger =
+      new TypeLiteral<Optional<javax.inject.Provider<Integer>>>() {};
+      
+  final TypeLiteral<List<String>> listOfStrings = new TypeLiteral<List<String>>() {};
+  
+  public void testTypeNotBoundByDefault() {
+    Module module = new AbstractModule() {
+      @Override protected void configure() {
+        OptionalBinder.newOptionalBinder(binder(), String.class);
+        requireBinding(new Key<Optional<String>>() {}); // the above specifies this.
+        requireBinding(String.class); // but it doesn't specify this.
+        binder().requireExplicitBindings(); // need to do this, otherwise String will JIT
+      }
+    };
+
+    try {
+      Guice.createInjector(module);
+      fail();
+    } catch (CreationException ce) {
+      assertContains(ce.getMessage(),
+          "1) Explicit bindings are required and java.lang.String is not explicitly bound.");
+      assertEquals(1, ce.getErrorMessages().size());
+    }
+  } 
+  
+  public void testOptionalIsAbsentByDefault() {
+    Module module = new AbstractModule() {
+      @Override protected void configure() {
+        OptionalBinder.newOptionalBinder(binder(), String.class);
+      }
+    };
+
+    Injector injector = Guice.createInjector(module);
+    Optional<String> optional = injector.getInstance(Key.get(optionalOfString));
+    assertFalse(optional.isPresent());
+    
+    Optional<Provider<String>> optionalP = injector.getInstance(Key.get(optionalOfProviderString));
+    assertFalse(optionalP.isPresent());
+    
+    Optional<javax.inject.Provider<String>> optionalJxP =
+        injector.getInstance(Key.get(optionalOfJavaxProviderString));
+    assertFalse(optionalJxP.isPresent());
+    
+    assertOptionalVisitor(stringKey, setOf(module), VisitType.BOTH, 0, null, null);
+  }
+  
+  public void testAbsentWithUserBoundValue() {
+    Module module = new AbstractModule() {
+      @Override protected void configure() {
+        OptionalBinder.newOptionalBinder(binder(), String.class);
+      }
+      @Provides String provideString() { return "foo"; }
+    };
+
+    Injector injector = Guice.createInjector(module);
+    assertEquals("foo", injector.getInstance(String.class));
+    
+    Optional<String> optional = injector.getInstance(Key.get(optionalOfString));
+    assertFalse(optional.isPresent());
+    
+    Optional<Provider<String>> optionalP = injector.getInstance(Key.get(optionalOfProviderString));
+    assertFalse(optionalP.isPresent());
+    
+    Optional<javax.inject.Provider<String>> optionalJxP =
+        injector.getInstance(Key.get(optionalOfJavaxProviderString));
+    assertFalse(optionalJxP.isPresent());
+    
+    assertOptionalVisitor(stringKey, setOf(module), VisitType.BOTH, 0, null, null);
+  }
+  
+  public void testSetDefault() {
+    Module module = new AbstractModule() {
+      @Override protected void configure() {
+        OptionalBinder.newOptionalBinder(binder(), String.class).setDefault().toInstance("a");
+      }
+    };
+    Injector injector = Guice.createInjector(module);
+    assertEquals("a", injector.getInstance(String.class));
+    
+    Optional<String> optional = injector.getInstance(Key.get(optionalOfString));
+    assertTrue(optional.isPresent());
+    assertEquals("a", optional.get());
+    
+    Optional<Provider<String>> optionalP = injector.getInstance(Key.get(optionalOfProviderString));
+    assertTrue(optionalP.isPresent());
+    assertEquals("a", optionalP.get().get());
+    
+    Optional<javax.inject.Provider<String>> optionalJxP =
+        injector.getInstance(Key.get(optionalOfJavaxProviderString));
+    assertTrue(optionalJxP.isPresent());
+    assertEquals("a", optionalJxP.get().get());
+
+    assertOptionalVisitor(stringKey, setOf(module), VisitType.BOTH, 0, instance("a"), null);
+  }
+  
+  public void testSetBinding() {
+    Module module = new AbstractModule() {
+      @Override protected void configure() {
+        OptionalBinder.newOptionalBinder(binder(), String.class).setBinding().toInstance("a");
+      }
+    };
+    Injector injector = Guice.createInjector(module);
+    assertEquals("a", injector.getInstance(String.class));
+    
+    Optional<String> optional = injector.getInstance(Key.get(optionalOfString));
+    assertTrue(optional.isPresent());
+    assertEquals("a", optional.get());
+    
+    Optional<Provider<String>> optionalP = injector.getInstance(Key.get(optionalOfProviderString));
+    assertTrue(optionalP.isPresent());
+    assertEquals("a", optionalP.get().get());
+    
+    Optional<javax.inject.Provider<String>> optionalJxP =
+        injector.getInstance(Key.get(optionalOfJavaxProviderString));
+    assertTrue(optionalJxP.isPresent());
+    assertEquals("a", optionalJxP.get().get());
+    
+    assertOptionalVisitor(stringKey, setOf(module), VisitType.BOTH, 0, null, instance("a"));
+  }
+  
+  public void testSetBindingOverridesDefault() {
+    Module module = new AbstractModule() {
+      @Override protected void configure() {
+        OptionalBinder<String> optionalBinder =
+            OptionalBinder.newOptionalBinder(binder(), String.class);
+        optionalBinder.setDefault().toInstance("a");
+        optionalBinder.setBinding().toInstance("b");
+      }
+    };
+    Injector injector = Guice.createInjector(module);
+    assertEquals("b", injector.getInstance(String.class));
+    
+    Optional<String> optional = injector.getInstance(Key.get(optionalOfString));
+    assertTrue(optional.isPresent());
+    assertEquals("b", optional.get());
+    
+    Optional<Provider<String>> optionalP = injector.getInstance(Key.get(optionalOfProviderString));
+    assertTrue(optionalP.isPresent());
+    assertEquals("b", optionalP.get().get());
+    
+    Optional<javax.inject.Provider<String>> optionalJxP =
+        injector.getInstance(Key.get(optionalOfJavaxProviderString));
+    assertTrue(optionalJxP.isPresent());
+    assertEquals("b", optionalJxP.get().get());
+    
+    assertOptionalVisitor(stringKey,
+        setOf(module),
+        VisitType.BOTH,
+        0,
+        instance("a"),
+        instance("b"));
+  }
+  
+  public void testSpreadAcrossModules() {
+    Module module1 = new AbstractModule() {
+      @Override protected void configure() {
+        OptionalBinder.newOptionalBinder(binder(), String.class);
+      }
+    };
+    Module module2 = new AbstractModule() {
+      @Override protected void configure() {
+        OptionalBinder.newOptionalBinder(binder(), String.class).setDefault().toInstance("a");
+      }
+    };
+    Module module3 = new AbstractModule() {
+      @Override protected void configure() {
+        OptionalBinder.newOptionalBinder(binder(), String.class).setBinding().toInstance("b");
+      }
+    };
+
+    Injector injector = Guice.createInjector(module1, module2, module3);
+    assertEquals("b", injector.getInstance(String.class));
+    
+    Optional<String> optional = injector.getInstance(Key.get(optionalOfString));
+    assertTrue(optional.isPresent());
+    assertEquals("b", optional.get());
+    
+    Optional<Provider<String>> optionalP = injector.getInstance(Key.get(optionalOfProviderString));
+    assertTrue(optionalP.isPresent());
+    assertEquals("b", optionalP.get().get());
+    
+    Optional<javax.inject.Provider<String>> optionalJxP =
+        injector.getInstance(Key.get(optionalOfJavaxProviderString));
+    assertTrue(optionalJxP.isPresent());
+    assertEquals("b", optionalJxP.get().get());
+    
+    assertOptionalVisitor(stringKey,
+        setOf(module1, module2, module3),
+        VisitType.BOTH,
+        0,
+        instance("a"),
+        instance("b"));
+  }
+  
+  public void testExactSameBindingCollapses_defaults() {
+    Module module = new AbstractModule() {
+      @Override protected void configure() {
+        OptionalBinder.newOptionalBinder(binder(), String.class).setDefault()
+            .toInstance(new String("a")); // using new String to ensure .equals is checked.
+        OptionalBinder.newOptionalBinder(binder(), String.class).setDefault()
+            .toInstance(new String("a"));
+      }
+    };
+    Injector injector = Guice.createInjector(module);
+    assertEquals("a", injector.getInstance(String.class));
+    
+    Optional<String> optional = injector.getInstance(Key.get(optionalOfString));
+    assertTrue(optional.isPresent());
+    assertEquals("a", optional.get());
+    
+    Optional<Provider<String>> optionalP = injector.getInstance(Key.get(optionalOfProviderString));
+    assertTrue(optionalP.isPresent());
+    assertEquals("a", optionalP.get().get());
+    
+    Optional<javax.inject.Provider<String>> optionalJxP =
+        injector.getInstance(Key.get(optionalOfJavaxProviderString));
+    assertTrue(optionalJxP.isPresent());
+    assertEquals("a", optionalJxP.get().get());
+
+    assertOptionalVisitor(stringKey, setOf(module), VisitType.BOTH, 0, instance("a"), null);
+  }
+  
+  public void testExactSameBindingCollapses_actual() {
+    Module module = new AbstractModule() {
+      @Override protected void configure() {
+        OptionalBinder.newOptionalBinder(binder(), String.class).setBinding()
+            .toInstance(new String("a")); // using new String to ensure .equals is checked.
+        OptionalBinder.newOptionalBinder(binder(), String.class).setBinding()
+            .toInstance(new String("a"));
+      }
+    };
+    Injector injector = Guice.createInjector(module);
+    assertEquals("a", injector.getInstance(String.class));
+    
+    Optional<String> optional = injector.getInstance(Key.get(optionalOfString));
+    assertTrue(optional.isPresent());
+    assertEquals("a", optional.get());
+    
+    Optional<Provider<String>> optionalP = injector.getInstance(Key.get(optionalOfProviderString));
+    assertTrue(optionalP.isPresent());
+    assertEquals("a", optionalP.get().get());
+    
+    Optional<javax.inject.Provider<String>> optionalJxP =
+        injector.getInstance(Key.get(optionalOfJavaxProviderString));
+    assertTrue(optionalJxP.isPresent());
+    assertEquals("a", optionalJxP.get().get());
+
+    assertOptionalVisitor(stringKey, setOf(module), VisitType.BOTH, 0, null, instance("a"));
+  }
+  
+  public void testDifferentBindingsFail_defaults() {
+    Module module = new AbstractModule() {
+      @Override protected void configure() {
+        OptionalBinder.newOptionalBinder(binder(), String.class).setDefault().toInstance("a");
+        OptionalBinder.newOptionalBinder(binder(), String.class).setDefault().toInstance("b");
+      }
+    };
+    try {
+      Guice.createInjector(module);
+      fail();
+    } catch (CreationException ce) {
+      assertEquals(ce.getMessage(), 1, ce.getErrorMessages().size());
+      assertContains(ce.getMessage(),
+          "1) OptionalBinder for java.lang.String called with different setDefault values, " 
+              + "from bindings:",
+          "at " + module.getClass().getName() + ".configure(",
+          "at " + module.getClass().getName() + ".configure(",
+          "at " + MapBinder.RealMapBinder.class.getName());
+    }
+  }  
+  
+  public void testDifferentBindingsFail_actual() {
+    Module module = new AbstractModule() {
+      @Override protected void configure() {
+        OptionalBinder.newOptionalBinder(binder(), String.class).setBinding().toInstance("a");
+        OptionalBinder.newOptionalBinder(binder(), String.class).setBinding().toInstance("b");
+      }
+    };
+    try {
+      Guice.createInjector(module);
+      fail();
+    } catch (CreationException ce) {
+      assertEquals(ce.getMessage(), 1, ce.getErrorMessages().size());
+      assertContains(ce.getMessage(),
+          "1) OptionalBinder for java.lang.String called with different setBinding values, " 
+              + "from bindings:",
+          "at " + module.getClass().getName() + ".configure(",
+          "at " + module.getClass().getName() + ".configure(",
+          "at " + MapBinder.RealMapBinder.class.getName());
+    }
+  }  
+  
+  public void testDifferentBindingsFail_both() {
+    Module module = new AbstractModule() {
+      @Override protected void configure() {
+        OptionalBinder.newOptionalBinder(binder(), String.class).setDefault().toInstance("a");
+        OptionalBinder.newOptionalBinder(binder(), String.class).setDefault().toInstance("b");
+        OptionalBinder.newOptionalBinder(binder(), String.class).setBinding().toInstance("b");
+        OptionalBinder.newOptionalBinder(binder(), String.class).setBinding().toInstance("c");
+      }
+    };
+    try {
+      Guice.createInjector(module);
+      fail();
+    } catch (CreationException ce) {
+      assertEquals(ce.getMessage(), 1, ce.getErrorMessages().size());
+      assertContains(ce.getMessage(),
+          "1) OptionalBinder for java.lang.String called with different setDefault values, " 
+              + "from bindings:",
+          "at " + module.getClass().getName() + ".configure(",
+          "at " + module.getClass().getName() + ".configure(",
+          "and OptionalBinder for java.lang.String called with different setBinding values, " 
+              + "from bindings:",
+          "at " + module.getClass().getName() + ".configure(",
+          "at " + module.getClass().getName() + ".configure(",
+          "at " + MapBinder.RealMapBinder.class.getName());
+    }
+  }
+  
+  public void testQualifiedAggregatesTogether() {
+    Module module1 = new AbstractModule() {
+      @Override
+      protected void configure() {
+        OptionalBinder.newOptionalBinder(binder(), Key.get(String.class, Names.named("foo")));
+      }
+    };
+    Module module2 = new AbstractModule() {
+      @Override
+      protected void configure() {
+        OptionalBinder.newOptionalBinder(binder(), Key.get(String.class, Names.named("foo")))
+            .setDefault().toInstance("a");
+      }
+    };
+    Module module3 = new AbstractModule() {
+      @Override
+      protected void configure() {
+        OptionalBinder.newOptionalBinder(binder(), Key.get(String.class, Names.named("foo")))
+            .setBinding().toInstance("b");
+      }
+    };
+
+    Injector injector = Guice.createInjector(module1, module2, module3);
+    assertEquals("b", injector.getInstance(Key.get(String.class, Names.named("foo"))));
+
+    Optional<String> optional = injector.getInstance(Key.get(optionalOfString, Names.named("foo")));
+    assertTrue(optional.isPresent());
+    assertEquals("b", optional.get());
+
+    Optional<Provider<String>> optionalP =
+        injector.getInstance(Key.get(optionalOfProviderString, Names.named("foo")));
+    assertTrue(optionalP.isPresent());
+    assertEquals("b", optionalP.get().get());
+
+    Optional<javax.inject.Provider<String>> optionalJxP =
+        injector.getInstance(Key.get(optionalOfJavaxProviderString, Names.named("foo")));
+    assertTrue(optionalJxP.isPresent());
+    assertEquals("b", optionalJxP.get().get());
+    
+    assertOptionalVisitor(Key.get(String.class, Names.named("foo")),
+        setOf(module1, module2, module3),
+        VisitType.BOTH,
+        0,
+        instance("a"),
+        instance("b"));
+  }
+  
+  public void testMultipleDifferentOptionals() {
+    Module module = new AbstractModule() {
+      @Override protected void configure() {
+        OptionalBinder.newOptionalBinder(binder(), String.class).setDefault().toInstance("a");
+        OptionalBinder.newOptionalBinder(binder(), Integer.class).setDefault().toInstance(1);
+      }
+    };
+    Injector injector = Guice.createInjector(module);
+    assertEquals("a", injector.getInstance(String.class));
+    assertEquals(1, injector.getInstance(Integer.class).intValue());
+    
+    assertOptionalVisitor(stringKey, setOf(module), VisitType.BOTH, 1, instance("a"), null);
+  }
+  
+  public void testOptionalIsAppropriatelyLazy() {
+    Module module = new AbstractModule() {
+      int nextValue = 1;
+      @Override protected void configure() {
+        OptionalBinder.newOptionalBinder(binder(), Integer.class)
+            .setDefault().to(Key.get(Integer.class, Names.named("foo")));
+      }
+      @Provides @Named("foo") int provideInt() {
+        return nextValue++;
+      }
+    };
+    Injector injector = Guice.createInjector(module);
+
+    Optional<Provider<Integer>> optionalP =
+        injector.getInstance(Key.get(optionalOfProviderInteger));
+    Optional<javax.inject.Provider<Integer>> optionalJxP =
+        injector.getInstance(Key.get(optionalOfJavaxProviderInteger));
+    
+    assertEquals(1, injector.getInstance(Integer.class).intValue());
+    assertEquals(2, injector.getInstance(Integer.class).intValue());
+    
+    // Calling .get() on an Optional<Integer> multiple times will keep giving the same thing
+    Optional<Integer> optional = injector.getInstance(Key.get(optionalOfInteger));
+    assertEquals(3, optional.get().intValue());
+    assertEquals(3, optional.get().intValue());
+    // But getting another Optional<Integer> will give a new one.
+    assertEquals(4, injector.getInstance(Key.get(optionalOfInteger)).get().intValue());
+    
+    // And the Optional<Provider> will return a provider that gives a new value each time.
+    assertEquals(5, optionalP.get().get().intValue());
+    assertEquals(6, optionalP.get().get().intValue());
+    
+    assertEquals(7, optionalJxP.get().get().intValue());
+    assertEquals(8, optionalJxP.get().get().intValue());
+  }
+  
+  public void testLinkedToNullProvidersMakeAbsentValuesAndPresentProviders_default() {
+    Module module = new AbstractModule() {
+      @Override protected void configure() {
+        OptionalBinder.newOptionalBinder(binder(), String.class)
+            .setDefault().toProvider(Providers.<String>of(null));
+      }
+    };
+    Injector injector = Guice.createInjector(module);
+    assertNull(injector.getInstance(String.class));
+    
+    Optional<String> optional = injector.getInstance(Key.get(optionalOfString));
+    assertFalse(optional.isPresent());
+    
+    Optional<Provider<String>> optionalP = injector.getInstance(Key.get(optionalOfProviderString));
+    assertTrue(optionalP.isPresent());
+    assertNull(optionalP.get().get());
+    
+    Optional<javax.inject.Provider<String>> optionalJxP =
+        injector.getInstance(Key.get(optionalOfJavaxProviderString));
+    assertTrue(optionalJxP.isPresent());
+    assertNull(optionalP.get().get());
+    
+    assertOptionalVisitor(stringKey,
+        setOf(module),
+        VisitType.BOTH,
+        0,
+        SpiUtils.<String>providerInstance(null),
+        null);
+  }
+  
+  public void testLinkedToNullProvidersMakeAbsentValuesAndPresentProviders_actual() {
+    Module module = new AbstractModule() {
+      @Override protected void configure() {
+        OptionalBinder.newOptionalBinder(binder(), String.class)
+            .setBinding().toProvider(Providers.<String>of(null));
+      }
+    };
+    Injector injector = Guice.createInjector(module);
+    assertNull(injector.getInstance(String.class));
+    
+    Optional<String> optional = injector.getInstance(Key.get(optionalOfString));
+    assertFalse(optional.isPresent());
+    
+    Optional<Provider<String>> optionalP = injector.getInstance(Key.get(optionalOfProviderString));
+    assertTrue(optionalP.isPresent());
+    assertNull(optionalP.get().get());
+    
+    Optional<javax.inject.Provider<String>> optionalJxP =
+        injector.getInstance(Key.get(optionalOfJavaxProviderString));
+    assertTrue(optionalJxP.isPresent());
+    assertNull(optionalP.get().get());
+    
+    assertOptionalVisitor(stringKey,
+        setOf(module),
+        VisitType.BOTH,
+        0,
+        null,
+        SpiUtils.<String>providerInstance(null));
+  }
+  
+  // TODO(sameb): Maybe change this?
+  public void testLinkedToNullActualDoesntFallbackToDefault() {
+    Module module = new AbstractModule() {
+      @Override protected void configure() {
+        OptionalBinder.newOptionalBinder(binder(), String.class).setDefault().toInstance("a");
+        OptionalBinder.newOptionalBinder(binder(), String.class)
+            .setBinding().toProvider(Providers.<String>of(null));
+      }
+    };
+    Injector injector = Guice.createInjector(module);
+    assertNull(injector.getInstance(String.class));
+    
+    Optional<String> optional = injector.getInstance(Key.get(optionalOfString));
+    assertFalse(optional.isPresent());
+    
+    Optional<Provider<String>> optionalP = injector.getInstance(Key.get(optionalOfProviderString));
+    assertTrue(optionalP.isPresent());
+    assertNull(optionalP.get().get());
+    
+    Optional<javax.inject.Provider<String>> optionalJxP =
+        injector.getInstance(Key.get(optionalOfJavaxProviderString));
+    assertTrue(optionalJxP.isPresent());
+    assertNull(optionalP.get().get());
+    
+    assertOptionalVisitor(stringKey,
+        setOf(module),
+        VisitType.BOTH,
+        0,
+        instance("a"),
+        SpiUtils.<String>providerInstance(null));
+  }
+
+  public void testSourceLinesInException() {
+    try {
+      Guice.createInjector(new AbstractModule() {
+        @Override protected void configure() {
+          OptionalBinder.newOptionalBinder(binder(),  Integer.class).setDefault();
+        }
+      });
+      fail();
+    } catch (CreationException expected) {
+      assertContains(expected.getMessage(), "No implementation for java.lang.Integer",
+          "at " + getClass().getName());
+    }
+  }
+
+  public void testDependencies() {
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override protected void configure() {
+        OptionalBinder<String> optionalbinder =
+            OptionalBinder.newOptionalBinder(binder(), String.class);
+        optionalbinder.setDefault().toInstance("A");
+        optionalbinder.setBinding().to(Key.get(String.class, Names.named("b")));
+        bindConstant().annotatedWith(Names.named("b")).to("B");
+      }
+    });
+
+    Binding<String> binding = injector.getBinding(Key.get(String.class));
+    HasDependencies withDependencies = (HasDependencies) binding;
+    Set<String> elements = Sets.newHashSet();
+    elements.addAll(recurseForDependencies(injector, withDependencies));
+    assertEquals(ImmutableSet.of("A", "B"), elements);
+  }
+  
+  private Set<String> recurseForDependencies(Injector injector, HasDependencies hasDependencies) {
+    Set<String> elements = Sets.newHashSet();
+    for (Dependency<?> dependency : hasDependencies.getDependencies()) {
+      Binding<?> binding = injector.getBinding(dependency.getKey());
+      HasDependencies deps = (HasDependencies) binding;
+      if (binding instanceof InstanceBinding) {
+        elements.add((String) ((InstanceBinding) binding).getInstance());
+      } else {
+        elements.addAll(recurseForDependencies(injector, deps));
+      }
+    }    
+    return elements;
+  }
+
+  /**
+   * Doubly-installed modules should not conflict, even when one is overridden.
+   */
+  public void testModuleOverrideRepeatedInstalls_toInstance() {
+    Module m = new AbstractModule() {
+      @Override protected void configure() {
+        OptionalBinder<String> b = OptionalBinder.newOptionalBinder(binder(), String.class);
+        b.setDefault().toInstance("A");
+        b.setBinding().toInstance("B");
+      }
+    };
+
+    assertEquals("B", Guice.createInjector(m, m).getInstance(Key.get(String.class)));
+
+    Injector injector = Guice.createInjector(m, Modules.override(m).with(m));
+    assertEquals("B", injector.getInstance(Key.get(String.class)));
+
+    assertOptionalVisitor(stringKey,
+        setOf(m, Modules.override(m).with(m)),
+        VisitType.BOTH,
+        0,
+        instance("A"),
+        instance("B"));
+  }
+
+  public void testModuleOverrideRepeatedInstalls_toKey() {
+    final Key<String> aKey = Key.get(String.class, Names.named("A_string"));
+    final Key<String> bKey = Key.get(String.class, Names.named("B_string"));
+    Module m = new AbstractModule() {
+      @Override protected void configure() {
+        bind(aKey).toInstance("A");
+        bind(bKey).toInstance("B");
+
+        OptionalBinder<String> b = OptionalBinder.newOptionalBinder(binder(), String.class);
+        b.setDefault().to(aKey);
+        b.setBinding().to(bKey);
+      }
+    };
+
+    assertEquals("B", Guice.createInjector(m, m).getInstance(Key.get(String.class)));
+
+    Injector injector = Guice.createInjector(m, Modules.override(m).with(m));
+    assertEquals("B", injector.getInstance(Key.get(String.class)));
+
+    assertOptionalVisitor(stringKey,
+        setOf(m, Modules.override(m).with(m)),
+        VisitType.BOTH,
+        0,
+        linked(aKey),
+        linked(bKey));
+  }
+
+  public void testModuleOverrideRepeatedInstalls_toProviderInstance() {
+    // Providers#of() does not redefine equals/hashCode, so use the same one both times.
+    final Provider<String> aProvider = Providers.of("A");
+    final Provider<String> bProvider = Providers.of("B");
+    Module m = new AbstractModule() {
+      @Override protected void configure() {
+        OptionalBinder<String> b = OptionalBinder.newOptionalBinder(binder(), String.class);
+        b.setDefault().toProvider(aProvider);
+        b.setBinding().toProvider(bProvider);
+      }
+    };
+
+    assertEquals("B", Guice.createInjector(m, m).getInstance(Key.get(String.class)));
+
+    Injector injector = Guice.createInjector(m, Modules.override(m).with(m));
+    assertEquals("B", injector.getInstance(Key.get(String.class)));
+
+    assertOptionalVisitor(stringKey,
+        setOf(m, Modules.override(m).with(m)),
+        VisitType.BOTH,
+        0,
+        providerInstance("A"),
+        providerInstance("B"));
+  }
+
+  private static class AStringProvider implements Provider<String> {
+    public String get() {
+      return "A";
+    }
+  }
+
+  private static class BStringProvider implements Provider<String> {
+    public String get() {
+      return "B";
+    }
+  }
+
+  public void testModuleOverrideRepeatedInstalls_toProviderKey() {
+    Module m = new AbstractModule() {
+      @Override protected void configure() {
+        OptionalBinder<String> b = OptionalBinder.newOptionalBinder(binder(), String.class);
+        b.setDefault().toProvider(Key.get(AStringProvider.class));
+        b.setBinding().toProvider(Key.get(BStringProvider.class));
+      }
+    };
+
+    assertEquals("B", Guice.createInjector(m, m).getInstance(Key.get(String.class)));
+
+    Injector injector = Guice.createInjector(m, Modules.override(m).with(m));
+    assertEquals("B", injector.getInstance(Key.get(String.class)));
+
+    assertOptionalVisitor(stringKey,
+        setOf(m, Modules.override(m).with(m)),
+        VisitType.BOTH,
+        0,
+        providerKey(Key.get(AStringProvider.class)),
+        providerKey(Key.get(BStringProvider.class)));
+  }
+
+  private static class StringGrabber {
+    private final String string;
+
+    @SuppressWarnings("unused")  // Found by reflection
+    public StringGrabber(@Named("A_string") String string) {
+      this.string = string;
+    }
+
+    @SuppressWarnings("unused")  // Found by reflection
+    public StringGrabber(@Named("B_string") String string, int unused) {
+      this.string = string;
+    }
+
+    @Override
+    public int hashCode() {
+      return string.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      return (obj instanceof StringGrabber) && ((StringGrabber) obj).string.equals(string);
+    }
+
+    @Override
+    public String toString() {
+      return "StringGrabber(" + string + ")";
+    }
+  }
+
+  public void testModuleOverrideRepeatedInstalls_toConstructor() {
+    Module m = new AbstractModule() {
+      @Override protected void configure() {
+        Key<String> aKey = Key.get(String.class, Names.named("A_string"));
+        Key<String> bKey = Key.get(String.class, Names.named("B_string"));
+        bind(aKey).toInstance("A");
+        bind(bKey).toInstance("B");
+        bind(Integer.class).toInstance(0);  // used to disambiguate constructors
+
+
+        OptionalBinder<StringGrabber> b =
+            OptionalBinder.newOptionalBinder(binder(), StringGrabber.class);
+        try {
+          b.setDefault().toConstructor(
+              StringGrabber.class.getConstructor(String.class));
+          b.setBinding().toConstructor(
+              StringGrabber.class.getConstructor(String.class, int.class));
+        } catch (NoSuchMethodException e) {
+          fail("No such method: " + e.getMessage());
+        }
+      }
+    };
+
+    assertEquals("B", Guice.createInjector(m, m).getInstance(Key.get(StringGrabber.class)).string);
+
+    Injector injector = Guice.createInjector(m, Modules.override(m).with(m));
+    assertEquals("B", injector.getInstance(Key.get(StringGrabber.class)).string);
+  }
+
+  /**
+   * Unscoped bindings should not conflict, whether they were bound with no explicit scope, or
+   * explicitly bound in {@link Scopes#NO_SCOPE}.
+   */
+  public void testDuplicateUnscopedBindings() {
+    Module m = new AbstractModule() {
+      @Override protected void configure() {
+        OptionalBinder<Integer> b = OptionalBinder.newOptionalBinder(binder(), Integer.class);
+        b.setDefault().to(Key.get(Integer.class, named("foo")));
+        b.setDefault().to(Key.get(Integer.class, named("foo"))).in(Scopes.NO_SCOPE);
+        b.setBinding().to(Key.get(Integer.class, named("foo")));
+        b.setBinding().to(Key.get(Integer.class, named("foo"))).in(Scopes.NO_SCOPE);
+      }
+      @Provides @Named("foo") int provideInt() { return 5; }
+    };
+    assertEquals(5, Guice.createInjector(m).getInstance(Integer.class).intValue());
+  }
+
+  /**
+   * Ensure key hash codes are fixed at injection time, not binding time.
+   */
+  public void testKeyHashCodesFixedAtInjectionTime() {
+    Module m = new AbstractModule() {
+      @Override protected void configure() {
+        OptionalBinder<List<String>> b = OptionalBinder.newOptionalBinder(binder(), listOfStrings);
+        List<String> list = Lists.newArrayList();
+        b.setDefault().toInstance(list);
+        b.setBinding().toInstance(list);
+        list.add("A");
+        list.add("B");
+      }
+    };
+
+    Injector injector = Guice.createInjector(m);
+    for (Entry<Key<?>, Binding<?>> entry : injector.getAllBindings().entrySet()) {
+      Key<?> bindingKey = entry.getKey();
+      Key<?> clonedKey;
+      if (bindingKey.getAnnotation() != null) {
+        clonedKey = Key.get(bindingKey.getTypeLiteral(), bindingKey.getAnnotation());
+      } else if (bindingKey.getAnnotationType() != null) {
+        clonedKey = Key.get(bindingKey.getTypeLiteral(), bindingKey.getAnnotationType());
+      } else {
+        clonedKey = Key.get(bindingKey.getTypeLiteral());
+      }
+      assertEquals(bindingKey, clonedKey);
+      assertEquals("Incorrect hashcode for " + bindingKey + " -> " + entry.getValue(),
+          bindingKey.hashCode(), clonedKey.hashCode());
+    }
+  }
+
+  /**
+   * Ensure bindings do not rehash their keys once returned from {@link Elements#getElements}.
+   */
+  public void testBindingKeysFixedOnReturnFromGetElements() {
+    final List<String> list = Lists.newArrayList();
+    Module m = new AbstractModule() {
+      @Override protected void configure() {
+        OptionalBinder<List<String>> b = OptionalBinder.newOptionalBinder(binder(), listOfStrings);
+        b.setDefault().toInstance(list);
+        list.add("A");
+        list.add("B");
+      }
+    };
+
+    InstanceBinding<?> binding = Iterables.getOnlyElement(
+        Iterables.filter(Elements.getElements(m), InstanceBinding.class));
+    Key<?> keyBefore = binding.getKey();
+    assertEquals(listOfStrings, keyBefore.getTypeLiteral());
+    assertFalse(RehashableKeys.Keys.needsRehashing(keyBefore));
+
+    list.add("C");
+    Key<?> keyAfter = binding.getKey();
+    assertSame(keyBefore, keyAfter);
+    assertTrue(RehashableKeys.Keys.needsRehashing(keyAfter));
+  }
+
+  @BindingAnnotation
+  @Retention(RetentionPolicy.RUNTIME)
+  @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
+  private static @interface Marker {}
+
+  @Marker
+  public void testMatchingMarkerAnnotations() throws Exception {
+    Method m = OptionalBinderTest.class.getDeclaredMethod("testMatchingMarkerAnnotations");
+    assertNotNull(m);
+    final Annotation marker = m.getAnnotation(Marker.class);
+    Injector injector = Guice.createInjector(new AbstractModule() {
+      @Override public void configure() {
+        OptionalBinder<Integer> mb1 =
+            OptionalBinder.newOptionalBinder(binder(), Key.get(Integer.class, Marker.class));
+        OptionalBinder<Integer> mb2 =
+            OptionalBinder.newOptionalBinder(binder(), Key.get(Integer.class, marker));
+        mb1.setDefault().toInstance(1);
+        mb2.setBinding().toInstance(2);
+
+        // This assures us that the two binders are equivalent, so we expect the instance added to
+        // each to have been added to one set.
+        assertEquals(mb1, mb2);
+      }
+    });
+    Integer i1 = injector.getInstance(Key.get(Integer.class, Marker.class));
+    Integer i2 = injector.getInstance(Key.get(Integer.class, marker));
+
+    // These must be identical, because the marker annotations collapsed to the same thing.
+    assertSame(i1, i2);
+    assertEquals(2, i2.intValue());
+  }
+  
+  @SuppressWarnings("unchecked") 
+  private <V> Set<V> setOf(V... elements) {
+    return ImmutableSet.copyOf(elements);
+  }
+}
diff --git a/extensions/multibindings/test/com/google/inject/multibindings/SpiUtils.java b/extensions/multibindings/test/com/google/inject/multibindings/SpiUtils.java
index 829fe3b..d121944 100644
--- a/extensions/multibindings/test/com/google/inject/multibindings/SpiUtils.java
+++ b/extensions/multibindings/test/com/google/inject/multibindings/SpiUtils.java
@@ -16,23 +16,31 @@
 
 package com.google.inject.multibindings;
 
+import static com.google.common.collect.Iterables.getOnlyElement;
 import static com.google.inject.multibindings.MapBinder.entryOfProviderOf;
 import static com.google.inject.multibindings.MapBinder.mapOf;
 import static com.google.inject.multibindings.MapBinder.mapOfJavaxProviderOf;
 import static com.google.inject.multibindings.MapBinder.mapOfProviderOf;
 import static com.google.inject.multibindings.MapBinder.mapOfSetOfProviderOf;
 import static com.google.inject.multibindings.Multibinder.setOf;
+import static com.google.inject.multibindings.OptionalBinder.optionalOfJavaxProvider;
+import static com.google.inject.multibindings.OptionalBinder.optionalOfProvider;
 import static com.google.inject.multibindings.SpiUtils.BindType.INSTANCE;
 import static com.google.inject.multibindings.SpiUtils.BindType.LINKED;
 import static com.google.inject.multibindings.SpiUtils.BindType.PROVIDER_INSTANCE;
+import static com.google.inject.multibindings.SpiUtils.BindType.PROVIDER_KEY;
 import static com.google.inject.multibindings.SpiUtils.VisitType.BOTH;
 import static com.google.inject.multibindings.SpiUtils.VisitType.INJECTOR;
 import static com.google.inject.multibindings.SpiUtils.VisitType.MODULE;
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
 
+import com.google.common.base.Objects;
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.google.inject.Binding;
@@ -40,15 +48,20 @@
 import com.google.inject.Injector;
 import com.google.inject.Key;
 import com.google.inject.Module;
+import com.google.inject.Provider;
 import com.google.inject.TypeLiteral;
+import com.google.inject.multibindings.OptionalBinder.Source;
 import com.google.inject.spi.DefaultBindingTargetVisitor;
 import com.google.inject.spi.Element;
 import com.google.inject.spi.Elements;
+import com.google.inject.spi.HasDependencies;
 import com.google.inject.spi.InstanceBinding;
 import com.google.inject.spi.LinkedKeyBinding;
 import com.google.inject.spi.ProviderInstanceBinding;
+import com.google.inject.spi.ProviderKeyBinding;
 import com.google.inject.spi.ProviderLookup;
 
+import java.lang.reflect.ParameterizedType;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -117,35 +130,16 @@
       for(Map.Entry<?, Binding<?>> entry : entries) {
         Object key = entry.getKey();
         Binding<?> value = entry.getValue();
-        if(!key.equals(result.k)) {
-          continue;
-        }
-        switch (result.v.type) {
-          case INSTANCE:
-            if (value instanceof InstanceBinding
-                && ((InstanceBinding) value).getInstance().equals(result.v.instance)) {
-              found = entry;
-          }
-          break;
-        case LINKED:
-          if (value instanceof LinkedKeyBinding
-              && ((LinkedKeyBinding) value).getKey().equals(result.v.key)) {
-            found = entry;
-          }
-          break;
-        case PROVIDER_INSTANCE:
-          if (value instanceof ProviderInstanceBinding
-              && ((ProviderInstanceBinding) value).getUserSuppliedProvider().get().equals(
-                  result.v.instance)) {
-            found = entry;
-          }
+        if(key.equals(result.k) && matches(value, result.v)) {
+          found = entry;
           break;
         }
       }
       if(found == null) {
         fail("Could not find entry: " + result + " in remaining entries: " + entries);
       } else {
-        assertTrue(mapbinder.containsElement(found.getValue()));
+        assertTrue("mapBinder doesn't contain: " + found.getValue(), 
+            mapbinder.containsElement(found.getValue()));
         entries.remove(found);
       }
     }
@@ -361,25 +355,8 @@
     for(BindResult result : bindResults) {
       Binding found = null;
       for(Binding item : elements) {
-        switch (result.type) {
-        case INSTANCE:
-          if (item instanceof InstanceBinding
-              && ((InstanceBinding) item).getInstance().equals(result.instance)) {
-            found = item;
-          }
-          break;
-        case LINKED:
-          if (item instanceof LinkedKeyBinding
-              && ((LinkedKeyBinding) item).getKey().equals(result.key)) {
-            found = item;
-          }
-          break;
-        case PROVIDER_INSTANCE:
-          if (item instanceof ProviderInstanceBinding
-              && ((ProviderInstanceBinding) item).getUserSuppliedProvider().get().equals(
-                  result.instance)) {
-            found = item;
-          }
+        if (matches(item, result)) {
+          found = item;
           break;
         }
       }
@@ -480,6 +457,320 @@
     // Validate that we can construct an injector out of the remaining bindings.
     Guice.createInjector(Elements.getModule(otherElements));
   }
+
+  /**
+   * Asserts that OptionalBinderBinding visitors for work correctly.
+   *
+   * @param <T> The type of the binding
+   * @param keyType The key OptionalBinder is binding
+   * @param modules The modules that define the bindings
+   * @param visitType The kind of test we should perform. A live Injector, a raw Elements (Module)
+   *        test, or both.
+   * @param expectedOtherOptionalBindings the # of other optional bindings we expect to see.
+   * @param expectedDefault the expected default binding, or null if none
+   * @param expectedActual the expected actual binding, or null if none
+   */
+  static <T> void assertOptionalVisitor(Key<T> keyType,
+      Iterable<? extends Module> modules,
+      VisitType visitType,
+      int expectedOtherOptionalBindings,
+      BindResult<?> expectedDefault,
+      BindResult<?> expectedActual) {
+    if (visitType == null) {
+      fail("must test something");
+    }
+
+    if (visitType == BOTH || visitType == INJECTOR) {
+      optionalInjectorTest(keyType, modules, expectedOtherOptionalBindings, expectedDefault,
+          expectedActual);
+    }
+
+    if (visitType == BOTH || visitType == MODULE) {
+      optionalModuleTest(keyType, modules, expectedOtherOptionalBindings, expectedDefault,
+          expectedActual);
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  private static <T> void optionalInjectorTest(Key<T> keyType, Iterable<? extends Module> modules,
+      int expectedOtherOptionalBindings, BindResult<?> expectedDefault,
+      BindResult<?> expectedActual) {
+    Key<Optional<T>> optionalKey =
+        keyType.ofType(OptionalBinder.optionalOf(keyType.getTypeLiteral()));
+    Injector injector = Guice.createInjector(modules);
+    Binding<Optional<T>> optionalBinding = injector.getBinding(optionalKey);
+    Visitor<Optional<T>> visitor = new Visitor<Optional<T>>();
+    OptionalBinderBinding<T> optionalBinder =
+        (OptionalBinderBinding<T>) optionalBinding.acceptTargetVisitor(visitor);
+    assertNotNull(optionalBinder);
+    assertEquals(optionalKey, optionalBinder.getKey());
+
+    if (expectedDefault == null) {
+      assertNull("did not expect a default binding", optionalBinder.getDefaultBinding());
+    } else {
+      assertTrue("expectedDefault: " + expectedDefault + ", actualDefault: "
+          + optionalBinder.getDefaultBinding(),
+          matches(optionalBinder.getDefaultBinding(), expectedDefault));
+    }
+
+    if (expectedActual == null) {
+      assertNull(optionalBinder.getActualBinding());
+    } else {
+      assertTrue("expectedActual: " + expectedActual + ", actualActual: "
+          + optionalBinder.getActualBinding(),
+          matches(optionalBinder.getActualBinding(), expectedActual));
+    }
+
+
+    Key<Optional<javax.inject.Provider<T>>> optionalJavaxProviderKey =
+        keyType.ofType(optionalOfJavaxProvider(keyType.getTypeLiteral()));
+    Key<Optional<Provider<T>>> optionalProviderKey =
+        keyType.ofType(optionalOfProvider(keyType.getTypeLiteral()));
+    Binding<Map<Source, T>> mapBinding = injector.getBinding(
+        keyType.ofType(mapOf(TypeLiteral.get(Source.class), keyType.getTypeLiteral())));
+    MapBinderBinding<Map<Source, T>> mapbinderBinding = (MapBinderBinding<
+        Map<Source, T>>) mapBinding.acceptTargetVisitor(new Visitor<Map<Source, T>>());
+
+    boolean keyMatch = false;
+    boolean optionalKeyMatch = false;
+    boolean optionalJavaxProviderKeyMatch = false;
+    boolean optionalProviderKeyMatch = false;
+    boolean mapBindingMatch = false;
+    boolean defaultMatch = false;
+    boolean actualMatch = false;
+    List<Object> otherOptionalBindings = Lists.newArrayList();
+    List<Binding> otherMatches = Lists.newArrayList();
+    for (Binding b : injector.getAllBindings().values()) {
+      boolean contains = optionalBinder.containsElement(b);
+      Object visited = b.acceptTargetVisitor(visitor);
+      if (visited instanceof OptionalBinderBinding) {
+        if (visited.equals(optionalBinder)) {
+          assertTrue(contains);
+        } else {
+          otherOptionalBindings.add(visited);
+        }
+      }
+      if (b.getKey().equals(keyType)) {
+        // keyType might match because a user bound it
+        // (which is possible in a purely absent OptionalBinder)
+        assertEquals(expectedDefault != null || expectedActual != null, contains);
+        if (contains) {
+          keyMatch = true;
+        }
+      } else if (b.getKey().equals(optionalKey)) {
+        assertTrue(contains);
+        optionalKeyMatch = true;
+      } else if (b.getKey().equals(optionalJavaxProviderKey)) {
+        assertTrue(contains);
+        optionalJavaxProviderKeyMatch = true;
+      } else if (b.getKey().equals(optionalProviderKey)) {
+        assertTrue(contains);
+        optionalProviderKeyMatch = true;
+      } else if (b.getKey().equals(mapBinding.getKey())) {
+        assertTrue(contains);
+        mapBindingMatch = true;
+        // Validate that this binding is also a MapBinding.
+        assertEquals(mapbinderBinding, b.acceptTargetVisitor(visitor));
+      } else if (expectedDefault != null && matches(b, expectedDefault)) {
+        assertTrue(contains);
+        defaultMatch = true;
+      } else if (expectedActual != null && matches(b, expectedActual)) {
+        assertTrue(contains);
+        actualMatch = true;
+      } else if (mapbinderBinding.containsElement(b)) {
+        assertTrue(contains);
+      } else if (contains) {
+        otherMatches.add(b);
+      }
+    }
+
+    assertEquals(otherMatches.toString(), 0, otherMatches.size());
+    // only expect a keymatch if either default or actual are set
+    assertEquals(expectedDefault != null || expectedActual != null, keyMatch);
+    assertTrue(optionalKeyMatch);
+    assertTrue(optionalJavaxProviderKeyMatch);
+    assertTrue(optionalProviderKeyMatch);
+    assertTrue(mapBindingMatch);
+    assertEquals(expectedDefault != null, defaultMatch);
+    assertEquals(expectedActual != null, actualMatch);
+    assertEquals("other OptionalBindings found: " + otherOptionalBindings,
+        expectedOtherOptionalBindings, otherOptionalBindings.size());
+  }
+
+  @SuppressWarnings("unchecked")
+  private static <T> void optionalModuleTest(Key<T> keyType, Iterable<? extends Module> modules,
+      int expectedOtherOptionalBindings, BindResult<?> expectedDefault,
+      BindResult<?> expectedActual) {
+    Set<Element> elements = ImmutableSet.copyOf(Elements.getElements(modules));
+    Map<Key<?>, Binding<?>> indexed = index(elements);
+    Key<Optional<T>> optionalKey =
+        keyType.ofType(OptionalBinder.optionalOf(keyType.getTypeLiteral()));
+    Key<Map<Source, T>> sourceMapKey =
+        keyType.ofType(mapOf(TypeLiteral.get(Source.class), keyType.getTypeLiteral()));
+    Visitor<Optional<T>> visitor = new Visitor<Optional<T>>();
+    OptionalBinderBinding<T> optionalBinder = null;
+    MapBinderBinding<Map<Source, T>> mapbinderBinding = null;
+    Key<?> defaultKey = null;
+    Key<?> actualKey = null;
+
+    Binding optionalBinding = indexed.get(optionalKey);
+    optionalBinder = (OptionalBinderBinding<T>) optionalBinding.acceptTargetVisitor(visitor);
+    Binding mapBinding = indexed.get(sourceMapKey);
+    mapbinderBinding = (MapBinderBinding<Map<Source, T>>) mapBinding.acceptTargetVisitor(visitor);
+
+    // Locate the defaultKey & actualKey
+    for (Element element : elements) {
+      if (optionalBinder.containsElement(element) && element instanceof Binding) {
+        Binding binding = (Binding) element;
+        if (isSourceEntry(binding, Source.DEFAULT)) {
+          defaultKey = keyFromOptionalSourceBinding(binding, indexed);
+        } else if (isSourceEntry(binding, Source.ACTUAL)) {
+          actualKey = keyFromOptionalSourceBinding(binding, indexed);
+        }
+      }
+    }
+    assertNotNull(optionalBinder);
+    assertEquals(expectedDefault == null, defaultKey == null);
+    assertEquals(expectedActual == null, actualKey == null);
+
+    Key<Optional<javax.inject.Provider<T>>> optionalJavaxProviderKey =
+        keyType.ofType(optionalOfJavaxProvider(keyType.getTypeLiteral()));
+    Key<Optional<Provider<T>>> optionalProviderKey =
+        keyType.ofType(optionalOfProvider(keyType.getTypeLiteral()));
+    boolean keyMatch = false;
+    boolean optionalKeyMatch = false;
+    boolean optionalJavaxProviderKeyMatch = false;
+    boolean optionalProviderKeyMatch = false;
+    boolean mapBindingMatch = false;
+    boolean defaultMatch = false;
+    boolean actualMatch = false;
+    List<Object> otherOptionalElements = Lists.newArrayList();
+    List<Element> otherContains = Lists.newArrayList();
+    List<Element> nonContainedElements = Lists.newArrayList();
+    for (Element element : elements) {
+      boolean contains = optionalBinder.containsElement(element);
+      if (!contains) {
+        nonContainedElements.add(element);
+      }
+      Key key = null;
+      Binding b = null;
+      if (element instanceof Binding) {
+        b = (Binding) element;
+        key = b.getKey();
+        Object visited = b.acceptTargetVisitor(visitor);
+        if (visited instanceof OptionalBinderBinding) {
+          if (visited.equals(optionalBinder)) {
+            assertTrue(contains);
+          } else {
+            otherOptionalElements.add(visited);
+          }
+        }
+      } else if (element instanceof ProviderLookup) {
+        key = ((ProviderLookup) element).getKey();
+      }
+
+      if (key != null && key.equals(keyType)) {
+        // keyType might match because a user bound it
+        // (which is possible in a purely absent OptionalBinder)
+        assertEquals(expectedDefault != null || expectedActual != null, contains);
+        if (contains) {
+          keyMatch = true;
+        }
+      } else if (key != null && key.equals(optionalKey)) {
+        assertTrue(contains);
+        optionalKeyMatch = true;
+      } else if (key != null && key.equals(optionalJavaxProviderKey)) {
+        assertTrue(contains);
+        optionalJavaxProviderKeyMatch = true;
+      } else if (key != null && key.equals(optionalProviderKey)) {
+        assertTrue(contains);
+        optionalProviderKeyMatch = true;
+      } else if (key != null && key.equals(sourceMapKey)) {
+        assertTrue(contains);
+        mapBindingMatch = true;
+        // Validate that this binding is also a MapBinding.
+        assertEquals(mapbinderBinding, b.acceptTargetVisitor(visitor));
+      } else if (key != null && key.equals(defaultKey)) {
+        assertTrue(contains);
+        if (b != null) { // otherwise it might just be a ProviderLookup into it
+          assertTrue("expected: " + expectedDefault + ", but was: " + b,
+              matches(b, expectedDefault));
+          defaultMatch = true;
+        }
+      } else if (key != null && key.equals(actualKey)) {
+        assertTrue(contains);
+        if (b != null) { // otherwise it might just be a ProviderLookup into it
+          assertTrue("expected: " + expectedActual + ", but was: " + b, matches(b, expectedActual));
+          actualMatch = true;
+        }
+      } else if (mapbinderBinding.containsElement(element)) {
+        assertTrue(contains);
+      } else if (contains) {
+        otherContains.add(element);
+      }
+    }
+    
+    // only expect a keymatch if either default or actual are set
+    assertEquals(expectedDefault != null || expectedActual != null, keyMatch);
+    assertTrue(optionalKeyMatch);
+    assertTrue(optionalJavaxProviderKeyMatch);
+    assertTrue(optionalProviderKeyMatch);
+    assertTrue(mapBindingMatch);
+    assertEquals(expectedDefault != null, defaultMatch);
+    assertEquals(expectedActual != null, actualMatch);
+    assertEquals(otherContains.toString(), 0, otherContains.size());
+    assertEquals("other OptionalBindings found: " + otherOptionalElements,
+        expectedOtherOptionalBindings, otherOptionalElements.size());
+    
+     // Validate that we can construct an injector out of the remaining bindings.
+    Guice.createInjector(Elements.getModule(nonContainedElements));
+  }
+  
+  private static Key<?> keyFromOptionalSourceBinding(Binding<?> binding,
+      Map<Key<?>, Binding<?>> elements) {
+    // Flow is:
+    //  binding == ProviderInstanceBinding<Map.Entry<Source, Provider<String>>
+    //   dependency on: Provider<String> that maps to ProviderInstanceBinding<String> in MapBinder
+    //      dependency on: Provider<String> of user set value.
+    Key<?> mapKey =
+        genericOf(getOnlyElement(((HasDependencies) binding).getDependencies()).getKey());
+    Binding<?> mapBinding = elements.get(mapKey);
+    Key<?> userKey =
+        genericOf(getOnlyElement(((HasDependencies) mapBinding).getDependencies()).getKey());
+    return userKey;
+  }
+
+  /** Returns {@code Key<T>} for something like {@code Key<Provider<T>>} */
+  private static Key<?> genericOf(Key<?> key) {
+    ParameterizedType type = (ParameterizedType) key.getTypeLiteral().getType();
+    assertEquals(1, type.getActualTypeArguments().length);
+    Key<?> result = key.ofType(type.getActualTypeArguments()[0]);
+    return result;
+  }
+
+  private static boolean isSourceEntry(Binding b, Source type) {
+    if (b instanceof ProviderInstanceBinding && b.getKey().getAnnotation() instanceof RealElement) {
+      javax.inject.Provider provider = ((ProviderInstanceBinding) b).getUserSuppliedProvider();
+      if (provider instanceof Map.Entry) {
+        Map.Entry entry = (Map.Entry) provider;
+        if (entry.getKey() == type) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  /** Returns the subset of elements that have keys, indexed by them. */
+  private static Map<Key<?>, Binding<?>> index(Iterable<Element> elements) {
+    ImmutableMap.Builder<Key<?>, Binding<?>> builder = ImmutableMap.builder();
+    for (Element element : elements) {
+      if (element instanceof Binding) {
+        builder.put(((Binding) element).getKey(), (Binding) element);
+      }
+    }
+    return builder.build();
+  }
   
   static <K, V> MapResult instance(K k, V v) {
     return new MapResult<K, V>(k, new BindResult<V>(INSTANCE, v, null));
@@ -511,32 +802,67 @@
       return "entry[key[" + k + "],value[" + v + "]]";
     }
   }  
+  
+  private static boolean matches(Binding<?> item, BindResult<?> result) {
+    switch (result.type) {
+    case INSTANCE:
+      if (item instanceof InstanceBinding
+          && ((InstanceBinding) item).getInstance().equals(result.instance)) {
+        return true;
+      }
+      break;
+    case LINKED:
+      if (item instanceof LinkedKeyBinding
+          && ((LinkedKeyBinding) item).getLinkedKey().equals(result.key)) {
+        return true;
+      }
+      break;
+    case PROVIDER_INSTANCE:
+      if (item instanceof ProviderInstanceBinding
+          && Objects.equal(((ProviderInstanceBinding) item).getUserSuppliedProvider().get(),
+                           result.instance)) {
+        return true;
+      }
+      break;
+    case PROVIDER_KEY:
+      if (item instanceof ProviderKeyBinding
+          && ((ProviderKeyBinding) item).getProviderKey().equals(result.key)) {
+        return true;
+      }
+      break;
+    }
+    return false;
+  }
 
-  static <T> BindResult instance(T t) {
+  static <T> BindResult<T> instance(T t) {
     return new BindResult<T>(INSTANCE, t, null);
   }
 
-  static <T> BindResult linked(Class<? extends T> clazz) {
+  static <T> BindResult<T> linked(Class<? extends T> clazz) {
     return new BindResult<T>(LINKED, null, Key.get(clazz));
   }
 
-  static <T> BindResult linked(Key<? extends T> key) {
+  static <T> BindResult<T> linked(Key<? extends T> key) {
     return new BindResult<T>(LINKED, null, key);
   }
 
-  static <T> BindResult providerInstance(T t) {
+  static <T> BindResult<T> providerInstance(T t) {
     return new BindResult<T>(PROVIDER_INSTANCE, t, null);
   }
+
+  static <T> BindResult<T> providerKey(Key<T> key) {
+    return new BindResult<T>(PROVIDER_KEY, null, key);
+  }
   
   /** The kind of binding. */
-  static enum BindType { INSTANCE, LINKED, PROVIDER_INSTANCE }
+  static enum BindType { INSTANCE, LINKED, PROVIDER_INSTANCE, PROVIDER_KEY }
   /** The result of the binding. */
   static class BindResult<T> {
     private final BindType type;
-    private final Key<? extends T> key;
+    private final Key<?> key;
     private final T instance;
     
-    private BindResult(BindType type, T instance, Key<? extends T> key) {
+    private BindResult(BindType type, T instance, Key<?> key) {
       this.type = type;
       this.instance = instance;
       this.key = key;
@@ -551,6 +877,8 @@
         return "linkedKey[" + key + "]";
       case PROVIDER_INSTANCE:
         return "providerInstance[" + instance + "]";
+      case PROVIDER_KEY:
+        return "providerKey[" + key + "]";
       }
       return null;
     }
@@ -565,7 +893,11 @@
   
     public Object visit(MapBinderBinding<? extends T> mapbinding) {
       return mapbinding;
-    }  
+    }
+    
+    public Object visit(OptionalBinderBinding<? extends T> optionalbinding) {
+      return optionalbinding;
+    }
   }
 }
 
diff --git a/lib/build/jsr305.jar b/lib/build/jsr305.jar
new file mode 100644
index 0000000..f973aea
--- /dev/null
+++ b/lib/build/jsr305.jar
Binary files differ