ইউনিয়ন টাইপ ব্যবহার করার সময় আপনাকে কোভারিয়েন্স এবং কন্ট্রাভারিয়েন্সের ধারণাগুলো সম্পর্কে সচেতন থাকতে হবে। এগুলো ডকুমেন্টেশন-এ ব্যাখ্যা করা আছে, তবে এই উদাহরণগুলো সহায়ক হতে পারে।
ধরুন আমাদের একটি Calculator ক্লাস আছে যেটি উপরের উদাহরণের মতো একটি ফাংশন ব্যবহার করে:
class Calculator
{
public function add(int|float $x, int|float $y): int|float
{
return $x + $y;
}
}
আপনি যদি ক্লাসটি এক্সটেন্ড করেন, তবে প্যারামিটার টাইপ এবং রিটার্ন টাইপ পরিবর্তন করার অনুমতি আপনার আছে, কিন্তু কেবল নিচের নিয়ম অনুযায়ী।
প্রথমত, প্যারামিটার টাইপ "চওড়া" হতে পারে, অর্থাৎ তারা বেস ক্লাসের চেয়ে বেশি টাইপ গ্রহণ করতে পারে। উদাহরণস্বরূপ, এটি অনুমোদিত:
class StringCalc extends Foo
{
public function add(float|int|string $x, float|int|string $y): int|float
{
if (!is_numeric($x) || !is_numeric($y)) {
throw new \InvalidArgumentException('$x and $y must be numeric');
}
return $x + $y;
}
}
আমরা মূল Calculator-এর জায়গায় StringCalc বসাতে পারি এবং সবকিছু তখনও কাজ করবে। কলকারী আগের মতোই float বা int পাঠাতে পারবে, এবং রিটার্ন টাইপও প্রত্যাশিত int বা float-ই থাকবে।
একইভাবে, আপনি রিটার্ন টাইপ সংকীর্ণ করতে পারেন, অর্থাৎ ইউনিয়ন থেকে টাইপ সরিয়ে এটি আরও নির্দিষ্ট করতে পারেন। এই উদাহরণটি এই নিয়মের সুবিধা নেয়:
class IntCalc extends Calculator
{
public function add(float|int $x, float|int $y) : int
{
return (int)($x + $y);
}
}
যেহেতু IntCalc এখনও int এবং float গ্রহণ করে, এটি কোনো সমস্যা ছাড়াই মূল Calculator-এর বিকল্প হিসেবে কাজ করতে পারে।
কী অনুমোদিত আর কী নয় সে সম্পর্কে আরও পড়তে, আপনি Liskov Substitution Principal (LSP) দেখতে পারেন।
কী অনুমোদিত হবে না তার একটি উদাহরণের জন্য, নিচের InvalidCalc দেখুন:
class InvalidCalc extends Calculator
{
public function add(int $x, int $y) : int|float|string
{
// Declaration must be compatible with Calculator->add(x: float|int, y: float|int)
// Return type declaration must be compatible with Calculator->add(x: float|int, y: float|int) : float|int
}
}
InvalidCalc ক্লাসটি কম্পাইল হবে না এবং এতে দুটি সমস্যা আছে। প্যারামিটার হিসেবে কী অনুমোদিত তা কমিয়ে দেওয়ার কারণে, InvalidCalc মূল Calculator-এর জায়গা নিতে পারবে না, কারণ এটি আর float গ্রহণ করে না। উপরন্তু, এটি এখন একটি string রিটার্ন করতে পারে, যা মূল ক্লাসের অনুমোদিত রিটার্ন টাইপের মধ্যেও পড়ে না।