diff --git a/core/lib/Drupal/Component/Plugin/Factory/DefaultFactory.php b/core/lib/Drupal/Component/Plugin/Factory/DefaultFactory.php
index 337e503..2315a7f 100644
--- a/core/lib/Drupal/Component/Plugin/Factory/DefaultFactory.php
+++ b/core/lib/Drupal/Component/Plugin/Factory/DefaultFactory.php
@@ -8,7 +8,6 @@
 
 use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
 use Drupal\Component\Plugin\Exception\PluginException;
-use Drupal\Component\Plugin\Derivative\DeriverInterface;
 
 /**
  * Default plugin factory.
@@ -31,10 +30,23 @@ class DefaultFactory implements FactoryInterface {
   protected $discovery;
 
   /**
-   * Constructs a Drupal\Component\Plugin\Factory\DefaultFactory object.
+   * Defines an interface each plugin should implement.
+   *
+   * @var string|null
    */
-  public function __construct(DiscoveryInterface $discovery) {
+  protected $interface;
+
+  /**
+   * Constructs a Drupal\Component\Plugin\Factory\DefaultFactory object.
+   *
+   * @param \Drupal\Component\Plugin\Discovery\DiscoveryInterface $discovery
+   *   The plugin discovery.
+   * @param string|null $plugin_interface
+   *   (optional) The interface each plugin should implement.
+   */
+  public function __construct(DiscoveryInterface $discovery, $plugin_interface = NULL) {
     $this->discovery = $discovery;
+    $this->interface = $plugin_interface;
   }
 
   /**
@@ -42,7 +54,7 @@ public function __construct(DiscoveryInterface $discovery) {
    */
   public function createInstance($plugin_id, array $configuration = array()) {
     $plugin_definition = $this->discovery->getDefinition($plugin_id);
-    $plugin_class = static::getPluginClass($plugin_id, $plugin_definition);
+    $plugin_class = static::getPluginClass($plugin_id, $plugin_definition, $this->interface);
     return new $plugin_class($configuration, $plugin_id, $plugin_definition);
   }
 
@@ -53,13 +65,18 @@ public function createInstance($plugin_id, array $configuration = array()) {
    *   The id of a plugin.
    * @param mixed $plugin_definition
    *   The plugin definition associated with the plugin ID.
+   * @param string $required_interface
+   *   (optional) THe required plugin interface.
    *
    * @return string
    *   The appropriate class name.
    *
    * @throws \Drupal\Component\Plugin\Exception\PluginException
+   *   Thrown when there is no class specified, the class doesn't exist, or
+   *   the class does not implement the specified required interface.
+   *
    */
-  public static function getPluginClass($plugin_id, $plugin_definition = NULL) {
+  public static function getPluginClass($plugin_id, $plugin_definition = NULL, $required_interface = NULL) {
     if (empty($plugin_definition['class'])) {
       throw new PluginException(sprintf('The plugin (%s) did not specify an instance class.', $plugin_id));
     }
@@ -70,6 +87,10 @@ public static function getPluginClass($plugin_id, $plugin_definition = NULL) {
       throw new PluginException(sprintf('Plugin (%s) instance class "%s" does not exist.', $plugin_id, $class));
     }
 
+    if ($required_interface && !is_subclass_of($plugin_definition['class'], $required_interface)) {
+      throw new PluginException(sprintf('Plugin "%s" (%s) in %s should implement interface %s.', $plugin_id, $plugin_definition['class'], $plugin_definition['provider'], $required_interface));
+    }
+
     return $class;
   }
 }
diff --git a/core/lib/Drupal/Component/Plugin/Factory/ReflectionFactory.php b/core/lib/Drupal/Component/Plugin/Factory/ReflectionFactory.php
index 3fce8be..ae9dfe1 100644
--- a/core/lib/Drupal/Component/Plugin/Factory/ReflectionFactory.php
+++ b/core/lib/Drupal/Component/Plugin/Factory/ReflectionFactory.php
@@ -19,7 +19,7 @@ class ReflectionFactory extends DefaultFactory {
    */
   public function createInstance($plugin_id, array $configuration = array()) {
     $plugin_definition = $this->discovery->getDefinition($plugin_id);
-    $plugin_class = static::getPluginClass($plugin_id, $plugin_definition);
+    $plugin_class = static::getPluginClass($plugin_id, $plugin_definition, $this->interface);
 
     // Lets figure out of there's a constructor for this class and pull
     // arguments from the $options array if so to populate it.
diff --git a/core/lib/Drupal/Core/Action/ActionManager.php b/core/lib/Drupal/Core/Action/ActionManager.php
index d3a4c0a..1baf427 100644
--- a/core/lib/Drupal/Core/Action/ActionManager.php
+++ b/core/lib/Drupal/Core/Action/ActionManager.php
@@ -33,7 +33,7 @@ class ActionManager extends DefaultPluginManager {
    *   The module handler to invoke the alter hook with.
    */
   public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
-    parent::__construct('Plugin/Action', $namespaces, $module_handler, 'Drupal\Core\Annotation\Action');
+    parent::__construct('Plugin/Action', $namespaces, $module_handler, 'Drupal\Core\Action\ActionInterface', 'Drupal\Core\Annotation\Action');
     $this->alterInfo('action_info');
     $this->setCacheBackend($cache_backend, 'action_info');
   }
diff --git a/core/lib/Drupal/Core/Archiver/ArchiverManager.php b/core/lib/Drupal/Core/Archiver/ArchiverManager.php
index b161bd9..b8ac88c 100644
--- a/core/lib/Drupal/Core/Archiver/ArchiverManager.php
+++ b/core/lib/Drupal/Core/Archiver/ArchiverManager.php
@@ -1,6 +1,7 @@
 <?php
 
 /**
+ * @file
  * Contains \Drupal\Core\Archiver\ArchiverManager.
  */
 
@@ -32,7 +33,7 @@ class ArchiverManager extends DefaultPluginManager {
    *   The module handler to invoke the alter hook with.
    */
   public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
-    parent::__construct('Plugin/Archiver', $namespaces, $module_handler, 'Drupal\Core\Archiver\Annotation\Archiver');
+    parent::__construct('Plugin/Archiver', $namespaces, $module_handler, 'Drupal\Core\Archiver\ArchiverInterface', 'Drupal\Core\Archiver\Annotation\Archiver');
     $this->alterInfo('archiver_info');
     $this->setCacheBackend($cache_backend, 'archiver_info_plugins');
   }
@@ -42,7 +43,7 @@ public function __construct(\Traversable $namespaces, CacheBackendInterface $cac
    */
   public function createInstance($plugin_id, array $configuration = array()) {
     $plugin_definition = $this->getDefinition($plugin_id);
-    $plugin_class = DefaultFactory::getPluginClass($plugin_id, $plugin_definition);
+    $plugin_class = DefaultFactory::getPluginClass($plugin_id, $plugin_definition, 'Drupal\Core\Archiver\ArchiverInterface');
     return new $plugin_class($configuration['filepath']);
   }
 
diff --git a/core/lib/Drupal/Core/Block/BlockManager.php b/core/lib/Drupal/Core/Block/BlockManager.php
index e65d2f6..8cde728 100644
--- a/core/lib/Drupal/Core/Block/BlockManager.php
+++ b/core/lib/Drupal/Core/Block/BlockManager.php
@@ -44,7 +44,7 @@ class BlockManager extends DefaultPluginManager implements BlockManagerInterface
    *   The module handler to invoke the alter hook with.
    */
   public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
-    parent::__construct('Plugin/Block', $namespaces, $module_handler, 'Drupal\Core\Block\Annotation\Block');
+    parent::__construct('Plugin/Block', $namespaces, $module_handler, 'Drupal\Core\Block\BlockPluginInterface', 'Drupal\Core\Block\Annotation\Block');
 
     $this->alterInfo('block');
     $this->setCacheBackend($cache_backend, 'block_plugins');
diff --git a/core/lib/Drupal/Core/Condition/ConditionManager.php b/core/lib/Drupal/Core/Condition/ConditionManager.php
index db0fa43..c1e309a 100644
--- a/core/lib/Drupal/Core/Condition/ConditionManager.php
+++ b/core/lib/Drupal/Core/Condition/ConditionManager.php
@@ -42,7 +42,7 @@ public function __construct(\Traversable $namespaces, CacheBackendInterface $cac
     $this->alterInfo('condition_info');
     $this->setCacheBackend($cache_backend, 'condition_plugins');
 
-    parent::__construct('Plugin/Condition', $namespaces, $module_handler, 'Drupal\Core\Condition\Annotation\Condition');
+    parent::__construct('Plugin/Condition', $namespaces, $module_handler, 'Drupal\Core\Condition\ConditionInterface', 'Drupal\Core\Condition\Annotation\Condition');
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Display/VariantManager.php b/core/lib/Drupal/Core/Display/VariantManager.php
index 7f18a29..b591508 100755
--- a/core/lib/Drupal/Core/Display/VariantManager.php
+++ b/core/lib/Drupal/Core/Display/VariantManager.php
@@ -31,7 +31,7 @@ class VariantManager extends DefaultPluginManager {
    *   The module handler to invoke the alter hook with.
    */
   public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
-    parent::__construct('Plugin/DisplayVariant', $namespaces, $module_handler, 'Drupal\Core\Display\Annotation\DisplayVariant');
+    parent::__construct('Plugin/DisplayVariant', $namespaces, $module_handler, 'Drupal\Core\Display\VariantInterface', 'Drupal\Core\Display\Annotation\DisplayVariant');
 
     $this->setCacheBackend($cache_backend, 'variant_plugins');
     $this->alterInfo('display_variant_plugin');
diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index 26eac4e..4f41256 100644
--- a/core/lib/Drupal/Core/Entity/EntityManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityManager.php
@@ -171,7 +171,7 @@ class EntityManager extends DefaultPluginManager implements EntityManagerInterfa
    *   The class resolver.
    */
   public function __construct(\Traversable $namespaces, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, LanguageManagerInterface $language_manager, TranslationInterface $translation_manager, ClassResolverInterface $class_resolver, TypedDataManager $typed_data_manager) {
-    parent::__construct('Entity', $namespaces, $module_handler, 'Drupal\Core\Entity\Annotation\EntityType');
+    parent::__construct('Entity', $namespaces, $module_handler, 'Drupal\Core\Entity\EntityInterface', 'Drupal\Core\Entity\Annotation\EntityType');
 
     $this->setCacheBackend($cache, 'entity_type', array('entity_types' => TRUE));
     $this->alterInfo('entity_type');
diff --git a/core/lib/Drupal/Core/Field/FieldTypePluginManager.php b/core/lib/Drupal/Core/Field/FieldTypePluginManager.php
index 6a5f39a..f3fb059 100644
--- a/core/lib/Drupal/Core/Field/FieldTypePluginManager.php
+++ b/core/lib/Drupal/Core/Field/FieldTypePluginManager.php
@@ -31,7 +31,7 @@ class FieldTypePluginManager extends DefaultPluginManager implements FieldTypePl
    *   The module handler.
    */
   public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
-    parent::__construct('Plugin/Field/FieldType', $namespaces, $module_handler, 'Drupal\Core\Field\Annotation\FieldType');
+    parent::__construct('Plugin/Field/FieldType', $namespaces, $module_handler, 'Drupal\Core\Field\FieldItemInterface', 'Drupal\Core\Field\Annotation\FieldType');
     $this->alterInfo('field_info');
     $this->setCacheBackend($cache_backend, 'field_types_plugins');
   }
diff --git a/core/lib/Drupal/Core/Field/FormatterPluginManager.php b/core/lib/Drupal/Core/Field/FormatterPluginManager.php
index 5d5cea2..b7b297b 100644
--- a/core/lib/Drupal/Core/Field/FormatterPluginManager.php
+++ b/core/lib/Drupal/Core/Field/FormatterPluginManager.php
@@ -47,8 +47,7 @@ class FormatterPluginManager extends DefaultPluginManager {
    *   The 'field type' plugin manager.
    */
   public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, FieldTypePluginManagerInterface $field_type_manager) {
-
-    parent::__construct('Plugin/Field/FieldFormatter', $namespaces, $module_handler, 'Drupal\Core\Field\Annotation\FieldFormatter');
+    parent::__construct('Plugin/Field/FieldFormatter', $namespaces, $module_handler, 'Drupal\Core\Field\FormatterInterface', 'Drupal\Core\Field\Annotation\FieldFormatter');
 
     $this->setCacheBackend($cache_backend, 'field_formatter_types_plugins');
     $this->alterInfo('field_formatter_info');
diff --git a/core/lib/Drupal/Core/Field/WidgetFactory.php b/core/lib/Drupal/Core/Field/WidgetFactory.php
index 9e9408f..db8c291 100644
--- a/core/lib/Drupal/Core/Field/WidgetFactory.php
+++ b/core/lib/Drupal/Core/Field/WidgetFactory.php
@@ -19,7 +19,7 @@ class WidgetFactory extends DefaultFactory {
    */
   public function createInstance($plugin_id, array $configuration = array()) {
     $plugin_definition = $this->discovery->getDefinition($plugin_id);
-    $plugin_class = static::getPluginClass($plugin_id, $plugin_definition);
+    $plugin_class = static::getPluginClass($plugin_id, $plugin_definition, $this->interface);
     return new $plugin_class($plugin_id, $plugin_definition, $configuration['field_definition'], $configuration['settings']);
   }
 }
diff --git a/core/lib/Drupal/Core/Field/WidgetPluginManager.php b/core/lib/Drupal/Core/Field/WidgetPluginManager.php
index 207b5e3..8aa4434 100644
--- a/core/lib/Drupal/Core/Field/WidgetPluginManager.php
+++ b/core/lib/Drupal/Core/Field/WidgetPluginManager.php
@@ -47,7 +47,7 @@ class WidgetPluginManager extends DefaultPluginManager {
    *   The 'field type' plugin manager.
    */
   public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, FieldTypePluginManagerInterface $field_type_manager) {
-    parent::__construct('Plugin/Field/FieldWidget', $namespaces, $module_handler, 'Drupal\Core\Field\Annotation\FieldWidget');
+    parent::__construct('Plugin/Field/FieldWidget', $namespaces, $module_handler, 'Drupal\Core\Field\WidgetInterface', 'Drupal\Core\Field\Annotation\FieldWidget');
 
     $this->setCacheBackend($cache_backend, 'field_widget_types_plugins');
     $this->alterInfo('field_widget_info');
diff --git a/core/lib/Drupal/Core/ImageToolkit/ImageToolkitManager.php b/core/lib/Drupal/Core/ImageToolkit/ImageToolkitManager.php
index 69e2f1c..9c11427 100644
--- a/core/lib/Drupal/Core/ImageToolkit/ImageToolkitManager.php
+++ b/core/lib/Drupal/Core/ImageToolkit/ImageToolkitManager.php
@@ -63,7 +63,7 @@ class ImageToolkitManager extends DefaultPluginManager {
    *   A logger instance.
    */
   public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, ImageToolkitOperationManagerInterface $operation_manager, LoggerInterface $logger) {
-    parent::__construct('Plugin/ImageToolkit', $namespaces, $module_handler, 'Drupal\Core\ImageToolkit\Annotation\ImageToolkit');
+    parent::__construct('Plugin/ImageToolkit', $namespaces, $module_handler, 'Drupal\Core\ImageToolkit\ImageToolkitInterface', 'Drupal\Core\ImageToolkit\Annotation\ImageToolkit');
 
     $this->setCacheBackend($cache_backend, 'image_toolkit_plugins');
     $this->configFactory = $config_factory;
diff --git a/core/lib/Drupal/Core/ImageToolkit/ImageToolkitOperationManager.php b/core/lib/Drupal/Core/ImageToolkit/ImageToolkitOperationManager.php
index ae7d0dd..dc4abaa 100644
--- a/core/lib/Drupal/Core/ImageToolkit/ImageToolkitOperationManager.php
+++ b/core/lib/Drupal/Core/ImageToolkit/ImageToolkitOperationManager.php
@@ -46,7 +46,7 @@ class ImageToolkitOperationManager extends DefaultPluginManager implements Image
    *   A logger instance.
    */
   public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, LoggerInterface $logger) {
-    parent::__construct('Plugin/ImageToolkit/Operation', $namespaces, $module_handler, 'Drupal\Core\ImageToolkit\Annotation\ImageToolkitOperation');
+    parent::__construct('Plugin/ImageToolkit/Operation', $namespaces, $module_handler, 'Drupal\Core\ImageToolkit\ImageToolkitOperationInterface', 'Drupal\Core\ImageToolkit\Annotation\ImageToolkitOperation');
 
     $this->alterInfo('image_toolkit_operation');
     $this->setCacheBackend($cache_backend, 'image_toolkit_operation_plugins');
diff --git a/core/lib/Drupal/Core/Mail/MailManager.php b/core/lib/Drupal/Core/Mail/MailManager.php
index 48e53cf..decbeed 100644
--- a/core/lib/Drupal/Core/Mail/MailManager.php
+++ b/core/lib/Drupal/Core/Mail/MailManager.php
@@ -12,8 +12,6 @@
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Config\ConfigFactoryInterface;
-use Drupal\Component\Utility\String;
-use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\Core\StringTranslation\TranslationInterface;
 
@@ -67,7 +65,7 @@ class MailManager extends DefaultPluginManager implements MailManagerInterface {
    *   The string translation service.
    */
   public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, ConfigFactoryInterface $config_factory, LoggerChannelFactoryInterface $logger_factory, TranslationInterface $string_translation) {
-    parent::__construct('Plugin/Mail', $namespaces, $module_handler, 'Drupal\Core\Annotation\Mail');
+    parent::__construct('Plugin/Mail', $namespaces, $module_handler, 'Drupal\Core\Mail\MailInterface', 'Drupal\Core\Annotation\Mail');
     $this->alterInfo('mail_backend_info');
     $this->setCacheBackend($cache_backend, 'mail_backend_plugins');
     $this->configFactory = $config_factory;
@@ -149,16 +147,7 @@ public function getInstance(array $options) {
     }
 
     if (empty($this->instances[$plugin_id])) {
-      $plugin = $this->createInstance($plugin_id);
-      if (is_subclass_of($plugin, '\Drupal\Core\Mail\MailInterface')) {
-        $this->instances[$plugin_id] = $plugin;
-      }
-      else {
-        throw new InvalidPluginDefinitionException($plugin_id, String::format('Class %class does not implement interface %interface', array(
-          '%class' => get_class($plugin),
-          '%interface' => 'Drupal\Core\Mail\MailInterface',
-        )));
-      }
+      $this->instances[$plugin_id] = $this->createInstance($plugin_id);
     }
     return $this->instances[$plugin_id];
   }
diff --git a/core/lib/Drupal/Core/Menu/ContextualLinkManager.php b/core/lib/Drupal/Core/Menu/ContextualLinkManager.php
index 8c33949..b99c11f 100644
--- a/core/lib/Drupal/Core/Menu/ContextualLinkManager.php
+++ b/core/lib/Drupal/Core/Menu/ContextualLinkManager.php
@@ -105,7 +105,7 @@ class ContextualLinkManager extends DefaultPluginManager implements ContextualLi
   public function __construct(ControllerResolverInterface $controller_resolver, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache_backend, LanguageManagerInterface $language_manager, AccessManagerInterface $access_manager, AccountInterface $account, RequestStack $request_stack) {
     $this->discovery = new YamlDiscovery('links.contextual', $module_handler->getModuleDirectories());
     $this->discovery = new ContainerDerivativeDiscoveryDecorator($this->discovery);
-    $this->factory = new ContainerFactory($this);
+    $this->factory = new ContainerFactory($this, '\Drupal\Core\Menu\ContextualLinkInterface');
 
     $this->controllerResolver = $controller_resolver;
     $this->accessManager = $access_manager;
diff --git a/core/lib/Drupal/Core/Menu/LocalActionManager.php b/core/lib/Drupal/Core/Menu/LocalActionManager.php
index 43f0e19..1033d79 100644
--- a/core/lib/Drupal/Core/Menu/LocalActionManager.php
+++ b/core/lib/Drupal/Core/Menu/LocalActionManager.php
@@ -116,7 +116,7 @@ public function __construct(ControllerResolverInterface $controller_resolver, Re
     // discovery.
     $this->discovery = new YamlDiscovery('links.action', $module_handler->getModuleDirectories());
     $this->discovery = new ContainerDerivativeDiscoveryDecorator($this->discovery);
-    $this->factory = new ContainerFactory($this);
+    $this->factory = new ContainerFactory($this, 'Drupal\Core\Menu\LocalActionInterface');
     $this->controllerResolver = $controller_resolver;
     $this->requestStack = $request_stack;
     $this->routeProvider = $route_provider;
diff --git a/core/lib/Drupal/Core/Menu/LocalTaskManager.php b/core/lib/Drupal/Core/Menu/LocalTaskManager.php
index a6bc10e..862050c 100644
--- a/core/lib/Drupal/Core/Menu/LocalTaskManager.php
+++ b/core/lib/Drupal/Core/Menu/LocalTaskManager.php
@@ -126,7 +126,7 @@ class LocalTaskManager extends DefaultPluginManager implements LocalTaskManagerI
   public function __construct(ControllerResolverInterface $controller_resolver, RequestStack $request_stack, RouteProviderInterface $route_provider, RouteBuilderInterface $route_builder, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, LanguageManagerInterface $language_manager, AccessManagerInterface $access_manager, AccountInterface $account) {
     $this->discovery = new YamlDiscovery('links.task', $module_handler->getModuleDirectories());
     $this->discovery = new ContainerDerivativeDiscoveryDecorator($this->discovery);
-    $this->factory = new ContainerFactory($this);
+    $this->factory = new ContainerFactory($this, '\Drupal\Core\Menu\LocalTaskInterface');
     $this->controllerResolver = $controller_resolver;
     $this->requestStack = $request_stack;
     $this->routeProvider = $route_provider;
diff --git a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
index e1d0731..a2d4cf2 100644
--- a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
+++ b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
@@ -9,7 +9,6 @@
 
 use Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface;
 use Drupal\Component\Plugin\Discovery\DiscoveryCachedTrait;
-use Drupal\Component\Plugin\Exception\PluginNotFoundException;
 use Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator;
 use Drupal\Component\Plugin\PluginManagerBase;
 use Drupal\Component\Plugin\PluginManagerInterface;
@@ -17,7 +16,6 @@
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
-use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
 use Drupal\Core\Plugin\Factory\ContainerFactory;
 
@@ -92,15 +90,17 @@ class DefaultPluginManager extends PluginManagerBase implements PluginManagerInt
    *   keyed by the corresponding namespace to look for plugin implementations.
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
    *   The module handler.
+   * @param string|null $plugin_interface
+   *   (optional) The interface each plugin should implement.
    * @param string $plugin_definition_annotation_name
    *   (optional) The name of the annotation that contains the plugin definition.
    *   Defaults to 'Drupal\Component\Annotation\Plugin'.
    */
-  public function __construct($subdir, \Traversable $namespaces, ModuleHandlerInterface $module_handler, $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin') {
+  public function __construct($subdir, \Traversable $namespaces, ModuleHandlerInterface $module_handler, $plugin_interface = NULL, $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin') {
     $this->subdir = $subdir;
     $this->discovery = new AnnotatedClassDiscovery($subdir, $namespaces, $plugin_definition_annotation_name);
     $this->discovery = new ContainerDerivativeDiscoveryDecorator($this->discovery);
-    $this->factory = new ContainerFactory($this);
+    $this->factory = new ContainerFactory($this, $plugin_interface);
     $this->moduleHandler = $module_handler;
   }
 
diff --git a/core/lib/Drupal/Core/Plugin/Factory/ContainerFactory.php b/core/lib/Drupal/Core/Plugin/Factory/ContainerFactory.php
index 702e640..16191f8 100644
--- a/core/lib/Drupal/Core/Plugin/Factory/ContainerFactory.php
+++ b/core/lib/Drupal/Core/Plugin/Factory/ContainerFactory.php
@@ -18,7 +18,7 @@ class ContainerFactory extends DefaultFactory {
    */
   public function createInstance($plugin_id, array $configuration = array()) {
     $plugin_definition = $this->discovery->getDefinition($plugin_id);
-    $plugin_class = static::getPluginClass($plugin_id, $plugin_definition);
+    $plugin_class = static::getPluginClass($plugin_id, $plugin_definition, $this->interface);
 
     // If the plugin provides a factory method, pass the container to it.
     if (is_subclass_of($plugin_class, 'Drupal\Core\Plugin\ContainerFactoryPluginInterface')) {
diff --git a/core/lib/Drupal/Core/Render/Element/Page.php b/core/lib/Drupal/Core/Render/Element/Page.php
index 4156ab4..f12c8f3 100644
--- a/core/lib/Drupal/Core/Render/Element/Page.php
+++ b/core/lib/Drupal/Core/Render/Element/Page.php
@@ -12,7 +12,7 @@
  *
  * @RenderElement("page")
  */
-class Page {
+class Page extends RenderElement {
 
   /**
    * {@inheritdoc}
diff --git a/core/lib/Drupal/Core/Render/ElementInfoManager.php b/core/lib/Drupal/Core/Render/ElementInfoManager.php
index b9f78fc..784fdef 100644
--- a/core/lib/Drupal/Core/Render/ElementInfoManager.php
+++ b/core/lib/Drupal/Core/Render/ElementInfoManager.php
@@ -46,7 +46,7 @@ class ElementInfoManager extends DefaultPluginManager implements ElementInfoMana
   public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
     $this->setCacheBackend($cache_backend, 'element_info');
 
-    parent::__construct('Element', $namespaces, $module_handler, 'Drupal\Core\Render\Annotation\RenderElement');
+    parent::__construct('Element', $namespaces, $module_handler, 'Drupal\Core\Render\Element\ElementInterface', 'Drupal\Core\Render\Annotation\RenderElement');
   }
 
   /**
diff --git a/core/lib/Drupal/Core/TypedData/TypedDataManager.php b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
index c9afe86..d8b70f6 100644
--- a/core/lib/Drupal/Core/TypedData/TypedDataManager.php
+++ b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
@@ -59,7 +59,7 @@ public function __construct(\Traversable $namespaces, CacheBackendInterface $cac
     $this->alterInfo('data_type_info');
     $this->setCacheBackend($cache_backend, 'typed_data_types_plugins');
 
-    parent::__construct('Plugin/DataType', $namespaces, $module_handler, 'Drupal\Core\TypedData\Annotation\DataType');
+    parent::__construct('Plugin/DataType', $namespaces, $module_handler, NULL, 'Drupal\Core\TypedData\Annotation\DataType');
   }
 
   /**
diff --git a/core/modules/aggregator/src/Plugin/AggregatorPluginManager.php b/core/modules/aggregator/src/Plugin/AggregatorPluginManager.php
index 53c3acd..e1cccc7 100644
--- a/core/modules/aggregator/src/Plugin/AggregatorPluginManager.php
+++ b/core/modules/aggregator/src/Plugin/AggregatorPluginManager.php
@@ -44,8 +44,13 @@ public function __construct($type, \Traversable $namespaces, CacheBackendInterfa
       'parser' => 'Drupal\aggregator\Annotation\AggregatorParser',
       'processor' => 'Drupal\aggregator\Annotation\AggregatorProcessor',
     );
+    $plugin_interfaces = array(
+      'fetcher' => 'Drupal\aggregator\Plugin\FetcherInterface',
+      'parser' => 'Drupal\aggregator\Plugin\ParserInterface',
+      'processor' => 'Drupal\aggregator\Plugin\ProcessorInterface',
+    );
 
-    parent::__construct("Plugin/aggregator/$type", $namespaces, $module_handler, $type_annotations[$type]);
+    parent::__construct("Plugin/aggregator/$type", $namespaces, $module_handler, $plugin_interfaces[$type], $type_annotations[$type]);
     $this->setCacheBackend($cache_backend, 'aggregator_' . $type . '_plugins');
   }
 
diff --git a/core/modules/ckeditor/src/CKEditorPluginManager.php b/core/modules/ckeditor/src/CKEditorPluginManager.php
index 289c906..1f05833 100644
--- a/core/modules/ckeditor/src/CKEditorPluginManager.php
+++ b/core/modules/ckeditor/src/CKEditorPluginManager.php
@@ -39,7 +39,7 @@ class CKEditorPluginManager extends DefaultPluginManager {
    *   The module handler to invoke the alter hook with.
    */
   public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
-    parent::__construct('Plugin/CKEditorPlugin', $namespaces, $module_handler, 'Drupal\ckeditor\Annotation\CKEditorPlugin');
+    parent::__construct('Plugin/CKEditorPlugin', $namespaces, $module_handler, 'Drupal\ckeditor\CKEditorPluginInterface', 'Drupal\ckeditor\Annotation\CKEditorPlugin');
     $this->alterInfo('ckeditor_plugin_info');
     $this->setCacheBackend($cache_backend, 'ckeditor_plugins');
   }
diff --git a/core/modules/config_translation/src/ConfigMapperManager.php b/core/modules/config_translation/src/ConfigMapperManager.php
index 8eb3735..6c99ce1 100644
--- a/core/modules/config_translation/src/ConfigMapperManager.php
+++ b/core/modules/config_translation/src/ConfigMapperManager.php
@@ -90,7 +90,7 @@ public function __construct(CacheBackendInterface $cache_backend, LanguageManage
     $this->discovery = new InfoHookDecorator($this->discovery, 'config_translation_info');
     $this->discovery = new ContainerDerivativeDiscoveryDecorator($this->discovery);
 
-    $this->factory = new ContainerFactory($this);
+    $this->factory = new ContainerFactory($this, '\Drupal\config_translation\ConfigMapperInterface');
 
     // Let others alter definitions with hook_config_translation_info_alter().
     $this->moduleHandler = $module_handler;
diff --git a/core/modules/editor/src/Plugin/EditorManager.php b/core/modules/editor/src/Plugin/EditorManager.php
index 1dd6df2..da0081e 100644
--- a/core/modules/editor/src/Plugin/EditorManager.php
+++ b/core/modules/editor/src/Plugin/EditorManager.php
@@ -33,7 +33,7 @@ class EditorManager extends DefaultPluginManager {
    *   The module handler to invoke the alter hook with.
    */
   public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
-    parent::__construct('Plugin/Editor', $namespaces, $module_handler, 'Drupal\editor\Annotation\Editor');
+    parent::__construct('Plugin/Editor', $namespaces, $module_handler, 'Drupal\editor\Plugin\EditorPluginInterface', 'Drupal\editor\Annotation\Editor');
     $this->alterInfo('editor_info');
     $this->setCacheBackend($cache_backend, 'editor_plugins');
   }
diff --git a/core/modules/entity_reference/src/Plugin/Type/SelectionPluginManager.php b/core/modules/entity_reference/src/Plugin/Type/SelectionPluginManager.php
index ce5ff83..b4e17a1 100644
--- a/core/modules/entity_reference/src/Plugin/Type/SelectionPluginManager.php
+++ b/core/modules/entity_reference/src/Plugin/Type/SelectionPluginManager.php
@@ -36,7 +36,7 @@ public function __construct(\Traversable $namespaces, CacheBackendInterface $cac
 
     // We're not using the parent constructor because we use a different factory
     // method and don't need the derivative discovery decorator.
-    $this->factory = new ReflectionFactory($this);
+    $this->factory = new ReflectionFactory($this, '\Drupal\entity_reference\Plugin\Type\Selection\SelectionInterface');
 
     $this->moduleHandler = $module_handler;
     $this->alterInfo('entity_reference_selection');
diff --git a/core/modules/filter/src/FilterPluginManager.php b/core/modules/filter/src/FilterPluginManager.php
index 77a3c71..8eb1144 100644
--- a/core/modules/filter/src/FilterPluginManager.php
+++ b/core/modules/filter/src/FilterPluginManager.php
@@ -34,7 +34,7 @@ class FilterPluginManager extends DefaultPluginManager {
    *   The module handler to invoke the alter hook with.
    */
   public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
-    parent::__construct('Plugin/Filter', $namespaces, $module_handler, 'Drupal\filter\Annotation\Filter');
+    parent::__construct('Plugin/Filter', $namespaces, $module_handler, 'Drupal\filter\Plugin\FilterInterface', 'Drupal\filter\Annotation\Filter');
     $this->alterInfo('filter_info');
     $this->setCacheBackend($cache_backend, 'filter_plugins', array('filter_formats' => TRUE));
   }
diff --git a/core/modules/image/src/ImageEffectManager.php b/core/modules/image/src/ImageEffectManager.php
index 762f947..aa4c24b 100644
--- a/core/modules/image/src/ImageEffectManager.php
+++ b/core/modules/image/src/ImageEffectManager.php
@@ -36,7 +36,7 @@ class ImageEffectManager extends DefaultPluginManager {
    *   The module handler.
    */
   public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
-    parent::__construct('Plugin/ImageEffect', $namespaces, $module_handler, 'Drupal\image\Annotation\ImageEffect');
+    parent::__construct('Plugin/ImageEffect', $namespaces, $module_handler, 'Drupal\image\ImageEffectInterface', 'Drupal\image\Annotation\ImageEffect');
 
     $this->alterInfo('image_effect_info');
     $this->setCacheBackend($cache_backend, 'image_effect_plugins');
diff --git a/core/modules/language/src/LanguageNegotiationMethodManager.php b/core/modules/language/src/LanguageNegotiationMethodManager.php
index 681c67c..b263ed5 100644
--- a/core/modules/language/src/LanguageNegotiationMethodManager.php
+++ b/core/modules/language/src/LanguageNegotiationMethodManager.php
@@ -28,7 +28,7 @@ class LanguageNegotiationMethodManager extends DefaultPluginManager {
    *   An object that implements ModuleHandlerInterface
    */
   public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
-    parent::__construct('Plugin/LanguageNegotiation', $namespaces, $module_handler);
+    parent::__construct('Plugin/LanguageNegotiation', $namespaces, $module_handler, 'Drupal\language\LanguageNegotiationMethodInterface');
     $this->cacheBackend = $cache_backend;
     $this->cacheKeyPrefix = 'language_negotiation_plugins';
     $this->cacheKey = 'language_negotiation_plugins';
diff --git a/core/modules/migrate/src/Plugin/MigratePluginManager.php b/core/modules/migrate/src/Plugin/MigratePluginManager.php
index 0ae77be..67cce89 100644
--- a/core/modules/migrate/src/Plugin/MigratePluginManager.php
+++ b/core/modules/migrate/src/Plugin/MigratePluginManager.php
@@ -2,7 +2,7 @@
 
 /**
  * @file
- * Contains \Drupal\migrate\MigraterPluginManager.
+ * Contains \Drupal\migrate\Plugin\MigratePluginManager.
  */
 
 namespace Drupal\migrate\Plugin;
@@ -46,7 +46,15 @@ class MigratePluginManager extends DefaultPluginManager {
    *   The annotation class name.
    */
   public function __construct($type, \Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, $annotation = 'Drupal\Component\Annotation\PluginID') {
-    parent::__construct("Plugin/migrate/$type", $namespaces, $module_handler, $annotation);
+    $plugin_interface_map = array(
+      'destination' => 'Drupal\migrate\Plugin\MigrateDestinationInterface',
+      'process' => 'Drupal\migrate\Plugin\MigrateProcessInterface',
+      'source' => 'Drupal\migrate\Plugin\MigrateSourceInterface',
+      'id_map' => 'Drupal\migrate\Plugin\MigrateIdMapInterface',
+      'entity_field' => 'Drupal\migrate\Plugin\MigrateEntityDestinationFieldInterface',
+    );
+    $plugin_interface = isset($plugin_interface_map[$type]) ? $plugin_interface_map[$type] : NULL;
+    parent::__construct("Plugin/migrate/$type", $namespaces, $module_handler, $plugin_interface, $annotation);
     $this->alterInfo('migrate_' . $type . '_info');
     $this->setCacheBackend($cache_backend, 'migrate_plugins_' . $type);
   }
diff --git a/core/modules/migrate_drupal/migrate_drupal.services.yml b/core/modules/migrate_drupal/migrate_drupal.services.yml
index 6dd03c0..2c8f00c 100644
--- a/core/modules/migrate_drupal/migrate_drupal.services.yml
+++ b/core/modules/migrate_drupal/migrate_drupal.services.yml
@@ -1,4 +1,4 @@
 services:
   plugin.manager.migrate.load:
-    class: Drupal\migrate\Plugin\MigratePluginManager
+    class: Drupal\migrate_drupal\Plugin\MigratePluginManager
     arguments: [load, '@container.namespaces', '@cache.discovery', '@module_handler']
diff --git a/core/modules/migrate_drupal/src/Plugin/MigrateLoadInterface.php b/core/modules/migrate_drupal/src/Plugin/MigrateLoadInterface.php
index 34dd07b..ce3ed83 100644
--- a/core/modules/migrate_drupal/src/Plugin/MigrateLoadInterface.php
+++ b/core/modules/migrate_drupal/src/Plugin/MigrateLoadInterface.php
@@ -2,11 +2,12 @@
 
 /**
  * @file
- * Contains Drupal\migrate\Plugin\MigrateLoadInterface
+ * Contains Drupal\migrate_drupal\Plugin\MigrateLoadInterface
  */
 
 namespace Drupal\migrate_drupal\Plugin;
 
+use Drupal\Component\Plugin\PluginInspectionInterface;
 use Drupal\Core\Entity\EntityStorageInterface;
 
 /**
@@ -16,7 +17,7 @@
  *
  * @ingroup migration
  */
-interface MigrateLoadInterface {
+interface MigrateLoadInterface extends PluginInspectionInterface {
 
   /**
    * Load an additional migration.
diff --git a/core/modules/migrate_drupal/src/Plugin/MigratePluginManager.php b/core/modules/migrate_drupal/src/Plugin/MigratePluginManager.php
new file mode 100644
index 0000000..c93ff07
--- /dev/null
+++ b/core/modules/migrate_drupal/src/Plugin/MigratePluginManager.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\migrate_drupal\Plugin\MigratePluginManager.
+ */
+
+namespace Drupal\migrate_drupal\Plugin;
+
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Plugin\Factory\ContainerFactory;
+use Drupal\migrate\Plugin\MigratePluginManager as BaseMigratePluginManager;
+
+/**
+ * Manages migrate_drupal plugins.
+ *
+ * @see plugin_api
+ *
+ * @ingroup migration
+ */
+class MigratePluginManager extends BaseMigratePluginManager {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function __construct($type, \Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, $annotation = 'Drupal\Component\Annotation\PluginID') {
+    parent::__construct($type, $namespaces, $cache_backend, $module_handler, $annotation);
+
+    $this->factory = new ContainerFactory($this, 'Drupal\migrate_drupal\Plugin\MigrateLoadInterface');
+  }
+
+
+}
diff --git a/core/modules/quickedit/src/Plugin/InPlaceEditorManager.php b/core/modules/quickedit/src/Plugin/InPlaceEditorManager.php
index 876da2d..210f675 100644
--- a/core/modules/quickedit/src/Plugin/InPlaceEditorManager.php
+++ b/core/modules/quickedit/src/Plugin/InPlaceEditorManager.php
@@ -35,7 +35,7 @@ class InPlaceEditorManager extends DefaultPluginManager {
    *   The module handler to invoke the alter hook with.
    */
   public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
-    parent::__construct('Plugin/InPlaceEditor', $namespaces, $module_handler, 'Drupal\quickedit\Annotation\InPlaceEditor');
+    parent::__construct('Plugin/InPlaceEditor', $namespaces, $module_handler, 'Drupal\quickedit\Plugin\InPlaceEditorInterface', 'Drupal\quickedit\Annotation\InPlaceEditor');
     $this->alterInfo('quickedit_editor');
     $this->setCacheBackend($cache_backend, 'quickedit:editor');
   }
diff --git a/core/modules/rest/src/Plugin/Type/ResourcePluginManager.php b/core/modules/rest/src/Plugin/Type/ResourcePluginManager.php
index ec9cb9f..97594c4 100644
--- a/core/modules/rest/src/Plugin/Type/ResourcePluginManager.php
+++ b/core/modules/rest/src/Plugin/Type/ResourcePluginManager.php
@@ -33,7 +33,7 @@ class ResourcePluginManager extends DefaultPluginManager {
    *   The module handler to invoke the alter hook with.
    */
   public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
-    parent::__construct('Plugin/rest/resource', $namespaces, $module_handler, 'Drupal\rest\Annotation\RestResource');
+    parent::__construct('Plugin/rest/resource', $namespaces, $module_handler, 'Drupal\rest\Plugin\ResourceInterface', 'Drupal\rest\Annotation\RestResource');
 
     $this->setCacheBackend($cache_backend, 'rest_plugins');
     $this->alterInfo('rest_resource');
diff --git a/core/modules/search/src/SearchPluginManager.php b/core/modules/search/src/SearchPluginManager.php
index 5670e71..bad133a 100644
--- a/core/modules/search/src/SearchPluginManager.php
+++ b/core/modules/search/src/SearchPluginManager.php
@@ -28,7 +28,7 @@ class SearchPluginManager extends DefaultPluginManager {
    *   The module handler to invoke the alter hook with.
    */
   public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
-    parent::__construct('Plugin/Search', $namespaces, $module_handler, 'Drupal\search\Annotation\SearchPlugin');
+    parent::__construct('Plugin/Search', $namespaces, $module_handler, 'Drupal\search\Plugin\SearchInterface', 'Drupal\search\Annotation\SearchPlugin');
     $this->setCacheBackend($cache_backend, 'search_plugins');
     $this->alterInfo('search_plugin');
   }
diff --git a/core/modules/system/src/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php b/core/modules/system/src/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php
index 941a93a..101dee6 100644
--- a/core/modules/system/src/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php
+++ b/core/modules/system/src/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php
@@ -43,6 +43,13 @@ protected function setUp() {
         'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\Cherry',
         'provider' => 'plugin_test',
       ),
+      'kale' => array(
+        'id' => 'kale',
+        'label' => 'Kale',
+        'color' => 'green',
+        'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\Kale',
+        'provider' => 'plugin_test',
+      ),
       'orange' => array(
         'id' => 'orange',
         'label' => 'Orange',
diff --git a/core/modules/system/src/Tests/Plugin/Discovery/CustomDirectoryAnnotatedClassDiscoveryTest.php b/core/modules/system/src/Tests/Plugin/Discovery/CustomDirectoryAnnotatedClassDiscoveryTest.php
index aa29a09..92a73f3 100644
--- a/core/modules/system/src/Tests/Plugin/Discovery/CustomDirectoryAnnotatedClassDiscoveryTest.php
+++ b/core/modules/system/src/Tests/Plugin/Discovery/CustomDirectoryAnnotatedClassDiscoveryTest.php
@@ -57,6 +57,13 @@ protected function setUp() {
         'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\Cherry',
         'provider' => 'plugin_test',
       ),
+      'kale' => array(
+        'id' => 'kale',
+        'label' => 'Kale',
+        'color' => 'green',
+        'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\Kale',
+        'provider' => 'plugin_test',
+      ),
       'orange' => array(
         'id' => 'orange',
         'label' => 'Orange',
diff --git a/core/modules/system/tests/modules/plugin_test/src/Plugin/DefaultsTestPluginManager.php b/core/modules/system/tests/modules/plugin_test/src/Plugin/DefaultsTestPluginManager.php
index e81c39e..bda80fa 100644
--- a/core/modules/system/tests/modules/plugin_test/src/Plugin/DefaultsTestPluginManager.php
+++ b/core/modules/system/tests/modules/plugin_test/src/Plugin/DefaultsTestPluginManager.php
@@ -29,7 +29,7 @@ public function __construct(ModuleHandlerInterface $module_handler) {
     // discovery implementation, but StaticDiscovery lets us add some simple
     // mock plugins for unit testing.
     $this->discovery = new StaticDiscovery();
-    $this->factory = new DefaultFactory($this);
+    $this->factory = new DefaultFactory($this, 'Drupal\Component\Plugin\PluginInspectionInterface');
     $this->moduleHandler = $module_handler;
 
     // Specify default values.
diff --git a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Apple.php b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Apple.php
index 9aabeca..d81b6ba 100644
--- a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Apple.php
+++ b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Apple.php
@@ -14,4 +14,4 @@
  *   color = "green"
  * )
  */
-class Apple {}
+class Apple implements FruitInterface {}
diff --git a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Banana.php b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Banana.php
index 4801da5..5e4d180 100644
--- a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Banana.php
+++ b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Banana.php
@@ -17,6 +17,6 @@
  *   }
  * )
  */
-class Banana {
+class Banana implements FruitInterface {
 
 }
diff --git a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Cherry.php b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Cherry.php
index d22b90a..30db552 100644
--- a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Cherry.php
+++ b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Cherry.php
@@ -14,4 +14,4 @@
  *   color = "red"
  * )
  */
-class Cherry {}
+class Cherry implements FruitInterface {}
diff --git a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/FruitInterface.php b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/FruitInterface.php
new file mode 100644
index 0000000..4c26071
--- /dev/null
+++ b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/FruitInterface.php
@@ -0,0 +1,14 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface.
+ */
+
+namespace Drupal\plugin_test\Plugin\plugin_test\fruit;
+
+/**
+ * Provides an interface for test plugins.
+ */
+interface FruitInterface { }
+
diff --git a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Apple.php b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Kale.php
similarity index 50%
copy from core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Apple.php
copy to core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Kale.php
index 9aabeca..9c69b15 100644
--- a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Apple.php
+++ b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Kale.php
@@ -2,16 +2,16 @@
 
 /**
  * @file
- * Definition of Drupal\plugin_test\Plugin\plugin_test\fruit\Apple.
+ * Contains \Drupal\plugin_test\Plugin\plugin_test\fruit\Kale.
  */
 
 namespace Drupal\plugin_test\Plugin\plugin_test\fruit;
 
 /**
  * @Plugin(
- *   id = "apple",
- *   label = "Apple",
+ *   id = "kale",
+ *   label = "Kale",
  *   color = "green"
  * )
  */
-class Apple {}
+class Kale {}
diff --git a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Orange.php b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Orange.php
index 27d7d00..10ba4ea 100644
--- a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Orange.php
+++ b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Orange.php
@@ -14,4 +14,4 @@
  *   color = "orange"
  * )
  */
-class Orange {}
+class Orange implements FruitInterface {}
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index d58064d..e7e4f7c 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -688,8 +688,21 @@ function taxonomy_implode_tags($tags, $vid = NULL) {
  * Implements hook_field_widget_info_alter().
  */
 function taxonomy_field_widget_info_alter(&$info) {
-  $info['options_select']['field_types'][] = 'taxonomy_term_reference';
-  $info['options_buttons']['field_types'][] = 'taxonomy_term_reference';
+  if (isset($info['options_select'])) {
+    $info['options_select']['field_types'][] = 'taxonomy_term_reference';
+  }
+  if (isset($info['options_buttons'])) {
+    $info['options_buttons']['field_types'][] = 'taxonomy_term_reference';
+  }
+}
+
+/**
+ * Implements hook_field_formatter_info_alter().
+ */
+function taxonomy_field_formatter_info_alter(array &$info) {
+  if (!\Drupal::moduleHandler()->moduleExists('entity_reference')) {
+    unset($info['entity_reference_rss_category']);
+  }
 }
 
 /**
diff --git a/core/modules/tour/src/TipPluginManager.php b/core/modules/tour/src/TipPluginManager.php
index 6f98f62..bcd2c78 100644
--- a/core/modules/tour/src/TipPluginManager.php
+++ b/core/modules/tour/src/TipPluginManager.php
@@ -33,7 +33,7 @@ class TipPluginManager extends DefaultPluginManager {
    *   The module handler to invoke the alter hook with.
    */
   public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
-    parent::__construct('Plugin/tour/tip', $namespaces, $module_handler, 'Drupal\tour\Annotation\Tip');
+    parent::__construct('Plugin/tour/tip', $namespaces, $module_handler, 'Drupal\tour\TipPluginInterface', 'Drupal\tour\Annotation\Tip');
 
     $this->alterInfo('tour_tips_info');
     $this->setCacheBackend($cache_backend, 'tour_plugins');
diff --git a/core/modules/update/tests/modules/update_test/src/Plugin/Archiver/UpdateTestArchiver.php b/core/modules/update/tests/modules/update_test/src/Plugin/Archiver/UpdateTestArchiver.php
new file mode 100644
index 0000000..330d8ab
--- /dev/null
+++ b/core/modules/update/tests/modules/update_test/src/Plugin/Archiver/UpdateTestArchiver.php
@@ -0,0 +1,51 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\update_test\Plugin\Archiver\UpdateTestArchiver.
+ */
+
+namespace Drupal\update_test\Plugin\Archiver;
+
+use Drupal\Core\Archiver\ArchiverInterface;
+
+/**
+ * Defines a test archiver implementation.
+ *
+ * @Archiver(
+ *   id = "update_test_archiver",
+ *   title = @Translation("Update Test Archiver"),
+ *   extensions = {"update-test-extension"}
+ * )
+ */
+class UpdateTestArchiver implements ArchiverInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function add($file_path) {
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function remove($path) {
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function extract($path, array $files = array()) {
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function listContents() {
+    return array();
+  }
+
+}
diff --git a/core/modules/update/tests/modules/update_test/update_test.module b/core/modules/update/tests/modules/update_test/update_test.module
index 536d330..93c97f4 100644
--- a/core/modules/update/tests/modules/update_test/update_test.module
+++ b/core/modules/update/tests/modules/update_test/update_test.module
@@ -108,19 +108,6 @@ function update_test_mock_page($project_name) {
 }
 
 /**
- * Implements hook_archiver_info_alter().
- */
-function update_test_archiver_info_alter(&$definitions) {
-  // We only care about the extensions for now and the only way to add a new
-  // extension without a class is via alter. This definition is bogus, real
-  // archivers have a class and the definition goes in the annotation of the
-  // class.
-  $definitions['update_test_archiver'] = array(
-    'extensions' => array('update-test-extension'),
-  );
-}
-
-/**
  * Implements hook_filetransfer_info().
  */
 function update_test_filetransfer_info() {
diff --git a/core/modules/views/src/Form/ViewsExposedForm.php b/core/modules/views/src/Form/ViewsExposedForm.php
index 618a737..6b6d610 100644
--- a/core/modules/views/src/Form/ViewsExposedForm.php
+++ b/core/modules/views/src/Form/ViewsExposedForm.php
@@ -81,7 +81,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
 
     // Go through each handler and let it generate its exposed widget.
     foreach ($view->display_handler->handlers as $type => $value) {
-      /** @var \Drupal\views\Plugin\views\HandlerBase $handler */
+      /** @var \Drupal\views\Plugin\views\ViewsHandlerInterface $handler */
       foreach ($view->$type as $id => $handler) {
         if ($handler->canExpose() && $handler->isExposed()) {
           // Grouped exposed filters have their own forms.
@@ -134,7 +134,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
    */
   public function validateForm(array &$form, FormStateInterface $form_state) {
     foreach (array('field', 'filter') as $type) {
-      /** @var \Drupal\views\Plugin\views\HandlerBase[] $handlers */
+      /** @var \Drupal\views\Plugin\views\ViewsHandlerInterface[] $handlers */
       $handlers = &$form_state['view']->$type;
       foreach ($handlers as $key => $handler) {
         $handlers[$key]->validateExposed($form, $form_state);
@@ -150,7 +150,7 @@ public function validateForm(array &$form, FormStateInterface $form_state) {
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
     foreach (array('field', 'filter') as $type) {
-      /** @var \Drupal\views\Plugin\views\HandlerBase[] $handlers */
+      /** @var \Drupal\views\Plugin\views\ViewsHandlerInterface[] $handlers */
       $handlers = &$form_state['view']->$type;
       foreach ($handlers as $key => $info) {
         $handlers[$key]->submitExposed($form, $form_state);
diff --git a/core/modules/views/src/Plugin/ViewsHandlerManager.php b/core/modules/views/src/Plugin/ViewsHandlerManager.php
index 7b7b76e..e7e1c89 100644
--- a/core/modules/views/src/Plugin/ViewsHandlerManager.php
+++ b/core/modules/views/src/Plugin/ViewsHandlerManager.php
@@ -52,7 +52,11 @@ class ViewsHandlerManager extends DefaultPluginManager {
    */
   public function __construct($handler_type, \Traversable $namespaces, ViewsData $views_data, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
     $plugin_definition_annotation_name = 'Drupal\views\Annotation\Views' . Container::camelize($handler_type);
-    parent::__construct("Plugin/views/$handler_type", $namespaces, $module_handler, $plugin_definition_annotation_name);
+    $plugin_interface = 'Drupal\views\Plugin\views\ViewsHandlerInterface';
+    if ($handler_type == 'join') {
+      $plugin_interface = 'Drupal\views\Plugin\views\join\JoinPluginInterface';
+    }
+    parent::__construct("Plugin/views/$handler_type", $namespaces, $module_handler, $plugin_interface, $plugin_definition_annotation_name);
 
     $this->setCacheBackend($cache_backend, "views:$handler_type", array('extension' => array(TRUE, 'views')));
 
@@ -74,7 +78,7 @@ public function __construct($handler_type, \Traversable $namespaces, ViewsData $
    *   (optional) Override the actual handler object with this plugin ID. Used for
    *   aggregation when the handler is redirected to the aggregation handler.
    *
-   * @return \Drupal\views\Plugin\views\HandlerBase
+   * @return \Drupal\views\Plugin\views\ViewsHandlerInterface
    *   An instance of a handler object. May be a broken handler instance.
    */
   public function getHandler($item, $override = NULL) {
diff --git a/core/modules/views/src/Plugin/ViewsPluginManager.php b/core/modules/views/src/Plugin/ViewsPluginManager.php
index ca98555..3b43981 100644
--- a/core/modules/views/src/Plugin/ViewsPluginManager.php
+++ b/core/modules/views/src/Plugin/ViewsPluginManager.php
@@ -34,7 +34,7 @@ class ViewsPluginManager extends DefaultPluginManager {
    */
   public function __construct($type, \Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
     $plugin_definition_annotation_name = 'Drupal\views\Annotation\Views' . Container::camelize($type);
-    parent::__construct("Plugin/views/$type", $namespaces, $module_handler, $plugin_definition_annotation_name);
+    parent::__construct("Plugin/views/$type", $namespaces, $module_handler, 'Drupal\views\Plugin\views\ViewsPluginInterface', $plugin_definition_annotation_name);
 
     $this->defaults += array(
       'parent' => 'parent',
diff --git a/core/modules/views/src/Plugin/views/HandlerBase.php b/core/modules/views/src/Plugin/views/HandlerBase.php
index ce31f43..27d24d7 100644
--- a/core/modules/views/src/Plugin/views/HandlerBase.php
+++ b/core/modules/views/src/Plugin/views/HandlerBase.php
@@ -16,7 +16,6 @@
 use Drupal\Core\Render\Element;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
-use Drupal\views\Plugin\views\PluginBase;
 use Drupal\views\ViewExecutable;
 use Drupal\Core\Database\Database;
 use Drupal\views\Views;
@@ -27,7 +26,7 @@
  *
  * @ingroup views_plugins
  */
-abstract class HandlerBase extends PluginBase {
+abstract class HandlerBase extends PluginBase implements ViewsHandlerInterface {
 
   /**
    * Where the $query object will reside:
@@ -109,7 +108,7 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition
   }
 
   /**
-   * Overrides \Drupal\views\Plugin\views\PluginBase::init().
+   * {@inheritdoc}
    */
   public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
     parent::init($view, $display, $options);
@@ -172,7 +171,7 @@ protected function defineOptions() {
   }
 
   /**
-   * Return a string representing this handler's name in the UI.
+   * {@inheritdoc}
    */
   public function adminLabel($short = FALSE) {
     if (!empty($this->options['admin_label'])) {
@@ -184,11 +183,7 @@ public function adminLabel($short = FALSE) {
   }
 
   /**
-   * Shortcut to get a handler's raw field value.
-   *
-   * This should be overridden for handlers with formulae or other
-   * non-standard fields. Because this takes an argument, fields
-   * overriding this can just call return parent::getField($formula)
+   * {@inheritdoc}
    */
   public function getField($field = NULL) {
     if (!isset($field)) {
@@ -218,15 +213,7 @@ public function getField($field = NULL) {
   }
 
   /**
-   * Sanitize the value for output.
-   *
-   * @param $value
-   *   The value being rendered.
-   * @param $type
-   *   The type of sanitization needed. If not provided, String::checkPlain() is used.
-   *
-   * @return string
-   *   Returns the safe value.
+   * {@inheritdoc}
    */
   public function sanitizeValue($value, $type = NULL) {
     switch ($type) {
@@ -277,7 +264,7 @@ protected function caseTransform($string, $option) {
   }
 
   /**
-   * Build the options form.
+   * {@inheritdoc}
    */
   public function buildOptionsForm(&$form, FormStateInterface $form_state) {
     // Some form elements belong in a fieldset for presentation, but can't
@@ -483,10 +470,7 @@ public function showExposeForm(&$form, FormStateInterface $form_state) {
   }
 
   /**
-   * Check whether current user has access to this handler.
-   *
-   * @param AccountInterface $account
-   * @return boolean
+   * {@inheritdoc}
    */
   public function access(AccountInterface $account) {
     if (isset($this->definition['access callback']) && function_exists($this->definition['access callback'])) {
@@ -500,26 +484,19 @@ public function access(AccountInterface $account) {
   }
 
   /**
-   * Run before the view is built.
-   *
-   * This gives all the handlers some time to set up before any handler has
-   * been fully run.
+   * {@inheritdoc}
    */
   public function preQuery() {
   }
 
   /**
-   * Don't run a query by default.
+   * {@inheritdoc}
    */
   public function query() {
   }
 
   /**
-   * Run after the view is executed, before the result is cached.
-   *
-   * This gives all the handlers some time to modify values. This is primarily
-   * used so that handlers that pull up secondary data can put it in the
-   * $values so that the raw data can be utilized externally.
+   * {@inheritdoc}
    */
   public function postExecute(&$values) { }
 
@@ -534,8 +511,7 @@ protected function placeholder() {
   }
 
   /**
-   * Called just prior to query(), this lets a handler set up any relationship
-   * it needs.
+   * {@inheritdoc}
    */
   public function setRelationship() {
     // Ensure this gets set to something.
@@ -564,8 +540,7 @@ public function setRelationship() {
   }
 
   /**
-   * Ensure the main table for this handler is in the query. This is used
-   * a lot.
+   * {@inheritdoc}
    */
   public function ensureMyTable() {
     if (!isset($this->tableAlias)) {
@@ -575,7 +550,7 @@ public function ensureMyTable() {
   }
 
   /**
-   * Provide text for the administrative summary.
+   * {@inheritdoc}
    */
   public function adminSummary() { }
 
@@ -612,11 +587,7 @@ public function acceptExposedInput($input) { return TRUE; }
   public function storeExposedInput($input, $status) { return TRUE; }
 
   /**
-   * Get the join object that should be used for this handler.
-   *
-   * This method isn't used a great deal, but it's very handy for easily
-   * getting the join if it is necessary to make some changes to it, such
-   * as adding an 'extra'.
+   * {@inheritdoc}
    */
   public function getJoin() {
     // get the join from this table that links back to the base table.
@@ -635,21 +606,12 @@ public function getJoin() {
   }
 
   /**
-   * Validates the handler against the complete View.
-   *
-   * This is called when the complete View is being validated. For validating
-   * the handler options form use validateOptionsForm().
-   *
-   * @see views_handler::validateOptionsForm()
-   *
-   * @return
-   *   Empty array if the handler is valid; an array of error strings if it is not.
+   * {@inheritdoc}
    */
   public function validate() { return array(); }
 
   /**
-   * Determines if the handler is considered 'broken', meaning it's a
-   * a placeholder used when a handler can't be found.
+   * {@inheritdoc}
    */
   public function broken() {
     return FALSE;
@@ -692,24 +654,14 @@ protected function getViewsData() {
   }
 
   /**
-   * Sets the views data service.
-   *
-   * @param \Drupal\views\ViewsData $views_data
-   *   The views data.
+   * {@inheritdoc}
    */
   public function setViewsData(ViewsData $views_data) {
     $this->viewsData = $views_data;
   }
 
   /**
-   * Fetches a handler to join one table to a primary table from the data cache.
-   *
-   * @param string $table
-   *   The table to join from.
-   * @param string $base_table
-   *   The table to join to.
-   *
-   * @return \Drupal\views\Plugin\views\join\JoinPluginBase
+   * {@inheritdoc}
    */
   public static function getTableJoin($table, $base_table) {
     $data = Views::viewsData()->get($table);
@@ -745,13 +697,7 @@ public static function getTableJoin($table, $base_table) {
   }
 
   /**
-   * Determines the entity type used by this handler.
-   *
-   * If this handler uses a relationship, the base class of the relationship is
-   * taken into account.
-   *
-   * @return string
-   *   The machine name of the entity type.
+   * {@inheritdoc}
    */
   public function getEntityType() {
     // If the user has configured a relationship on the handler take that into
@@ -772,15 +718,7 @@ public function getEntityType() {
   }
 
   /**
-   * Breaks x,y,z and x+y+z into an array.
-   *
-   * @param string $str
-   *   The string to split.
-   * @param bool $force_int
-   *   Enforce a numeric check.
-   *
-   * @return \stdClass
-   *   A stdClass object containing value and operator properties.
+   * {@inheritdoc}
    */
   public static function breakString($str, $force_int = FALSE) {
     $operator = NULL;
diff --git a/core/modules/views/src/Plugin/views/PluginBase.php b/core/modules/views/src/Plugin/views/PluginBase.php
index b66578b..a92027d 100644
--- a/core/modules/views/src/Plugin/views/PluginBase.php
+++ b/core/modules/views/src/Plugin/views/PluginBase.php
@@ -42,7 +42,7 @@
  *
  * @ingroup views_plugins
  */
-abstract class PluginBase extends ComponentPluginBase implements ContainerFactoryPluginInterface {
+abstract class PluginBase extends ComponentPluginBase implements ContainerFactoryPluginInterface, ViewsPluginInterface {
 
   /**
    * Include negotiated languages when listing languages.
@@ -108,14 +108,7 @@ public static function create(ContainerInterface $container, array $configuratio
   }
 
   /**
-   * Initialize the plugin.
-   *
-   * @param \Drupal\views\ViewExecutable $view
-   *   The view object.
-   * @param \Drupal\views\Plugin\views\display\DisplayPluginBase $display
-   *   The display handler.
-   * @param array $options
-   *   The options configured for this plugin.
+   * {@inheritdoc}
    */
   public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
     $this->view = $view;
@@ -171,8 +164,7 @@ protected function setOptionDefaults(array &$storage, array $options) {
   }
 
   /**
-   * Unpack options over our existing defaults, drilling down into arrays
-   * so that defaults don't get totally blown away.
+   * {@inheritdoc}
    */
   public function unpackOptions(&$storage, $options, $definition = NULL, $all = TRUE, $check = TRUE) {
     if ($check && !is_array($options)) {
@@ -211,19 +203,14 @@ public function unpackOptions(&$storage, $options, $definition = NULL, $all = TR
   }
 
   /**
-   * Clears a plugin.
+   * {@inheritdoc}
    */
   public function destroy() {
     unset($this->view, $this->display, $this->query);
   }
 
   /**
-   * Init will be called after construct, when the plugin is attached to a
-   * view and a display.
-   */
-
-  /**
-   * Provide a form to edit options for this plugin.
+   * {@inheritdoc}
    */
   public function buildOptionsForm(&$form, FormStateInterface $form_state) {
     // Some form elements belong in a fieldset for presentation, but can't
@@ -234,47 +221,41 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) {
   }
 
   /**
-   * Validate the options form.
+   * {@inheritdoc}
    */
   public function validateOptionsForm(&$form, FormStateInterface $form_state) { }
 
   /**
-   * Handle any special handling on the validate form.
+   * {@inheritdoc}
    */
   public function submitOptionsForm(&$form, FormStateInterface $form_state) { }
 
   /**
-   * Add anything to the query that we might need to.
+   * {@inheritdoc}
    */
   public function query() { }
 
   /**
-   * Provide a full list of possible theme templates used by this style.
+   * {@inheritdoc}
    */
   public function themeFunctions() {
     return $this->view->buildThemeFunctions($this->definition['theme']);
   }
 
   /**
-   * Validate that the plugin is correct and can be saved.
-   *
-   * @return
-   *   An array of error strings to tell the user what is wrong with this
-   *   plugin.
+   * {@inheritdoc}
    */
   public function validate() { return array(); }
 
   /**
-   * Returns the summary of the settings in the display.
+   * {@inheritdoc}
    */
   public function summaryTitle() {
     return t('Settings');
   }
 
   /**
-   * Return the human readable name of the display.
-   *
-   * This appears on the ui beside each plugin and beside the settings link.
+   * {@inheritdoc}
    */
   public function pluginTitle() {
     // Short_title is optional so its defaults to an empty string.
@@ -285,39 +266,21 @@ public function pluginTitle() {
   }
 
   /**
-   * Returns the usesOptions property.
+   * {@inheritdoc}
    */
   public function usesOptions() {
     return $this->usesOptions;
   }
 
   /**
-   * Returns a string with any core tokens replaced.
-   *
-   * @param string $string
-   *   The string to preform the token replacement on.
-   * @param array $options
-   *   An array of options, as passed to \Drupal\Core\Utility\Token::replace().
-   *
-   * @return string
-   *   The tokenized string.
+   * {@inheritdoc}
    */
   public function globalTokenReplace($string = '', array $options = array()) {
     return \Drupal::token()->replace($string, array('view' => $this->view), $options);
   }
 
   /**
-   * Returns an array of available token replacements.
-   *
-   * @param bool $prepared
-   *   Whether to return the raw token info for each token or an array of
-   *   prepared tokens for each type. E.g. "[view:name]".
-   * @param array $types
-   *   An array of additional token types to return, defaults to 'site' and
-   *   'view'.
-   *
-   * @return array
-   *   An array of available token replacement info or tokens, grouped by type.
+   * {@inheritdoc}
    */
   public function getAvailableGlobalTokens($prepared = FALSE, array $types = array()) {
     $info = \Drupal::token()->getInfo();
@@ -341,12 +304,7 @@ public function getAvailableGlobalTokens($prepared = FALSE, array $types = array
   }
 
   /**
-   * Adds elements for available core tokens to a form.
-   *
-   * @param array $form
-   *   The form array to alter, passed by reference.
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
-   *   The current state of the form.
+   * {@inheritdoc}
    */
   public function globalTokenForm(&$form, FormStateInterface $form_state) {
     $token_items = array();
@@ -377,19 +335,7 @@ public function globalTokenForm(&$form, FormStateInterface $form_state) {
   }
 
   /**
-   * Moves form elements into fieldsets for presentation purposes.
-   *
-   * Many views forms use #tree = TRUE to keep their values in a hierarchy for
-   * easier storage. Moving the form elements into fieldsets during form
-   * building would break up that hierarchy. Therefore, we wait until the
-   * pre_render stage, where any changes we make affect presentation only and
-   * aren't reflected in $form_state->getValues().
-   *
-   * @param array $form
-   *   The form build array to alter.
-   *
-   * @return array
-   *   The form build array.
+   * {@inheritdoc}
    */
   public static function preRenderAddFieldsetMarkup(array $form) {
     foreach (Element::children($form) as $key) {
@@ -408,17 +354,7 @@ public static function preRenderAddFieldsetMarkup(array $form) {
   }
 
   /**
-   * Flattens the structure of form elements.
-   *
-   * If a form element has #flatten = TRUE, then all of it's children get moved
-   * to the same level as the element itself. So $form['to_be_flattened'][$key]
-   * becomes $form[$key], and $form['to_be_flattened'] gets unset.
-   *
-   * @param array $form
-   *   The form build array to alter.
-   *
-   * @return array
-   *   The form build array.
+   * {@inheritdoc}
    */
   public static function preRenderFlattenData($form) {
     foreach (Element::children($form) as $key) {
@@ -436,12 +372,7 @@ public static function preRenderFlattenData($form) {
   }
 
   /**
-   * Returns an array of module dependencies for this plugin.
-   *
-   * Dependencies are a list of module names, which might depend on the
-   * configuration.
-   *
-   * @return array
+   * {@inheritdoc}
    */
   public function getDependencies() {
     return array();
diff --git a/core/modules/views/src/Plugin/views/ViewsHandlerInterface.php b/core/modules/views/src/Plugin/views/ViewsHandlerInterface.php
new file mode 100644
index 0000000..4a48372
--- /dev/null
+++ b/core/modules/views/src/Plugin/views/ViewsHandlerInterface.php
@@ -0,0 +1,144 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Plugin\views\ViewsHandlerInterface.
+ */
+
+namespace Drupal\views\Plugin\views;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Session\AccountInterface;
+
+/**
+ * Provides an interface for all views handlers.
+ */
+interface ViewsHandlerInterface extends ViewsPluginInterface {
+
+  /**
+   * Run before the view is built.
+   *
+   * This gives all the handlers some time to set up before any handler has
+   * been fully run.
+   */
+  public function preQuery();
+
+  /**
+   * Determines the entity type used by this handler.
+   *
+   * If this handler uses a relationship, the base class of the relationship is
+   * taken into account.
+   *
+   * @return string
+   *   The machine name of the entity type.
+   */
+  public function getEntityType();
+
+  /**
+   * Determines if the handler is considered 'broken', meaning it's a
+   * a placeholder used when a handler can't be found.
+   */
+  public function broken();
+
+  /**
+   * Ensure the main table for this handler is in the query. This is used
+   * a lot.
+   */
+  public function ensureMyTable();
+
+  /**
+   * Check whether current user has access to this handler.
+   *
+   * @param AccountInterface $account
+   *
+   * @return boolean
+   */
+  public function access(AccountInterface $account);
+
+  /**
+   * Get the join object that should be used for this handler.
+   *
+   * This method isn't used a great deal, but it's very handy for easily
+   * getting the join if it is necessary to make some changes to it, such
+   * as adding an 'extra'.
+   */
+  public function getJoin();
+
+  /**
+   * Sanitize the value for output.
+   *
+   * @param $value
+   *   The value being rendered.
+   * @param $type
+   *   The type of sanitization needed. If not provided, String::checkPlain() is used.
+   *
+   * @return string
+   *   Returns the safe value.
+   */
+  public function sanitizeValue($value, $type = NULL);
+
+  /**
+   * Fetches a handler to join one table to a primary table from the data cache.
+   *
+   * @param string $table
+   *   The table to join from.
+   * @param string $base_table
+   *   The table to join to.
+   *
+   * @return \Drupal\views\Plugin\views\join\JoinPluginBase
+   */
+  public static function getTableJoin($table, $base_table);
+
+  /**
+   * Shortcut to get a handler's raw field value.
+   *
+   * This should be overridden for handlers with formulae or other
+   * non-standard fields. Because this takes an argument, fields
+   * overriding this can just call return parent::getField($formula)
+   */
+  public function getField($field = NULL);
+
+  /**
+   * Run after the view is executed, before the result is cached.
+   *
+   * This gives all the handlers some time to modify values. This is primarily
+   * used so that handlers that pull up secondary data can put it in the
+   * $values so that the raw data can be utilized externally.
+   */
+  public function postExecute(&$values);
+
+  /**
+   * Shortcut to display the exposed options form.
+   */
+  public function showExposeForm(&$form, FormStateInterface $form_state);
+
+  /**
+   * Called just prior to query(), this lets a handler set up any relationship
+   * it needs.
+   */
+  public function setRelationship();
+
+  /**
+   * Return a string representing this handler's name in the UI.
+   */
+  public function adminLabel($short = FALSE);
+
+  /**
+   * Breaks x,y,z and x+y+z into an array.
+   *
+   * @param string $str
+   *   The string to split.
+   * @param bool $force_int
+   *   Enforce a numeric check.
+   *
+   * @return \stdClass
+   *   A stdClass object containing value and operator properties.
+   */
+  public static function breakString($str, $force_int = FALSE);
+
+  /**
+   * Provide text for the administrative summary.
+   */
+  public function adminSummary();
+
+}
diff --git a/core/modules/views/src/Plugin/views/ViewsPluginInterface.php b/core/modules/views/src/Plugin/views/ViewsPluginInterface.php
new file mode 100644
index 0000000..df5de7a
--- /dev/null
+++ b/core/modules/views/src/Plugin/views/ViewsPluginInterface.php
@@ -0,0 +1,181 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Plugin\views\ViewsPluginInterface.
+ */
+
+namespace Drupal\views\Plugin\views;
+
+use Drupal\Component\Plugin\DerivativeInspectionInterface;
+use Drupal\Component\Plugin\PluginInspectionInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\views\Plugin\views\display\DisplayPluginBase;
+use Drupal\views\ViewExecutable;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides an interface for all views plugins.
+ */
+interface ViewsPluginInterface extends PluginInspectionInterface, DerivativeInspectionInterface {
+
+  /**
+   * Returns an array of module dependencies for this plugin.
+   *
+   * Dependencies are a list of module names, which might depend on the
+   * configuration.
+   *
+   * @return array
+   */
+  public function getDependencies();
+
+  /**
+   * Return the human readable name of the display.
+   *
+   * This appears on the ui beside each plugin and beside the settings link.
+   */
+  public function pluginTitle();
+
+  /**
+   * Returns the usesOptions property.
+   */
+  public function usesOptions();
+
+  /**
+   * Validate the options form.
+   */
+  public function validateOptionsForm(&$form, FormStateInterface $form_state);
+
+  /**
+   * Returns the summary of the settings in the display.
+   */
+  public function summaryTitle();
+
+  /**
+   * Moves form elements into fieldsets for presentation purposes.
+   *
+   * Many views forms use #tree = TRUE to keep their values in a hierarchy for
+   * easier storage. Moving the form elements into fieldsets during form
+   * building would break up that hierarchy. Therefore, we wait until the
+   * pre_render stage, where any changes we make affect presentation only and
+   * aren't reflected in $form_state->getValues().
+   *
+   * @param array $form
+   *   The form build array to alter.
+   *
+   * @return array
+   *   The form build array.
+   */
+  public static function preRenderAddFieldsetMarkup(array $form);
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition);
+
+  /**
+   * Initialize the plugin.
+   *
+   * @param \Drupal\views\ViewExecutable $view
+   *   The view object.
+   * @param \Drupal\views\Plugin\views\display\DisplayPluginBase $display
+   *   The display handler.
+   * @param array $options
+   *   The options configured for this plugin.
+   */
+  public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL);
+
+  /**
+   * Handle any special handling on the validate form.
+   */
+  public function submitOptionsForm(&$form, FormStateInterface $form_state);
+
+  /**
+   * Adds elements for available core tokens to a form.
+   *
+   * @param array $form
+   *   The form array to alter, passed by reference.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   */
+  public function globalTokenForm(&$form, FormStateInterface $form_state);
+
+  /**
+   * Returns an array of available token replacements.
+   *
+   * @param bool $prepared
+   *   Whether to return the raw token info for each token or an array of
+   *   prepared tokens for each type. E.g. "[view:name]".
+   * @param array $types
+   *   An array of additional token types to return, defaults to 'site' and
+   *   'view'.
+   *
+   * @return array
+   *   An array of available token replacement info or tokens, grouped by type.
+   */
+  public function getAvailableGlobalTokens($prepared = FALSE, array $types = array());
+
+  /**
+   * Flattens the structure of form elements.
+   *
+   * If a form element has #flatten = TRUE, then all of it's children get moved
+   * to the same level as the element itself. So $form['to_be_flattened'][$key]
+   * becomes $form[$key], and $form['to_be_flattened'] gets unset.
+   *
+   * @param array $form
+   *   The form build array to alter.
+   *
+   * @return array
+   *   The form build array.
+   */
+  public static function preRenderFlattenData($form);
+
+  /**
+   * Returns a string with any core tokens replaced.
+   *
+   * @param string $string
+   *   The string to preform the token replacement on.
+   * @param array $options
+   *   An array of options, as passed to \Drupal\Core\Utility\Token::replace().
+   *
+   * @return string
+   *   The tokenized string.
+   */
+  public function globalTokenReplace($string = '', array $options = array());
+
+  /**
+   * Clears a plugin.
+   */
+  public function destroy();
+
+  /**
+   * Validate that the plugin is correct and can be saved.
+   *
+   * @return
+   *   An array of error strings to tell the user what is wrong with this
+   *   plugin.
+   */
+  public function validate();
+
+  /**
+   * Add anything to the query that we might need to.
+   */
+  public function query();
+
+  /**
+   * Unpack options over our existing defaults, drilling down into arrays
+   * so that defaults don't get totally blown away.
+   */
+  public function unpackOptions(&$storage, $options, $definition = NULL, $all = TRUE, $check = TRUE);
+
+  /**
+   * Provide a form to edit options for this plugin.
+   */
+  public function buildOptionsForm(&$form, FormStateInterface $form_state);
+
+  /**
+   * Provide a full list of possible theme templates used by this style.
+   */
+  public function themeFunctions();
+
+}
diff --git a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
index 7d3232f..d7d5ea4 100644
--- a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
+++ b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
@@ -837,7 +837,7 @@ public function usesFields() {
    * @param string $type
    *   The type of the plugin.
    *
-   * @return \Drupal\views\Plugin\views\PluginBase
+   * @return \Drupal\views\Plugin\views\ViewsPluginInterface
    */
   public function getPlugin($type) {
     // Look up the plugin name to use for this instance.
@@ -890,7 +890,7 @@ public function &getHandler($type, $id) {
   /**
    * Get a full array of handlers for $type. This caches them.
    *
-   * @return \Drupal\views\Plugin\views\HandlerBase[]
+   * @return \Drupal\views\Plugin\views\ViewsHandlerInterface[]
    */
   public function getHandlers($type) {
     if (!isset($this->handlers[$type])) {
diff --git a/core/modules/views/src/Plugin/views/join/JoinPluginBase.php b/core/modules/views/src/Plugin/views/join/JoinPluginBase.php
index cd1dda3..d282f39 100644
--- a/core/modules/views/src/Plugin/views/join/JoinPluginBase.php
+++ b/core/modules/views/src/Plugin/views/join/JoinPluginBase.php
@@ -58,7 +58,7 @@
  *
  * Extensions of this class can be used to create more interesting joins.
  */
-class JoinPluginBase extends PluginBase {
+class JoinPluginBase extends PluginBase implements JoinPluginInterface {
 
   /**
    * The table to join (right table).
@@ -181,16 +181,7 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition
   }
 
   /**
-   * Builds the SQL for the join this object represents.
-   *
-   * When possible, try to use table alias instead of table names.
-   *
-   * @param $select_query
-   *   An select query object.
-   * @param $table
-   *   The base table to join.
-   * @param \Drupal\views\Plugin\views\query\QueryPluginBase $view_query
-   *   The source views query.
+   * {@inheritdoc}
    */
   public function buildJoin($select_query, $table, $view_query) {
     if (empty($this->configuration['table formula'])) {
diff --git a/core/modules/views/src/Plugin/views/join/JoinPluginInterface.php b/core/modules/views/src/Plugin/views/join/JoinPluginInterface.php
new file mode 100644
index 0000000..03fefb2
--- /dev/null
+++ b/core/modules/views/src/Plugin/views/join/JoinPluginInterface.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Plugin\views\join\JoinPluginInterface.
+ */
+
+namespace Drupal\views\Plugin\views\join;
+
+/**
+ * Represents a join and creates the SQL necessary to implement the join.
+ *
+ * Extensions of this class can be used to create more interesting joins.
+ */
+interface JoinPluginInterface {
+
+  /**
+   * Builds the SQL for the join this object represents.
+   *
+   * When possible, try to use table alias instead of table names.
+   *
+   * @param $select_query
+   *   An select query object.
+   * @param $table
+   *   The base table to join.
+   * @param \Drupal\views\Plugin\views\query\QueryPluginBase $view_query
+   *   The source views query.
+   */
+  public function buildJoin($select_query, $table, $view_query);
+
+}
diff --git a/core/modules/views/src/Tests/Handler/HandlerTest.php b/core/modules/views/src/Tests/Handler/HandlerTest.php
index 9c9e038..b182dee 100644
--- a/core/modules/views/src/Tests/Handler/HandlerTest.php
+++ b/core/modules/views/src/Tests/Handler/HandlerTest.php
@@ -204,16 +204,14 @@ public function testHandlerWeights() {
       $loaded_order = array_keys($view->display_handler->getOption($type));
       $this->assertIdentical($original_order[$type], $loaded_order);
     }
-
   }
 
-
   /**
    * Check to see if a value is the same as the value on a certain handler.
    *
    * @param $expected
    *   The expected value to check.
-   * @param \Drupal\views\Plugin\views\HandlerBase $handler
+   * @param \Drupal\views\Plugin\views\ViewsHandlerInterface $handler
    *   The handler that has the $handler->value property to compare with first.
    * @param string $message
    *   The message to display along with the assertion.
diff --git a/core/tests/Drupal/Tests/Component/Plugin/DefaultFactoryTest.php b/core/tests/Drupal/Tests/Component/Plugin/DefaultFactoryTest.php
new file mode 100644
index 0000000..21dcf7c
--- /dev/null
+++ b/core/tests/Drupal/Tests/Component/Plugin/DefaultFactoryTest.php
@@ -0,0 +1,70 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Component\Plugin\DefaultFactoryTest.
+ */
+
+namespace Drupal\Tests\Component\Plugin;
+
+use Drupal\Component\Plugin\Factory\DefaultFactory;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @coversDefaultClass \Drupal\Component\Plugin\DefaultFactory
+ */
+class DefaultFactoryTest extends UnitTestCase {
+
+  /**
+   * Tests getPluginClass() with a valid plugin.
+   */
+  public function testGetPluginClassWithValidPlugin() {
+    $plugin_class = 'Drupal\plugin_test\Plugin\plugin_test\fruit\Cherry';
+    $class = DefaultFactory::getPluginClass('cherry', ['class' => $plugin_class]);
+
+    $this->assertEquals($plugin_class, $class);
+  }
+
+  /**
+   * Tests getPluginClass() with a missing class definition.
+   *
+   * @expectedException \Drupal\Component\Plugin\Exception\PluginException
+   * @expectedExceptionMessage The plugin (cherry) did not specify an instance class.
+   */
+  public function testGetPluginClassWithMissingClass() {
+    DefaultFactory::getPluginClass('cherry', []);
+  }
+
+  /**
+   * Tests getPluginClass() with a not existing class definition.
+   *
+   * @expectedException \Drupal\Component\Plugin\Exception\PluginException
+   * @expectedExceptionMessage Plugin (kiwifruit) instance class "\Drupal\plugin_test\Plugin\plugin_test\fruit\Kiwifruit" does not exist.
+   */
+  public function testGetPluginClassWithNotExistingClass() {
+    DefaultFactory::getPluginClass('kiwifruit', ['class' => '\Drupal\plugin_test\Plugin\plugin_test\fruit\Kiwifruit']);
+  }
+
+  /**
+   * Tests getPluginClass() with a required interface.
+   */
+  public function testGetPluginClassWithInterface() {
+    $plugin_class = 'Drupal\plugin_test\Plugin\plugin_test\fruit\Cherry';
+    $class = DefaultFactory::getPluginClass('cherry', ['class' => $plugin_class], '\Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface');
+
+    $this->assertEquals($plugin_class, $class);
+  }
+
+  /**
+   * Tests getPluginClass() with a required interface but no implementation.
+   *
+   * @expectedException \Drupal\Component\Plugin\Exception\PluginException
+   * @expectedExceptionMessage Plugin "cherry" (Drupal\plugin_test\Plugin\plugin_test\fruit\Kale) in core should implement interface \Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface.
+   */
+  public function testGetPluginClassWithInterfaceAndInvalidClass() {
+    $plugin_class = 'Drupal\plugin_test\Plugin\plugin_test\fruit\Kale';
+    DefaultFactory::getPluginClass('cherry', ['class' => $plugin_class, 'provider' => 'core'], '\Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface');
+  }
+
+}
+
diff --git a/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php b/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php
index c1dba2e..2d6390c 100644
--- a/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php
@@ -246,7 +246,7 @@ protected function setupLocalTaskManager() {
     $this->manager = $this
       ->getMockBuilder('Drupal\Core\Menu\LocalTaskManager')
       ->disableOriginalConstructor()
-      ->setMethods(NULL)
+      ->setMethods(array('enforcePluginInterface'))
       ->getMock();
 
     $property = new \ReflectionProperty('Drupal\Core\Menu\LocalTaskManager', 'controllerResolver');
diff --git a/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php b/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php
index 1eb0d26..274d46d 100644
--- a/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php
@@ -13,6 +13,8 @@
  * Tests the DefaultPluginManager.
  *
  * @group Plugin
+ *
+ * @coversDefaultClass \Drupal\Core\Plugin\DefaultPluginManager
  */
 class DefaultPluginManagerTest extends UnitTestCase {
 
@@ -76,7 +78,7 @@ public function testDefaultPluginManagerWithDisabledModule() {
       ->with('disabled_module')
       ->will($this->returnValue(FALSE));
 
-    $plugin_manager = new TestPluginManager($this->namespaces, $definitions, $module_handler, 'test_alter_hook');
+    $plugin_manager = new TestPluginManager($this->namespaces, $definitions, $module_handler, 'test_alter_hook', '\Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface');
 
     $this->assertEmpty($plugin_manager->getDefinition('cherry', FALSE), 'Plugin information of a disabled module is not available');
   }
@@ -101,7 +103,7 @@ public function testDefaultPluginManagerWithObjects() {
       ->with('disabled_module')
       ->will($this->returnValue(FALSE));
 
-    $plugin_manager = new TestPluginManager($this->namespaces, $definitions, $module_handler, 'test_alter_hook');
+    $plugin_manager = new TestPluginManager($this->namespaces, $definitions, $module_handler, 'test_alter_hook', '\Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface');
 
     $this->assertEmpty($plugin_manager->getDefinition('cherry', FALSE), 'Plugin information is available');
   }
@@ -110,7 +112,7 @@ public function testDefaultPluginManagerWithObjects() {
    * Tests the plugin manager with no cache and altering.
    */
   public function testDefaultPluginManager() {
-    $plugin_manager = new TestPluginManager($this->namespaces, $this->expectedDefinitions);
+    $plugin_manager = new TestPluginManager($this->namespaces, $this->expectedDefinitions, NULL, NULL, '\Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface');
     $this->assertEquals($this->expectedDefinitions, $plugin_manager->getDefinitions());
     $this->assertEquals($this->expectedDefinitions['banana'], $plugin_manager->getDefinition('banana'));
   }
@@ -129,7 +131,7 @@ public function testDefaultPluginManagerWithAlter() {
       ->method('alter')
       ->with($this->equalTo($alter_hook_name), $this->equalTo($this->expectedDefinitions));
 
-    $plugin_manager = new TestPluginManager($this->namespaces, $this->expectedDefinitions, $module_handler, $alter_hook_name);
+    $plugin_manager = new TestPluginManager($this->namespaces, $this->expectedDefinitions, $module_handler, $alter_hook_name, '\Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface');
 
     $this->assertEquals($this->expectedDefinitions, $plugin_manager->getDefinitions());
     $this->assertEquals($this->expectedDefinitions['banana'], $plugin_manager->getDefinition('banana'));
@@ -153,7 +155,7 @@ public function testDefaultPluginManagerWithEmptyCache() {
       ->method('set')
       ->with($cid, $this->expectedDefinitions);
 
-    $plugin_manager = new TestPluginManager($this->namespaces, $this->expectedDefinitions);
+    $plugin_manager = new TestPluginManager($this->namespaces, $this->expectedDefinitions, NULL, NULL, '\Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface');
     $plugin_manager->setCacheBackend($cache_backend, $cid);
 
     $this->assertEquals($this->expectedDefinitions, $plugin_manager->getDefinitions());
@@ -177,7 +179,7 @@ public function testDefaultPluginManagerWithFilledCache() {
       ->expects($this->never())
       ->method('set');
 
-    $plugin_manager = new TestPluginManager($this->namespaces, $this->expectedDefinitions);
+    $plugin_manager = new TestPluginManager($this->namespaces, $this->expectedDefinitions, NULL, NULL, '\Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface');
     $plugin_manager->setCacheBackend($cache_backend, $cid);
 
     $this->assertEquals($this->expectedDefinitions, $plugin_manager->getDefinitions());
@@ -201,10 +203,83 @@ public function testCacheClearWithTags() {
 
     $this->getContainerWithCacheBins($cache_backend);
 
-    $plugin_manager = new TestPluginManager($this->namespaces, $this->expectedDefinitions);
+    $plugin_manager = new TestPluginManager($this->namespaces, $this->expectedDefinitions, NULL, NULL, '\Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface');
     $plugin_manager->setCacheBackend($cache_backend, $cid, array('tag' => TRUE));
 
     $plugin_manager->clearCachedDefinitions();
   }
 
+  /**
+   * Tests plugins with the proper interface.
+   *
+   * @covers ::createInstance
+   * @covers ::enforcePluginInterface
+   */
+  public function testCreateInstanceWithJustValidInterfaces() {
+    $plugin_manager = new TestPluginManager($this->namespaces, $this->expectedDefinitions, NULL, NULL, '\Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface');
+
+    foreach ($this->expectedDefinitions as $plugin_id => $definition) {
+      $plugin_manager->createInstance($plugin_id);
+    }
+  }
+
+  /**
+   * Tests plugins without the proper interface.
+   *
+   * @covers ::createInstance
+   * @covers ::enforcePluginInterface
+   *
+   * @expectedException \Drupal\Component\Plugin\Exception\PluginException
+   * @expectedExceptionMessage Plugin "kale" (Drupal\plugin_test\Plugin\plugin_test\fruit\Kale) in plugin_test should implement interface \Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface
+   */
+  public function testCreateInstanceWithInvalidInterfaces() {
+    $module_handler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
+
+    $module_handler->expects($this->any())
+      ->method('moduleExists')
+      ->with('plugin_test')
+      ->willReturn(TRUE);
+
+    $this->expectedDefinitions['kale'] = array(
+      'id' => 'kale',
+      'label' => 'Kale',
+      'color' => 'green',
+      'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\Kale',
+      'provider' => 'plugin_test',
+    );
+    $this->expectedDefinitions['apple']['provider'] = 'plugin_test';
+    $this->expectedDefinitions['banana']['provider'] = 'plugin_test';
+
+    $plugin_manager = new TestPluginManager($this->namespaces, $this->expectedDefinitions, $module_handler, NULL, '\Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface');
+    $plugin_manager->createInstance('kale');
+  }
+
+  /**
+   * Tests plugins without a required interface.
+   *
+   * @covers ::getDefinitions
+   * @covers ::enforcePluginInterface
+   */
+  public function testGetDefinitionsWithoutRequiredInterface() {
+    $module_handler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
+
+    $module_handler->expects($this->any())
+      ->method('moduleExists')
+      ->with('plugin_test')
+      ->willReturn(FALSE);
+
+    $this->expectedDefinitions['kale'] = array(
+      'id' => 'kale',
+      'label' => 'Kale',
+      'color' => 'green',
+      'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\Kale',
+      'provider' => 'plugin_test',
+    );
+    $this->expectedDefinitions['apple']['provider'] = 'plugin_test';
+    $this->expectedDefinitions['banana']['provider'] = 'plugin_test';
+
+    $plugin_manager = new TestPluginManager($this->namespaces, $this->expectedDefinitions, $module_handler, NULL);
+    $plugin_manager->getDefinitions();
+  }
+
 }
diff --git a/core/tests/Drupal/Tests/Core/Plugin/TestPluginManager.php b/core/tests/Drupal/Tests/Core/Plugin/TestPluginManager.php
index a7ea79d..c1c2d7f 100644
--- a/core/tests/Drupal/Tests/Core/Plugin/TestPluginManager.php
+++ b/core/tests/Drupal/Tests/Core/Plugin/TestPluginManager.php
@@ -8,6 +8,7 @@
 namespace Drupal\Tests\Core\Plugin;
 
 use Drupal\Component\Plugin\Discovery\StaticDiscovery;
+use Drupal\Component\Plugin\Factory\DefaultFactory;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Plugin\DefaultPluginManager;
 
@@ -28,13 +29,16 @@ class TestPluginManager extends DefaultPluginManager {
    *   (optional) The module handler to invoke the alter hook with.
    * @param string $alter_hook
    *   (optional) Name of the alter hook.
+   * @param string $interface
+   *   (optional) The interface required for the plugins.
    */
-  public function __construct(\Traversable $namespaces, array $definitions, ModuleHandlerInterface $module_handler = NULL, $alter_hook = NULL) {
+  public function __construct(\Traversable $namespaces, array $definitions, ModuleHandlerInterface $module_handler = NULL, $alter_hook = NULL, $interface = NULL) {
     // Create the object that can be used to return definitions for all the
     // plugins available for this type. Most real plugin managers use a richer
     // discovery implementation, but StaticDiscovery lets us add some simple
     // mock plugins for unit testing.
     $this->discovery = new StaticDiscovery();
+    $this->factory = new DefaultFactory($this->discovery, $interface);
 
     // Add the static definitions.
     foreach ($definitions as $key => $definition) {
