Fundamentals 4 min read

Why We Need Secondary Constructors and How to Implement Them in Kotlin

The article explains the importance of auxiliary (secondary) constructors for flexible object creation, contrasts PHP's lack of support with Kotlin's feature, and demonstrates clean implementations using primary constructors and static factory methods to improve readability and maintainability.

php中文网 Courses
php中文网 Courses
php中文网 Courses
Why We Need Secondary Constructors and How to Implement Them in Kotlin

Auxiliary (or "multiple") constructors play a crucial role during object creation by adding an extra logical layer, allowing flexible adjustment of initial parameters and enhancing maintainability.

Unfortunately, the PHP language does not support auxiliary constructors, whereas Kotlin provides this capability.

To illustrate, consider a KYC questionnaire where required fields vary according to the user's employment status (employed, self‑employed, retired, unemployed, etc.).

final class Questionnaire { public function __construct( private QuestionnaireId $questionnaireId, private EmploymentStatus $employmentStatus, private ?string $incomeSource, private ?array $annualIncomeRange, ) { } // ... other methods ... } $employed = new Questionnaire(EmploymentStatus::EMPLOYED, 'Company Name', [60000, 80000]); $selfEmployed = new Questionnaire(EmploymentStatus::SELF_EMPLOYED, null, [50000, 60000]); $retired = new Questionnaire(EmploymentStatus::RETIRED, null, [20000, 30000]); $unemployed = new Questionnaire(EmploymentStatus::UNEMPLOYED, null, null);

This approach, however, permits inconsistent objects such as unemployed persons with income, retired persons with company information, or employed persons without income, unless additional validation logic is added.

The cleaner solution is to use a private constructor together with public static factory methods, effectively creating secondary constructors:

final class Questionnaire { private function __construct( private EmploymentStatus $employmentStatus, private ?string $incomeSource, private ?array $annualIncomeRange, ) { } public static function asEmployed(string $incomeSource, array $annualIncomeRange): self { return new self(EmploymentStatus::EMPLOYED, $incomeSource, $annualIncomeRange); } public static function asSelfEmployed(array $annualIncomeRange): self { return new self(EmploymentStatus::SELF_EMPLOYED, null, $annualIncomeRange); } public static function asRetired(array $annualIncomeRange): self { return new self(EmploymentStatus::RETIRED, null, $annualIncomeRange); } public static function asUnemployed(): self { return new self(EmploymentStatus::UNEMPLOYED, null, null); } // ... other methods ... } $employed = Questionnaire::asEmployed('Company name', [50000, 80000]); $selfEmployed = Questionnaire::asSelfEmployed([40000, 60000]); $retired = Questionnaire::asRetired([30000, 50000]); $unemployed = Questionnaire::asUnemployed();

Developers can thus avoid scattered conditional logic, achieve a consistent data model, and greatly improve code readability and maintainability by relying on clearly named factory methods that encapsulate business rules.

design patternsKotlinprogramming fundamentalsobject-orientedConstructors
php中文网 Courses
Written by

php中文网 Courses

php中文网's platform for the latest courses and technical articles, helping PHP learners advance quickly.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.