diff --git a/core/includes/form.inc b/core/includes/form.inc
index 95470f9..44f0ad6 100644
--- a/core/includes/form.inc
+++ b/core/includes/form.inc
@@ -6,6 +6,7 @@
  */
 
 use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Form\FormInterface;
 use Drupal\Core\Database\Database;
 use Drupal\Core\Template\Attribute;
 use Drupal\Core\Utility\Color;
@@ -106,8 +107,9 @@
  * is not needed (i.e., when initially rendering the form) and is often
  * used as a menu callback.
  *
- * @param $form_id
- *   The unique string identifying the desired form. If a function with that
+ * @param \Drupal\Core\Form\FormInterface|string $form_arg
+ *   A form object to use to build the form, or the unique string identifying
+ *   the desired form. If $form_arg is a string and a function with that
  *   name exists, it is called to build the form array. Modules that need to
  *   generate the same form (or very similar forms) using different $form_ids
  *   can implement hook_forms(), which maps different $form_id values to the
@@ -126,36 +128,23 @@
  *
  * @see drupal_build_form()
  */
-function drupal_get_form($form_id) {
+function drupal_get_form($form_arg) {
   $form_state = array();
 
   $args = func_get_args();
-  // Remove $form_id from the arguments.
+  // Remove $form_arg from the arguments.
   array_shift($args);
   $form_state['build_info']['args'] = $args;
 
-  return drupal_build_form($form_id, $form_state);
-}
+  // Determine the form ID.
+  if (is_object($form_arg) && $form_arg instanceof FormInterface) {
+    $form_state['build_info']['callback_object'] = $form_arg;
+    $form_id = $form_arg->getFormID();
+  }
+  else {
+    $form_id = $form_arg;
+  }
 
-/**
- * Returns a renderable form array using a specific callback.
- *
- * When using drupal_get_form(), the $form_id or information from hook_forms()
- * is used to determine the callback used to build the form. If the callback
- * needs to be explicitly specified, or if it is a method, use this function.
- *
- * @param string $form_id
- *   The unique string identifying the desired form.
- * @param callable $callback
- *   A callback to be used for building the form.
- * @param array $args
- *   (optional) An array of arguments to pass to the form callback. Defaults to
- *   an empty array.
- */
-function drupal_get_callback_form($form_id, $callback, array $args = array()) {
-  $form_state = array();
-  $form_state['build_info']['args'] = $args;
-  $form_state['build_info']['callback'] = $callback;
   return drupal_build_form($form_id, $form_state);
 }
 
@@ -773,7 +762,13 @@ function drupal_retrieve_form($form_id, &$form_state) {
 
   // If an explicit form builder callback is defined we just use it, otherwise
   // we look for a function named after the $form_id.
-  $callback = !empty($form_state['build_info']['callback']) ? $form_state['build_info']['callback'] : $form_id;
+  $callback = $form_id;
+  if (!empty($form_state['build_info']['callback'])) {
+    $callback = $form_state['build_info']['callback'];
+  }
+  elseif (!empty($form_state['build_info']['callback_object'])) {
+    $callback = array($form_state['build_info']['callback_object'], 'build');
+  }
 
   // We first check to see if there is a valid form builder callback defined.
   // If there is, we simply pass the arguments on to it to get the form.
@@ -1081,8 +1076,11 @@ function drupal_prepare_form($form_id, &$form, &$form_state) {
   if (!isset($form['#validate'])) {
     // Ensure that modules can rely on #validate being set.
     $form['#validate'] = array();
+    if (isset($form_state['build_info']['callback_object'])) {
+      $form['#validate'][] = array($form_state['build_info']['callback_object'], 'validate');
+    }
     // Check for a handler specific to $form_id.
-    if (function_exists($form_id . '_validate')) {
+    elseif (function_exists($form_id . '_validate')) {
       $form['#validate'][] = $form_id . '_validate';
     }
     // Otherwise check whether this is a shared form and whether there is a
@@ -1095,8 +1093,11 @@ function drupal_prepare_form($form_id, &$form, &$form_state) {
   if (!isset($form['#submit'])) {
     // Ensure that modules can rely on #submit being set.
     $form['#submit'] = array();
+    if (isset($form_state['build_info']['callback_object'])) {
+      $form['#submit'][] = array($form_state['build_info']['callback_object'], 'submit');
+    }
     // Check for a handler specific to $form_id.
-    if (function_exists($form_id . '_submit')) {
+    elseif (function_exists($form_id . '_submit')) {
       $form['#submit'][] = $form_id . '_submit';
     }
     // Otherwise check whether this is a shared form and whether there is a
diff --git a/core/lib/Drupal/Core/Form/FormInterface.php b/core/lib/Drupal/Core/Form/FormInterface.php
new file mode 100644
index 0000000..2d8864e
--- /dev/null
+++ b/core/lib/Drupal/Core/Form/FormInterface.php
@@ -0,0 +1,56 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Form\FormInterface.
+ */
+
+namespace Drupal\Core\Form;
+
+/**
+ * Provides an interface for a Form.
+ */
+interface FormInterface {
+
+  /**
+   * Returns a unique string identifying the form.
+   *
+   * @return string
+   *   The unique string identifying the form.
+   */
+  public function getFormID();
+
+  /**
+   * Form constructor.
+   *
+   * @param array $form
+   *   An associative array containing the structure of the form.
+   * @param array $form_state
+   *   An associative array containing the current state of the form.
+   *
+   * @return array
+   *   The form structure.
+   */
+  public function build(array $form, array &$form_state);
+
+  /**
+   * Form validation handler.
+   *
+   * @param array $form
+   *   An associative array containing the structure of the form.
+   * @param array $form_state
+   *   An associative array containing the current state of the form.
+   */
+  public function validate(array &$form, array &$form_state);
+
+  /**
+   * Form submission handler.
+   *
+   * @param array $form
+   *   An associative array containing the structure of the form.
+   * @param array $form_state
+   *   An associative array containing the current state of the form.
+   */
+  public function submit(array &$form, array &$form_state);
+
+}
diff --git a/core/modules/block/lib/Drupal/block/BlockListController.php b/core/modules/block/lib/Drupal/block/BlockListController.php
index fc82771..bde1ce7 100644
--- a/core/modules/block/lib/Drupal/block/BlockListController.php
+++ b/core/modules/block/lib/Drupal/block/BlockListController.php
@@ -9,11 +9,12 @@
 
 use Drupal\Core\Config\Entity\ConfigEntityListController;
 use Drupal\block\Plugin\Core\Entity\Block;
+use Drupal\Core\Form\FormInterface;
 
 /**
  * Defines the block list controller.
  */
-class BlockListController extends ConfigEntityListController {
+class BlockListController extends ConfigEntityListController implements FormInterface {
 
   /**
    * The regions containing the blocks.
@@ -55,7 +56,7 @@ public function render($theme = NULL) {
     // If no theme was specified, use the current theme.
     $this->theme = $theme ?: $GLOBALS['theme_key'];
 
-    return drupal_get_callback_form('block_admin_display_form', array($this, 'form'));
+    return drupal_get_form($this);
   }
 
   /**
@@ -92,9 +93,18 @@ protected function sort(Block $a, Block $b) {
   }
 
   /**
+   * Implements \Drupal\Core\Form\FormInterface::getFormID().
+   */
+  public function getFormID() {
+    return 'block_admin_display_form';
+  }
+
+  /**
+   * Implements \Drupal\Core\Form\FormInterface::build().
+   *
    * Form constructor for the main block administration form.
    */
-  public function form($form, &$form_state) {
+  public function build(array $form, array &$form_state) {
     $entities = $this->load();
     $form['#attached']['css'][] = drupal_get_path('module', 'block') . '/block.admin.css';
     $form['#attached']['library'][] = array('system', 'drupal.tableheader');
@@ -175,15 +185,23 @@ public function form($form, &$form_state) {
       '#type' => 'submit',
       '#value' => t('Save blocks'),
       '#button_type' => 'primary',
-      '#submit' => array(array($this, 'submit')),
     );
     return $form;
   }
 
   /**
+   * Implements \Drupal\Core\Form\FormInterface::validate().
+   */
+  public function validate(array &$form, array &$form_state) {
+    // No validation.
+  }
+
+  /**
+   * Implements \Drupal\Core\Form\FormInterface::submit().
+   *
    * Form submission handler for the main block administration form.
    */
-  public function submit($form, &$form_state) {
+  public function submit(array &$form, array &$form_state) {
     $entities = entity_load_multiple('block', array_keys($form_state['values']['blocks']));
     foreach ($entities as $entity_id => $entity) {
       $entity->set('weight', $form_state['values']['blocks'][$entity_id]['weight']);
diff --git a/core/modules/field_ui/field_ui.admin.inc b/core/modules/field_ui/field_ui.admin.inc
index 4056e4a..566959b 100644
--- a/core/modules/field_ui/field_ui.admin.inc
+++ b/core/modules/field_ui/field_ui.admin.inc
@@ -313,9 +313,7 @@ function field_ui_field_overview($entity_type, $bundle) {
   $bundle = field_extract_bundle($entity_type, $bundle);
   field_ui_inactive_message($entity_type, $bundle);
 
-  $field_overview = new FieldOverview($entity_type, $bundle);
-
-  return drupal_get_callback_form('field_ui_field_overview_form', array($field_overview, 'form'), array($entity_type, $bundle));
+  return drupal_get_form(new FieldOverview($entity_type, $bundle));
 }
 
 /**
@@ -360,9 +358,7 @@ function field_ui_display_overview($entity_type, $bundle, $view_mode) {
   $bundle = field_extract_bundle($entity_type, $bundle);
   field_ui_inactive_message($entity_type, $bundle);
 
-  $display_overview = new DisplayOverview($entity_type, $bundle, $view_mode);
-
-  return drupal_get_callback_form('field_ui_display_overview_form', array($display_overview, 'form'), array($entity_type, $bundle, $view_mode));
+  return drupal_get_form(new DisplayOverview($entity_type, $bundle, $view_mode));
 }
 
 /**
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php b/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php
index 4314fb4..12e6011 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php
@@ -32,19 +32,16 @@ public function getRegions() {
   }
 
   /**
-   * Overrides Drupal\field_ui\OverviewBase::form().
-   *
-   * @param array $form
-   *   An associative array containing the structure of the form.
-   * @param array $form_state
-   *   A reference to a keyed array containing the current state of the form.
-   *
-   * @return array
-   *   The array containing the complete form.
+   * Implements \Drupal\Core\Form\FormInterface::getFormID().
    */
-  public function form(array $form, array &$form_state) {
-    $form = parent::form($form, $form_state);
+  public function getFormID() {
+    return 'field_ui_display_overview_form';
+  }
 
+  /**
+   * Implements \Drupal\Core\Form\FormInterface::build().
+   */
+  public function build(array $form, array &$form_state) {
     // Gather type information.
     $instances = field_info_instances($this->entity_type, $this->bundle);
     $field_types = field_info_field_types();
@@ -410,14 +407,9 @@ public function form(array $form, array &$form_state) {
   }
 
   /**
-   * Overrides Drupal\field_ui\OverviewBase::submit().
-   *
-   * @param array $form
-   *   An associative array containing the structure of the form.
-   * @param array $form_state
-   *   A reference to a keyed array containing the current state of the form.
+   * Overrides \Drupal\field_ui\OverviewBase::submit().
    */
-  public function submit(array $form, array &$form_state) {
+  public function submit(array &$form, array &$form_state) {
     $form_values = $form_state['values'];
     $display = entity_get_display($this->entity_type, $this->bundle, $this->view_mode);
 
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php b/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php
index a977df9..d6a0046 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php
@@ -43,19 +43,16 @@ public function getRegions() {
   }
 
   /**
-   * Overrides Drupal\field_ui\OverviewBase::form().
-   *
-   * @param array $form
-   *   An associative array containing the structure of the form.
-   * @param array $form_state
-   *   A reference to a keyed array containing the current state of the form.
-   *
-   * @return array
-   *   The array containing the complete form.
+   * Implements \Drupal\Core\Form\FormInterface::getFormID().
    */
-  public function form(array $form, array &$form_state) {
-    $form = parent::form($form, $form_state);
+  public function getFormID() {
+    return 'field_ui_field_overview_form';
+  }
 
+  /**
+   * Implements \Drupal\Core\Form\FormInterface::build().
+   */
+  public function build(array $form, array &$form_state) {
     // When displaying the form, make sure the list of fields is up-to-date.
     if (empty($form_state['post'])) {
       field_info_cache_clear();
@@ -423,14 +420,9 @@ public function form(array $form, array &$form_state) {
   }
 
   /**
-   * Overrides Drupal\field_ui\OverviewBase::validate().
-   *
-   * @param array $form
-   *   An associative array containing the structure of the form.
-   * @param array $form_state
-   *   A reference to a keyed array containing the current state of the form.
+   * Overrides \Drupal\field_ui\OverviewBase::validate().
    */
-  public function validate(array $form, array &$form_state) {
+  public function validate(array &$form, array &$form_state) {
     $this->validateAddNew($form, $form_state);
     $this->validateAddExisting($form, $form_state);
   }
@@ -532,14 +524,9 @@ protected function validateAddExisting(array $form, array &$form_state) {
   }
 
   /**
-   * Overrides Drupal\field_ui\OverviewBase::submit().
-   *
-   * @param array $form
-   *   An associative array containing the structure of the form.
-   * @param array $form_state
-   *   A reference to a keyed array containing the current state of the form.
+   * Overrides \Drupal\field_ui\OverviewBase::submit().
    */
-  public function submit(array $form, array &$form_state) {
+  public function submit(array &$form, array &$form_state) {
     $form_values = $form_state['values']['fields'];
 
     $bundle_settings = field_bundle_settings($this->entity_type, $this->bundle);
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/OverviewBase.php b/core/modules/field_ui/lib/Drupal/field_ui/OverviewBase.php
index 0313efd..9a37e67 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/OverviewBase.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/OverviewBase.php
@@ -7,10 +7,12 @@
 
 namespace Drupal\field_ui;
 
+use Drupal\Core\Form\FormInterface;
+
 /**
  * Abstract base class for Field UI overview forms.
  */
-abstract class OverviewBase {
+abstract class OverviewBase implements FormInterface {
 
   /**
    * The name of the entity type.
@@ -59,43 +61,15 @@ public function __construct($entity_type, $bundle, $view_mode = NULL) {
   }
 
   /**
-   * Creates a field UI overview form.
-   *
-   * @param array $form
-   *   An associative array containing the structure of the form.
-   * @param array $form_state
-   *   A reference to a keyed array containing the current state of the form.
-   *
-   * @return array
-   *   The array containing the complete form.
-   */
-  public function form(array $form, array &$form_state) {
-    // Add the validate and submit behavior.
-    $form['#validate'] = array(array($this, 'validate'));
-    $form['#submit'] = array(array($this, 'submit'));
-    return $form;
-  }
-
-  /**
-   * Validate handler for the field UI overview form.
-   *
-   * @param array $form
-   *   The root element or form.
-   * @param array $form_state
-   *   The state of the form.
+   * Implements \Drupal\Core\Form\FormInterface::validate().
    */
-  public function validate(array $form, array &$form_state) {
+  public function validate(array &$form, array &$form_state) {
   }
 
   /**
-   * Submit handler for the field UI overview form.
-   *
-   * @param array $form
-   *   An associative array containing the structure of the form.
-   * @param array $form_state
-   *   A reference to a keyed array containing the current state of the form.
+   * Implements \Drupal\Core\Form\FormInterface::submit().
    */
-  public function submit(array $form, array &$form_state) {
+  public function submit(array &$form, array &$form_state) {
   }
 
   /**
diff --git a/core/modules/system/lib/Drupal/system/Tests/Form/CallbackBuilderTest.php b/core/modules/system/lib/Drupal/system/Tests/Form/CallbackBuilderTest.php
deleted file mode 100644
index f1a146d..0000000
--- a/core/modules/system/lib/Drupal/system/Tests/Form/CallbackBuilderTest.php
+++ /dev/null
@@ -1,42 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\system\Tests\Form\CallbackBuilderTest.
- */
-
-namespace Drupal\system\Tests\Form;
-
-use Drupal\simpletest\WebTestBase;
-
-/**
- * Tests form builder callbacks.
- */
-class CallbackBuilderTest extends WebTestBase {
-
-  /**
-   * Modules to enable.
-   *
-   * @var array
-   */
-  public static $modules = array('form_test');
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Form builder callbacks',
-      'description' => 'Tests form builder callbacks.',
-      'group' => 'Form API',
-    );
-  }
-
-  /**
-   * Tests using a static method to build a form.
-   */
-  function testStaticMethodCallback() {
-    $this->drupalGet('form-test/callback-builder');
-    $this->assertText('The Callbacks::buildForm() method was used for this form.');
-    $elements = $this->xpath('//form[@id="form-test-callback-builder-form"]');
-    $this->assertTrue(!empty($elements), 'The correct form ID was used even when it is not the callback function name.');
-  }
-
-}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Form/FormObjectTest.php b/core/modules/system/lib/Drupal/system/Tests/Form/FormObjectTest.php
new file mode 100644
index 0000000..9aee405
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Form/FormObjectTest.php
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Tests\Form\FormObjectTest.
+ */
+
+namespace Drupal\system\Tests\Form;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Tests building a form from an object.
+ */
+class FormObjectTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('form_test');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Form object tests',
+      'description' => 'Tests building a form from an object.',
+      'group' => 'Form API',
+    );
+  }
+
+  /**
+   * Tests using an object as the form callback.
+   */
+  function testObjectFormCallback() {
+    $this->drupalGet('form-test/object-builder');
+    $this->assertText('The FormTestObject::build() method was used for this form.');
+    $elements = $this->xpath('//form[@id="form-test-form-test-object"]');
+    $this->assertTrue(!empty($elements), 'The correct form ID was used.');
+    $this->drupalPost('form-test/object-builder', NULL, t('Save'));
+    $this->assertText('The FormTestObject::validate() method was used for this form.');
+    $this->assertText('The FormTestObject::submit() method was used for this form.');
+  }
+
+}
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 223af2e..8fa0993 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -3284,7 +3284,10 @@ function system_config_form($form, &$form_state) {
   // primary submit handler, do that first, using the same logic.
   if (!isset($form['#submit'])) {
     $form['#submit'] = array();
-    if (function_exists($form_state['build_info']['form_id'] . '_submit')) {
+    if (isset($form_state['build_info']['callback_object'])) {
+      $form['#submit'][] = array($form_state['build_info']['callback_object'], 'submit');
+    }
+    elseif (function_exists($form_state['build_info']['form_id'] . '_submit')) {
       $form['#submit'][] = $form_state['build_info']['form_id'] . '_submit';
     }
     elseif (isset($form_state['build_info']['base_form_id']) && function_exists($form_state['build_info']['base_form_id'] . '_submit')) {
diff --git a/core/modules/system/tests/modules/form_test/form_test.module b/core/modules/system/tests/modules/form_test/form_test.module
index 8c17e01..569cbae 100644
--- a/core/modules/system/tests/modules/form_test/form_test.module
+++ b/core/modules/system/tests/modules/form_test/form_test.module
@@ -6,6 +6,7 @@
  */
 
 use Drupal\form_test\Callbacks;
+use Drupal\form_test\FormTestObject;
 
 /**
  * Implements hook_menu().
@@ -25,10 +26,9 @@ function form_test_menu() {
     'access callback' => TRUE,
     'type' => MENU_CALLBACK,
   );
-  $items['form-test/callback-builder'] = array(
-    'title' => 'Form callback builder test',
-    'page callback' => 'drupal_get_callback_form',
-    'page arguments' => array('form_test_callback_builder_form', '\Drupal\form_test\Callbacks::buildForm'),
+  $items['form-test/object-builder'] = array(
+    'title' => 'Form object builder test',
+    'page callback' => 'form_test_object_builder',
     'access callback' => TRUE,
     'type' => MENU_CALLBACK,
   );
@@ -369,6 +369,13 @@ function form_test_permission() {
 }
 
 /**
+ * Page callback: Displays a form built from an object.
+ */
+function form_test_object_builder() {
+  return drupal_get_form(new FormTestObject());
+}
+
+/**
  * Form submit handler to return form values as JSON.
  */
 function _form_test_submit_values_json($form, &$form_state) {
diff --git a/core/modules/system/tests/modules/form_test/lib/Drupal/form_test/Callbacks.php b/core/modules/system/tests/modules/form_test/lib/Drupal/form_test/Callbacks.php
index ac43172..d9b0fe7 100644
--- a/core/modules/system/tests/modules/form_test/lib/Drupal/form_test/Callbacks.php
+++ b/core/modules/system/tests/modules/form_test/lib/Drupal/form_test/Callbacks.php
@@ -48,12 +48,4 @@ public function validateName(&$element, &$form_state) {
     }
   }
 
-  /**
-   * Form constructor for the Form callback builder test form.
-   */
-  public static function buildForm($form, &$form_state) {
-    $form['element'] = array('#markup' => 'The Callbacks::buildForm() method was used for this form.');
-    return $form;
-  }
-
 }
diff --git a/core/modules/system/tests/modules/form_test/lib/Drupal/form_test/FormTestObject.php b/core/modules/system/tests/modules/form_test/lib/Drupal/form_test/FormTestObject.php
new file mode 100644
index 0000000..f3b0cf3
--- /dev/null
+++ b/core/modules/system/tests/modules/form_test/lib/Drupal/form_test/FormTestObject.php
@@ -0,0 +1,52 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\form_test\FormTestObject.
+ */
+
+namespace Drupal\form_test;
+
+use Drupal\Core\Form\FormInterface;
+
+/**
+ * Provides a test form object.
+ */
+class FormTestObject implements FormInterface {
+
+  /**
+   * Implements \Drupal\Core\Form\FormInterface::getFormID().
+   */
+  public function getFormID() {
+    return 'form_test_form_test_object';
+  }
+
+  /**
+   * Implements \Drupal\Core\Form\FormInterface::build().
+   */
+  public function build(array $form, array &$form_state) {
+    $form['element'] = array('#markup' => 'The FormTestObject::build() method was used for this form.');
+
+    $form['actions']['#type'] = 'actions';
+    $form['actions']['submit'] = array(
+      '#type' => 'submit',
+      '#value' => t('Save'),
+    );
+    return $form;
+  }
+
+  /**
+   * Implements \Drupal\Core\Form\FormInterface::validate().
+   */
+  public function validate(array &$form, array &$form_state) {
+    drupal_set_message(t('The FormTestObject::validate() method was used for this form.'));
+  }
+
+  /**
+   * Implements \Drupal\Core\Form\FormInterface::submit().
+   */
+  public function submit(array &$form, array &$form_state) {
+    drupal_set_message(t('The FormTestObject::submit() method was used for this form.'));
+  }
+
+}
