public function finalize() : FinalizedClassDefinition
    {
        // Automatically ignore any properties generated by phpunit mock objects
        // this allows typed objects to be used as mocks.
        if (is_a($this->class, \PHPUnit_Framework_MockObject_MockObject::class, true)) {
            foreach ($this->properties as $definition) {
                if (strpos($definition->getName(), '__phpunit') === 0) {
                    $definition->setIgnored(true);
                }
            }
        }

        foreach ($this->properties as $definition) {
            if (!$definition->hasType() && !$definition->isIgnored()) {
                throw new IncompleteClassDefinitionException($this->class, $definition);
            }
        }

        $properties               = [];
        $propertiesGroupedByClass = [];
        $defaultInstance          = clone $this->instance;

        foreach ($this->properties as $name => $definition) {
            if (!$definition->isIgnored()) {
                $properties[] = new FinalizedPropertyDefinition(
                    $definition->getName(),
                    $definition->getType(),
                    $definition->getReferenceOn($defaultInstance),
                    $definition->getAccessibility(),
                    $definition->isImmutable()
                );

                $propertiesGroupedByClass[$definition->getClass()][] = $definition->getName();
            }
        }

        /** @var TypedObject $cleanInstance */
        $cleanInstance = $this->reflection->isAbstract() ? null : clone $this->instance;

        return new FinalizedClassDefinition(
            $this->reflection,
            $cleanInstance,
            $properties,
            new PropertyUnsetter($propertiesGroupedByClass)
        );
    }