diff --git a/core/modules/link/src/Plugin/Validation/Constraint/LinkAccessConstraintValidator.php b/core/modules/link/src/Plugin/Validation/Constraint/LinkAccessConstraintValidator.php
index f2a5f839d2b243cfd2cc12f88b7cfc94f22148f5..7d0e4fbee70b63911cf2c2f306e1838214cb8906 100644
--- a/core/modules/link/src/Plugin/Validation/Constraint/LinkAccessConstraintValidator.php
+++ b/core/modules/link/src/Plugin/Validation/Constraint/LinkAccessConstraintValidator.php
@@ -6,20 +6,12 @@
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\Validator\Constraint;
-use Symfony\Component\Validator\ConstraintValidatorInterface;
-use Symfony\Component\Validator\ExecutionContextInterface;
+use Symfony\Component\Validator\ConstraintValidator;
 
 /**
  * Validates the LinkAccess constraint.
  */
-class LinkAccessConstraintValidator implements ConstraintValidatorInterface, ContainerInjectionInterface {
-
-  /**
-   * Stores the validator's state during validation.
-   *
-   * @var \Symfony\Component\Validator\ExecutionContextInterface
-   */
-  protected $context;
+class LinkAccessConstraintValidator extends ConstraintValidator implements ContainerInjectionInterface {
 
   /**
    * Proxy for the current user account.
@@ -47,13 +39,6 @@ public static function create(ContainerInterface $container) {
     );
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function initialize(ExecutionContextInterface $context) {
-    $this->context = $context;
-  }
-
   /**
    * {@inheritdoc}
    */
diff --git a/core/modules/link/src/Plugin/Validation/Constraint/LinkExternalProtocolsConstraintValidator.php b/core/modules/link/src/Plugin/Validation/Constraint/LinkExternalProtocolsConstraintValidator.php
index e85de58dbc9b88411e18c06f7b0837d80c57f199..0bc178a7e08ee3e205af0197f23fb740a3ba75c4 100644
--- a/core/modules/link/src/Plugin/Validation/Constraint/LinkExternalProtocolsConstraintValidator.php
+++ b/core/modules/link/src/Plugin/Validation/Constraint/LinkExternalProtocolsConstraintValidator.php
@@ -4,27 +4,12 @@
 
 use Drupal\Component\Utility\UrlHelper;
 use Symfony\Component\Validator\Constraint;
-use Symfony\Component\Validator\ConstraintValidatorInterface;
-use Symfony\Component\Validator\ExecutionContextInterface;
+use Symfony\Component\Validator\ConstraintValidator;
 
 /**
  * Validates the LinkExternalProtocols constraint.
  */
-class LinkExternalProtocolsConstraintValidator implements ConstraintValidatorInterface {
-
-  /**
-   * Stores the validator's state during validation.
-   *
-   * @var \Symfony\Component\Validator\ExecutionContextInterface
-   */
-  protected $context;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function initialize(ExecutionContextInterface $context) {
-    $this->context = $context;
-  }
+class LinkExternalProtocolsConstraintValidator extends ConstraintValidator {
 
   /**
    * {@inheritdoc}
diff --git a/core/modules/link/src/Plugin/Validation/Constraint/LinkNotExistingInternalConstraintValidator.php b/core/modules/link/src/Plugin/Validation/Constraint/LinkNotExistingInternalConstraintValidator.php
index 86c38666e068ad016be9cbc8fbcd7c94a6d71070..46218133d5a4e90caded26ea66dd9088d4075193 100644
--- a/core/modules/link/src/Plugin/Validation/Constraint/LinkNotExistingInternalConstraintValidator.php
+++ b/core/modules/link/src/Plugin/Validation/Constraint/LinkNotExistingInternalConstraintValidator.php
@@ -6,27 +6,12 @@
 use Symfony\Component\Routing\Exception\MissingMandatoryParametersException;
 use Symfony\Component\Routing\Exception\RouteNotFoundException;
 use Symfony\Component\Validator\Constraint;
-use Symfony\Component\Validator\ConstraintValidatorInterface;
-use Symfony\Component\Validator\ExecutionContextInterface;
+use Symfony\Component\Validator\ConstraintValidator;
 
 /**
  * Validates the LinkNotExistingInternal constraint.
  */
-class LinkNotExistingInternalConstraintValidator implements ConstraintValidatorInterface {
-
-  /**
-   * Stores the validator's state during validation.
-   *
-   * @var \Symfony\Component\Validator\ExecutionContextInterface
-   */
-  protected $context;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function initialize(ExecutionContextInterface $context) {
-    $this->context = $context;
-  }
+class LinkNotExistingInternalConstraintValidator extends ConstraintValidator {
 
   /**
    * {@inheritdoc}
diff --git a/core/modules/link/src/Plugin/Validation/Constraint/LinkTypeConstraint.php b/core/modules/link/src/Plugin/Validation/Constraint/LinkTypeConstraint.php
index 4f706f6a0f31e1d28e9cefbad2475b63ac04f456..52171d68de902fbc2800bc575a783816dbbb3afd 100644
--- a/core/modules/link/src/Plugin/Validation/Constraint/LinkTypeConstraint.php
+++ b/core/modules/link/src/Plugin/Validation/Constraint/LinkTypeConstraint.php
@@ -2,10 +2,7 @@
 
 namespace Drupal\link\Plugin\Validation\Constraint;
 
-use Drupal\link\LinkItemInterface;
 use Symfony\Component\Validator\Constraint;
-use Symfony\Component\Validator\ConstraintValidatorInterface;
-use Symfony\Component\Validator\ExecutionContextInterface;
 
 /**
  * Validation constraint for links receiving data allowed by its settings.
@@ -15,64 +12,8 @@
  *   label = @Translation("Link data valid for link type.", context = "Validation"),
  * )
  */
-class LinkTypeConstraint extends Constraint implements ConstraintValidatorInterface {
+class LinkTypeConstraint extends Constraint {
 
   public $message = "The path '@uri' is invalid.";
 
-  /**
-   * @var \Symfony\Component\Validator\ExecutionContextInterface
-   */
-  protected $context;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function initialize(ExecutionContextInterface $context) {
-    $this->context = $context;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function validatedBy() {
-    return get_class($this);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function validate($value, Constraint $constraint) {
-    if (isset($value)) {
-      $uri_is_valid = TRUE;
-
-      /** @var $link_item \Drupal\link\LinkItemInterface */
-      $link_item = $value;
-      $link_type = $link_item->getFieldDefinition()->getSetting('link_type');
-
-      // Try to resolve the given URI to a URL. It may fail if it's schemeless.
-      try {
-        $url = $link_item->getUrl();
-      }
-      catch (\InvalidArgumentException $e) {
-        $uri_is_valid = FALSE;
-      }
-
-      // If the link field doesn't support both internal and external links,
-      // check whether the URL (a resolved URI) is in fact violating either
-      // restriction.
-      if ($uri_is_valid && $link_type !== LinkItemInterface::LINK_GENERIC) {
-        if (!($link_type & LinkItemInterface::LINK_EXTERNAL) && $url->isExternal()) {
-          $uri_is_valid = FALSE;
-        }
-        if (!($link_type & LinkItemInterface::LINK_INTERNAL) && !$url->isExternal()) {
-          $uri_is_valid = FALSE;
-        }
-      }
-
-      if (!$uri_is_valid) {
-        $this->context->addViolation($this->message, array('@uri' => $link_item->uri));
-      }
-    }
-  }
-
 }
diff --git a/core/modules/link/src/Plugin/Validation/Constraint/LinkTypeConstraintValidator.php b/core/modules/link/src/Plugin/Validation/Constraint/LinkTypeConstraintValidator.php
new file mode 100644
index 0000000000000000000000000000000000000000..a67eb4dfc0bc242a66c99b85bd5df58e7024b60a
--- /dev/null
+++ b/core/modules/link/src/Plugin/Validation/Constraint/LinkTypeConstraintValidator.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace Drupal\link\Plugin\Validation\Constraint;
+
+use Drupal\link\LinkItemInterface;
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\ConstraintValidator;
+
+/**
+ * Constraint validator for links receiving data allowed by its settings.
+ */
+class LinkTypeConstraintValidator extends ConstraintValidator {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validate($value, Constraint $constraint) {
+    if (isset($value)) {
+      $uri_is_valid = TRUE;
+
+      /** @var $link_item \Drupal\link\LinkItemInterface */
+      $link_item = $value;
+      $link_type = $link_item->getFieldDefinition()->getSetting('link_type');
+
+      // Try to resolve the given URI to a URL. It may fail if it's schemeless.
+      try {
+        $url = $link_item->getUrl();
+      }
+      catch (\InvalidArgumentException $e) {
+        $uri_is_valid = FALSE;
+      }
+
+      // If the link field doesn't support both internal and external links,
+      // check whether the URL (a resolved URI) is in fact violating either
+      // restriction.
+      if ($uri_is_valid && $link_type !== LinkItemInterface::LINK_GENERIC) {
+        if (!($link_type & LinkItemInterface::LINK_EXTERNAL) && $url->isExternal()) {
+          $uri_is_valid = FALSE;
+        }
+        if (!($link_type & LinkItemInterface::LINK_INTERNAL) && !$url->isExternal()) {
+          $uri_is_valid = FALSE;
+        }
+      }
+
+      if (!$uri_is_valid) {
+        $this->context->addViolation($constraint->message, array('@uri' => $link_item->uri));
+      }
+    }
+  }
+
+}
diff --git a/core/modules/link/tests/src/Unit/Plugin/Validation/Constraint/LinkAccessConstraintValidatorTest.php b/core/modules/link/tests/src/Unit/Plugin/Validation/Constraint/LinkAccessConstraintValidatorTest.php
index 9e4623eed80df6a030c269c72004029baf2a78b4..c2290bc4d17196310053c86054316e2a2abe32bd 100644
--- a/core/modules/link/tests/src/Unit/Plugin/Validation/Constraint/LinkAccessConstraintValidatorTest.php
+++ b/core/modules/link/tests/src/Unit/Plugin/Validation/Constraint/LinkAccessConstraintValidatorTest.php
@@ -5,6 +5,7 @@
 use Drupal\link\Plugin\Validation\Constraint\LinkAccessConstraint;
 use Drupal\link\Plugin\Validation\Constraint\LinkAccessConstraintValidator;
 use Drupal\Tests\UnitTestCase;
+use Symfony\Component\Validator\Context\ExecutionContextInterface;
 
 /**
  * Tests the LinkAccessConstraintValidator validator.
@@ -29,7 +30,7 @@ class LinkAccessConstraintValidatorTest extends UnitTestCase {
    * @dataProvider providerValidate
    */
   public function testValidate($value, $user, $valid) {
-    $context = $this->getMock('Symfony\Component\Validator\ExecutionContextInterface');
+    $context = $this->getMock(ExecutionContextInterface::class);
 
     if ($valid) {
       $context->expects($this->never())
diff --git a/core/modules/link/tests/src/Unit/Plugin/Validation/Constraint/LinkExternalProtocolsConstraintValidatorTest.php b/core/modules/link/tests/src/Unit/Plugin/Validation/Constraint/LinkExternalProtocolsConstraintValidatorTest.php
index 771b307f62f456a031ea37b99112a504df4fd580..0de416f01ce342b848dcf2a2a47001a59cf64a15 100644
--- a/core/modules/link/tests/src/Unit/Plugin/Validation/Constraint/LinkExternalProtocolsConstraintValidatorTest.php
+++ b/core/modules/link/tests/src/Unit/Plugin/Validation/Constraint/LinkExternalProtocolsConstraintValidatorTest.php
@@ -7,6 +7,7 @@
 use Drupal\link\Plugin\Validation\Constraint\LinkExternalProtocolsConstraint;
 use Drupal\link\Plugin\Validation\Constraint\LinkExternalProtocolsConstraintValidator;
 use Drupal\Tests\UnitTestCase;
+use Symfony\Component\Validator\Context\ExecutionContextInterface;
 
 /**
  * @coversDefaultClass \Drupal\link\Plugin\Validation\Constraint\LinkExternalProtocolsConstraintValidator
@@ -19,7 +20,7 @@ class LinkExternalProtocolsConstraintValidatorTest extends UnitTestCase {
    * @dataProvider providerValidate
    */
   public function testValidate($value, $valid) {
-    $context = $this->getMock('Symfony\Component\Validator\ExecutionContextInterface');
+    $context = $this->getMock(ExecutionContextInterface::class);
 
     if ($valid) {
       $context->expects($this->never())
@@ -77,7 +78,7 @@ public function testValidateWithMalformedUri() {
       ->method('getUrl')
       ->willThrowException(new \InvalidArgumentException());
 
-    $context = $this->getMock('Symfony\Component\Validator\ExecutionContextInterface');
+    $context = $this->getMock(ExecutionContextInterface::class);
     $context->expects($this->never())
       ->method('addViolation');
 
@@ -97,7 +98,7 @@ public function testValidateIgnoresInternalUrls() {
       ->method('getUrl')
       ->willReturn(Url::fromRoute('example.test'));
 
-    $context = $this->getMock('Symfony\Component\Validator\ExecutionContextInterface');
+    $context = $this->getMock(ExecutionContextInterface::class);
     $context->expects($this->never())
       ->method('addViolation');
 
diff --git a/core/modules/link/tests/src/Unit/Plugin/Validation/Constraint/LinkNotExistingInternalConstraintValidatorTest.php b/core/modules/link/tests/src/Unit/Plugin/Validation/Constraint/LinkNotExistingInternalConstraintValidatorTest.php
index 69776fd5af309ee28a1b3e078901ab583d0b03b7..41ecd4a87d80091fca4d185c3de7ae8a4dd727bf 100644
--- a/core/modules/link/tests/src/Unit/Plugin/Validation/Constraint/LinkNotExistingInternalConstraintValidatorTest.php
+++ b/core/modules/link/tests/src/Unit/Plugin/Validation/Constraint/LinkNotExistingInternalConstraintValidatorTest.php
@@ -7,6 +7,7 @@
 use Drupal\link\Plugin\Validation\Constraint\LinkNotExistingInternalConstraintValidator;
 use Drupal\Tests\UnitTestCase;
 use Symfony\Component\Routing\Exception\RouteNotFoundException;
+use Symfony\Component\Validator\Context\ExecutionContextInterface;
 
 /**
  * @coversDefaultClass \Drupal\link\Plugin\Validation\Constraint\LinkNotExistingInternalConstraintValidator
@@ -19,7 +20,7 @@ class LinkNotExistingInternalConstraintValidatorTest extends UnitTestCase {
    * @dataProvider providerValidate
    */
   public function testValidate($value, $valid) {
-    $context = $this->getMock('Symfony\Component\Validator\ExecutionContextInterface');
+    $context = $this->getMock(ExecutionContextInterface::class);
 
     if ($valid) {
       $context->expects($this->never())
@@ -94,7 +95,7 @@ public function testValidateWithMalformedUri() {
       ->method('getUrl')
       ->willThrowException(new \InvalidArgumentException());
 
-    $context = $this->getMock('Symfony\Component\Validator\ExecutionContextInterface');
+    $context = $this->getMock(ExecutionContextInterface::class);
     $context->expects($this->never())
       ->method('addViolation');
 
diff --git a/core/modules/user/src/Plugin/Validation/Constraint/UserMailRequired.php b/core/modules/user/src/Plugin/Validation/Constraint/UserMailRequired.php
index 5905e1212b9f743af60589f5a03c6cb9954ee7df..b500a8d973b78a5395529aef36f237af79eaf8d3 100644
--- a/core/modules/user/src/Plugin/Validation/Constraint/UserMailRequired.php
+++ b/core/modules/user/src/Plugin/Validation/Constraint/UserMailRequired.php
@@ -3,8 +3,6 @@
 namespace Drupal\user\Plugin\Validation\Constraint;
 
 use Symfony\Component\Validator\Constraint;
-use Symfony\Component\Validator\ConstraintValidatorInterface;
-use Symfony\Component\Validator\ExecutionContextInterface;
 
 /**
  * Checks if the user's email address is provided if required.
@@ -18,7 +16,7 @@
  *   label = @Translation("User email required", context = "Validation")
  * )
  */
-class UserMailRequired extends Constraint implements ConstraintValidatorInterface {
+class UserMailRequired extends Constraint {
 
   /**
    * Violation message. Use the same message as FormValidator.
@@ -30,45 +28,4 @@ class UserMailRequired extends Constraint implements ConstraintValidatorInterfac
    */
   public $message = '@name field is required.';
 
-  /**
-   * @var \Symfony\Component\Validator\ExecutionContextInterface
-   */
-  protected $context;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function initialize(ExecutionContextInterface $context) {
-    $this->context = $context;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function validatedBy() {
-    return get_class($this);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function validate($items, Constraint $constraint) {
-    /** @var \Drupal\Core\Field\FieldItemListInterface $items */
-    /** @var \Drupal\user\UserInterface $account */
-    $account = $items->getEntity();
-    $existing_value = NULL;
-    if ($account->id()) {
-      $account_unchanged = \Drupal::entityManager()
-        ->getStorage('user')
-        ->loadUnchanged($account->id());
-      $existing_value = $account_unchanged->getEmail();
-    }
-
-    $required = !(!$existing_value && \Drupal::currentUser()->hasPermission('administer users'));
-
-    if ($required && (!isset($items) || $items->isEmpty())) {
-      $this->context->addViolation($this->message, ['@name' => $account->getFieldDefinition('mail')->getLabel()]);
-    }
-  }
-
 }
diff --git a/core/modules/user/src/Plugin/Validation/Constraint/UserMailRequiredValidator.php b/core/modules/user/src/Plugin/Validation/Constraint/UserMailRequiredValidator.php
new file mode 100644
index 0000000000000000000000000000000000000000..7e20b7a8c84b94a6a99375f669ce2e30b673181f
--- /dev/null
+++ b/core/modules/user/src/Plugin/Validation/Constraint/UserMailRequiredValidator.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Drupal\user\Plugin\Validation\Constraint;
+
+use Symfony\Component\Validator\ConstraintValidator;
+use Symfony\Component\Validator\Constraint;
+
+/**
+ * Checks if the user's email address is provided if required.
+ *
+ * The user mail field is NOT required if account originally had no mail set
+ * and the user performing the edit has 'administer users' permission.
+ * This allows users without email address to be edited and deleted.
+ */
+class UserMailRequiredValidator extends ConstraintValidator {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validate($items, Constraint $constraint) {
+    /** @var \Drupal\Core\Field\FieldItemListInterface $items */
+    /** @var \Drupal\user\UserInterface $account */
+    $account = $items->getEntity();
+    $existing_value = NULL;
+    if ($account->id()) {
+      $account_unchanged = \Drupal::entityManager()
+        ->getStorage('user')
+        ->loadUnchanged($account->id());
+      $existing_value = $account_unchanged->getEmail();
+    }
+
+    $required = !(!$existing_value && \Drupal::currentUser()->hasPermission('administer users'));
+
+    if ($required && (!isset($items) || $items->isEmpty())) {
+      $this->context->addViolation($constraint->message, ['@name' => $account->getFieldDefinition('mail')->getLabel()]);
+    }
+  }
+
+}
diff --git a/core/modules/user/tests/src/Unit/Plugin/Validation/Constraint/ProtectedUserFieldConstraintValidatorTest.php b/core/modules/user/tests/src/Unit/Plugin/Validation/Constraint/ProtectedUserFieldConstraintValidatorTest.php
index d29c8c53ecc816580e73df251e09d28fa85d5717..69edf3b4d6ae098d939bec05de97924686c6eb0e 100644
--- a/core/modules/user/tests/src/Unit/Plugin/Validation/Constraint/ProtectedUserFieldConstraintValidatorTest.php
+++ b/core/modules/user/tests/src/Unit/Plugin/Validation/Constraint/ProtectedUserFieldConstraintValidatorTest.php
@@ -5,6 +5,7 @@
 use Drupal\Tests\UnitTestCase;
 use Drupal\user\Plugin\Validation\Constraint\ProtectedUserFieldConstraint;
 use Drupal\user\Plugin\Validation\Constraint\ProtectedUserFieldConstraintValidator;
+use Symfony\Component\Validator\Context\ExecutionContextInterface;
 
 /**
  * @coversDefaultClass \Drupal\user\Plugin\Validation\Constraint\ProtectedUserFieldConstraintValidator
@@ -47,7 +48,7 @@ public function testValidate($items, $expected_violation, $name = FALSE) {
 
     // If a violation is expected, then the context's addViolation method will
     // be called, otherwise it should not be called.
-    $context = $this->getMock('Symfony\Component\Validator\ExecutionContextInterface');
+    $context = $this->getMock(ExecutionContextInterface::class);
 
     if ($expected_violation) {
       $context->expects($this->once())
diff --git a/core/tests/Drupal/Tests/Core/Validation/Plugin/Validation/Constraint/PrimitiveTypeConstraintValidatorTest.php b/core/tests/Drupal/Tests/Core/Validation/Plugin/Validation/Constraint/PrimitiveTypeConstraintValidatorTest.php
index 12f36eac2936a315ecfee3f8f7c89e5296781ba0..6647e5fa72de3d766df99af980b1291a878e448d 100644
--- a/core/tests/Drupal/Tests/Core/Validation/Plugin/Validation/Constraint/PrimitiveTypeConstraintValidatorTest.php
+++ b/core/tests/Drupal/Tests/Core/Validation/Plugin/Validation/Constraint/PrimitiveTypeConstraintValidatorTest.php
@@ -13,6 +13,7 @@
 use Drupal\Core\Validation\Plugin\Validation\Constraint\PrimitiveTypeConstraintValidator;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Tests\UnitTestCase;
+use Symfony\Component\Validator\Context\ExecutionContextInterface;
 
 /**
  * @coversDefaultClass \Drupal\Core\Validation\Plugin\Validation\Constraint\PrimitiveTypeConstraintValidator
@@ -26,7 +27,7 @@ class PrimitiveTypeConstraintValidatorTest extends UnitTestCase {
    * @dataProvider provideTestValidate
    */
   public function testValidate(PrimitiveInterface $typed_data, $value, $valid) {
-    $context = $this->getMock('\Symfony\Component\Validator\Context\ExecutionContextInterface');
+    $context = $this->getMock(ExecutionContextInterface::class);
     $context->expects($this->any())
       ->method('getObject')
       ->willReturn($typed_data);