diff --git a/core/lib/Drupal/Core/Cache/ApcuBackend.php b/core/lib/Drupal/Core/Cache/ApcuBackend.php
index e04e014152..5ba959e9fc 100644
--- a/core/lib/Drupal/Core/Cache/ApcuBackend.php
+++ b/core/lib/Drupal/Core/Cache/ApcuBackend.php
@@ -93,7 +93,7 @@ public function getMultiple(&$cids, $allow_invalid = FALSE) {
       foreach ($result as $key => $item) {
         $item = $this->prepareItem($item, $allow_invalid);
         if ($item) {
-          $cache[$map[$key]] = $item;
+          $cache[$map[$key]] = new CacheItem($item->cid, $item->data, $item->expire, $item->tags, $item->created, $item->valid);
         }
       }
     }
diff --git a/core/lib/Drupal/Core/Cache/CacheBackendInterface.php b/core/lib/Drupal/Core/Cache/CacheBackendInterface.php
index 852305f8f1..975f8bf1a4 100644
--- a/core/lib/Drupal/Core/Cache/CacheBackendInterface.php
+++ b/core/lib/Drupal/Core/Cache/CacheBackendInterface.php
@@ -33,7 +33,7 @@
    *   The "valid" property of the returned object indicates whether the item is
    *   valid or not. Defaults to FALSE.
    *
-   * @return object|false
+   * @return \Drupal\Core\Cache\CacheItemInterface|false
    *   The cache item or FALSE on failure.
    *
    * @see \Drupal\Core\Cache\CacheBackendInterface::getMultiple()
@@ -55,7 +55,7 @@ public function get($cid, $allow_invalid = FALSE);
    *   "valid" property of the returned objects indicates whether the items are
    *   valid or not. Defaults to FALSE.
    *
-   * @return array
+   * @return \Drupal\Core\Cache\CacheItemInterface[]
    *   An array of cache item objects indexed by cache ID.
    *
    * @see \Drupal\Core\Cache\CacheBackendInterface::get()
diff --git a/core/lib/Drupal/Core/Cache/CacheItem.php b/core/lib/Drupal/Core/Cache/CacheItem.php
new file mode 100644
index 0000000000..e0fcb86253
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/CacheItem.php
@@ -0,0 +1,153 @@
+<?php
+
+namespace Drupal\Core\Cache;
+
+/**
+ * Provides a default cache item.
+ *
+ * This class extends \stdClass to retain backwards compatibility in the 8.x.x
+ * branch. The CacheItem has no private properties either as to mimic the
+ * behaviour of a stdClass object. This should be changed to be a real value
+ * object when backwards compatibility breaks are allowed (i.e. 9.0.x).
+ */
+class CacheItem extends \stdClass implements CacheItemInterface {
+
+  /**
+   * The key of this cache item.
+   *
+   * @var string
+   */
+  public $cid;
+
+  /**
+   * The raw value of this cache item.
+   *
+   * @var mixed
+   */
+  public $data;
+
+  /**
+   * The Unix timestamp of when this cache item was created.
+   *
+   * @var int
+   */
+  public $created = 0;
+
+  /**
+   * The Unix timestamp of when this cache item will expire.
+   *
+   * @var int
+   *   Either a Unix timestamp, or
+   *   \Drupal\Core\Cache\CacheBackendInterface::CACHE_PERMANENT when the item
+   *   will never expire automatically.
+   */
+  public $expire = Cache::PERMANENT;
+
+  /**
+   * Whether the cache item is valid or not.
+   *
+   * @var bool
+   */
+  public $valid = FALSE;
+
+  /**
+   * Cache tags of this cache item.
+   *
+   * @var string[] $tags
+   *   An array of tags to be stored with the cache item. These should normally
+   *   identify objects used to build the cache item, which should trigger
+   *   cache invalidation when updated. For example if a cached item represents
+   *   a node, both the node ID and the author's user ID might be passed in as
+   *   tags. For example array('node' => array(123), 'user' => array(92)).
+   */
+  public $tags = [];
+
+  /**
+   * The current checksum.
+   *
+   * @var int
+   */
+  public $checksum = 0;
+
+  /**
+   * Constructs a new CacheItem.
+   *
+   * @param string $cid
+   *   The item's unique key within the cache bin it was stored.
+   * @param mixed $data
+   *   The data of the cache item
+   * @param int $expire
+   *   Either a Unix timestamp, or
+   *   \Drupal\Core\Cache\CacheBackendInterface::CACHE_PERMANENT when the item
+   *   will never expire automatically.
+   * @param string[] $tags
+   *   An array of tags to be stored with the cache item.
+   * @param int $created
+   *   The Unix timestamp of when this cache item was created.
+   * @param bool $valid
+   *   Whether the cache item is valid.
+   */
+  public function __construct($cid, $data, $expire = CacheBackendInterface::CACHE_PERMANENT, array $tags = array(), $created = 0, $valid = FALSE) {
+    $this->cid = $cid;
+    $this->created = $created;
+    $this->data = $data;
+    $this->expire = $expire;
+    $this->tags = $tags;
+    $this->valid = $valid;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getKey() {
+    return $this->cid;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getData() {
+    return $this->data;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCreationTimestamp() {
+    return $this->created;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getExpirationTimestamp() {
+    return $this->expire;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isExpired() {
+    if (CacheBackendInterface::CACHE_PERMANENT === $this->expire) {
+      return FALSE;
+    }
+    else {
+      return \Drupal::time()->getCurrentTime() >= $this->expire;
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTags() {
+    return $this->tags;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isValid() {
+    return $this->valid;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Cache/CacheItemInterface.php b/core/lib/Drupal/Core/Cache/CacheItemInterface.php
new file mode 100644
index 0000000000..1db8306677
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/CacheItemInterface.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace Drupal\Core\Cache;
+
+/**
+ * Defines an interface for cache item value objects.
+ */
+interface CacheItemInterface {
+
+  /**
+   * Returns the key for the current cache item.
+   *
+   * @return string
+   *   The key string for this cache item.
+   */
+  public function getKey();
+
+  /**
+   * Retrieves the value of the item from the cache associated with this objects key.
+   *
+   * @return mixed
+   *   The value corresponding to this cache item's key, or NULL if not found.
+   */
+  public function getData();
+
+  /**
+   * Gets cache entry creation Unix timestamp.
+   *
+   * @return int
+   *   The Unix timestamp when the entry was created.
+   */
+  public function getCreationTimestamp();
+
+  /**
+   * Gets cache entry expiration Unix timestamp.
+   *
+   * @return int
+   *   The Unix timestamp when the entry expires.
+   */
+  public function getExpirationTimestamp();
+
+  /**
+   * Gets whether this item has expired.
+   *
+   * The computed return is based upon the local site time (depending on the
+   * configured locale) which means that the entry may already have expire or is
+   * still valid in the backend while this tells the opposite.
+   *
+   * @return bool
+   *   TRUE if the entry has expired.
+   */
+  public function isExpired();
+
+  /**
+   * Gets entry tags.
+   *
+   * @return string[] $tags
+   *   An array of tags to be stored with the cache item.
+   */
+  public function getTags();
+
+  /**
+   * Returns whether the cache item is valid.
+   *
+   * @return bool
+   *   TRUE if the item is valid, FALSE otherwise.
+   */
+  public function isValid();
+
+}
diff --git a/core/lib/Drupal/Core/Cache/DatabaseBackend.php b/core/lib/Drupal/Core/Cache/DatabaseBackend.php
index 88f018f8cc..66b3d14ec4 100644
--- a/core/lib/Drupal/Core/Cache/DatabaseBackend.php
+++ b/core/lib/Drupal/Core/Cache/DatabaseBackend.php
@@ -121,7 +121,7 @@ public function getMultiple(&$cids, $allow_invalid = FALSE) {
       $item->cid = $cid_mapping[$item->cid];
       $item = $this->prepareItem($item, $allow_invalid);
       if ($item) {
-        $cache[$item->cid] = $item;
+        $cache[$item->cid] = new CacheItem($item->cid, $item->data, $item->expire, $item->tags, $item->created, $item->valid);
       }
     }
     $cids = array_diff($cids, array_keys($cache));
diff --git a/core/lib/Drupal/Core/Cache/MemoryBackend.php b/core/lib/Drupal/Core/Cache/MemoryBackend.php
index 99e6e3a3c6..818fd0afae 100644
--- a/core/lib/Drupal/Core/Cache/MemoryBackend.php
+++ b/core/lib/Drupal/Core/Cache/MemoryBackend.php
@@ -49,7 +49,7 @@ public function getMultiple(&$cids, $allow_invalid = FALSE) {
     foreach ($items as $item) {
       $item = $this->prepareItem($item, $allow_invalid);
       if ($item) {
-        $ret[$item->cid] = $item;
+        $ret[$item->cid] = new CacheItem($item->cid, $item->data, $item->expire, $item->tags, $item->created, $item->valid);
       }
     }
 
diff --git a/core/lib/Drupal/Core/Cache/PhpBackend.php b/core/lib/Drupal/Core/Cache/PhpBackend.php
index d3af7f5843..d42ad618b1 100644
--- a/core/lib/Drupal/Core/Cache/PhpBackend.php
+++ b/core/lib/Drupal/Core/Cache/PhpBackend.php
@@ -96,7 +96,7 @@ public function getMultiple(&$cids, $allow_invalid = FALSE) {
 
     foreach ($cids as $cid) {
       if ($item = $this->get($cid, $allow_invalid)) {
-        $ret[$item->cid] = $item;
+        $ret[$item->cid] = new CacheItem($item->cid, $item->data, $item->expire, $item->tags, $item->created, $item->valid);
       }
     }
 
diff --git a/core/tests/Drupal/Tests/Core/Cache/CacheItemTest.php b/core/tests/Drupal/Tests/Core/Cache/CacheItemTest.php
new file mode 100644
index 0000000000..956208a76b
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Cache/CacheItemTest.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace Drupal\Tests\Core\Cache;
+
+use Drupal\Component\Datetime\TimeInterface;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Cache\CacheItem;
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @coversDefaultClass \Drupal\Core\Cache\CacheItem
+ * @group Cache
+ */
+class CacheItemTest extends UnitTestCase {
+
+  /**
+   * Test that the constructor fills in all properties.
+   *
+   * @covers ::__construct
+   * @covers ::getKey
+   * @covers ::getCreationTimestamp
+   * @covers ::getData
+   * @covers ::getExpirationTimestamp
+   * @covers ::getTags
+   * @covers ::isValid
+   */
+  public function testItem() {
+    $cache_item = new CacheItem('foo', ['animals' => 'owls']);
+    $this->assertSame('foo', $cache_item->cid);
+    $this->assertSame('foo', $cache_item->getKey());
+    $this->assertSame(0, $cache_item->created);
+    $this->assertSame(0, $cache_item->getCreationTimestamp());
+    $this->assertSame(['animals' => 'owls'], $cache_item->data);
+    $this->assertSame(['animals' => 'owls'], $cache_item->getData());
+    $this->assertSame(CacheBackendInterface::CACHE_PERMANENT, $cache_item->expire);
+    $this->assertSame(CacheBackendInterface::CACHE_PERMANENT, $cache_item->getExpirationTimestamp());
+    $this->assertSame([], $cache_item->tags);
+    $this->assertSame([], $cache_item->getTags());
+    $this->assertFalse($cache_item->valid);
+    $this->assertFalse($cache_item->isValid());
+  }
+
+  /**
+   * Tests the isExpired method.
+   *
+   * @param int $expire
+   *   The expiration time.
+   * @param bool $expected
+   *   A boolean to indicate if the item should be expired.
+   *
+   * @covers ::isExpired
+   * @dataProvider provideExpirationData
+   */
+  public function testExpiration($expire, $expected) {
+    $time_service = $this->prophesize(TimeInterface::class);
+    $time_service->getCurrentTime()->willReturn(200);
+
+    $container = new ContainerBuilder();
+    $container->set('datetime.time', $time_service->reveal());
+    \Drupal::setContainer($container);
+
+    $cache_item = new CacheItem('bar', 'More owls', $expire);
+    $this->assertSame($expected, $cache_item->isExpired());
+  }
+
+  /**
+   * Provides test data.
+   *
+   * @return array
+   */
+  public function provideExpirationData() {
+    return [
+      [CacheBackendInterface::CACHE_PERMANENT, FALSE],
+      [CacheBackendInterface::CACHE_PERMANENT, FALSE],
+      [10, TRUE],
+      [1000, FALSE],
+    ];
+  }
+
+}
