בהינתן אלגוריתם מסוים, למשל אלגוריתם למיון מערך, אין דרישה בסיסית באלגוריתם לדעת כל פרט על הטיפוס של אברי המערך. כל שיש לדעת הוא את גודל המערך, כיצד ניתן לבצע השוואה בין איברי המערך, וכיצד ניתן לשנות את הסדר שלהם. בשפות תכנות בעלות טיפוסיות חזקה וסטטית, שמקפידות על סוג המשתנה שמועבר לפרוצדורה, זו עשויה להיות בעיה: בכתיבת פרוצדורה שתמיין את איברי המערך, מאיזה טיפוס נגדיר את הפרמטרים שלה? יש צורך למצוא דרך להגדיר טיפוס מסוג גנרי כלשהו, שכל הידוע עליו הוא הנתונים החיוניים למימוש הפרוצדורה, ותו לא. ישנן שפות שמאפשרות זאת באמצעות מנגנון לזיהוי אוטומטי של הטיפוס של כל ביטוי (כמו שפת התכנות ML), באמצעות טכניקות של ירושה ודריסה בתכנות מונחה עצמים, או באמצעות תכנות גנרי. כמו כן, כל שפה בעלת מערכת טיפוסים חלשה או דינמית, כגון פייתון, מאפשרת פולימורפיזם.
פולימורפיזם אד-הוק.
העמסה (Overloading).
פולימורפיזם בצורתו הבסיסית ביותר קיים בכל שפת תכנות עילית. כאשר כותבים ביטויים כגון (4 + 4) או (5.3 + 2.1), אנו מנצלים את העובדה שאופרטור החיבור מועמס - כלומר, בעל משמעות כפולה - גם לטיפוס המשמש למספרים שלמים כמו 4, וגם לטיפוס המשמש לייצוג מספרים ממשיים כמו 2.1; זאת, אף על פי שהייצוג של מספרים אלו כביטים בשפת המכונה שונה לחלוטין בין מספרים שלמים למספרים ממשיים, ולכן גם פקודות המכונה המורות למעבד לבצע את פעולת החיבור תהיינה שונות; כל זה שקוף למתכנת, ולכן מקל על עבודתו.
המרה (Coercion).
שפת C מאפשרת צורה מוגבלת מאוד של פולימורפיזם, נוסף על העמסה בסיסית של אופרטורים אריתמטיים: בשפה מוגדרת המרה אוטומטית מכל טיפוס של מצביע אל הטיפוס הגנרי *void, ובחזרה. מנגנון זה מנוצל על ידי פונקציות כגון פונקציית הספרייה הסטנדרטית qsort המשמשת למיון מערך, המקבלת מערך של מצביעים וממיינת אותו, תוך התייחסות אל כל המצביעים במערך כ-*void. טכניקה זו מאפשרת לבצע כל פעולה, אך היא איננה גמישה, ומצריכה מידע מפורש מהמתכנת בקשר לגודל של משתנים מהטיפוס שמועבר בפועל - מידע שאמור להימצא בידי המהדר בזמן הידור התוכנית, ולכן דורשת עבודה מיותרת. שפות אחרות, כגון שפת פסקל, לעיתים אינן מאפשרות אפילו גנריות בסיסית כזאת.
פולימורפיזם פרמטרי.
שפת C.
ניתן לבצע פולימורפיזם בעזרת פקודות מאקרו, שמבצעות החלפה של טקסט מסוים בתוכנית בטקסט אחר, לפני ההידור. לדוגמה, המאקרו הבא בשפת C :
מדמה פונקציה שמחזירה את המינימום מבין הביטויים המועברים אליה, ומתאימה לכל טיפוס עליו מוגדרת פעולת ההשוואה "&gt;" (פעולה שבפני עצמה מועמסת, בדומה לאופרטור החיבור, ולכן פולימורפית). עם זאת, למאקרו כזה ישנם חסרונות רבים לעומת פונקציה אמיתית: לא מתבצעת בדיקת טיפוסים, ביטוי עם תוצאת לוואי עלול לבצע אותה מספר פעמים, ולהחזיר תוצאה לא צפויה, ועוד.
שפת ++C.
בשפת ++C, פולימורפיזם פרמטרי ממומש בזמן הידור, תוך שימוש בתבניות (templates):
template &lt;typename T&gt;
T max(T x, T y)
return x &lt; y ? y : x;
התבנית הזאת מגדירה משפחה של פונקציות max המקבלות שני איברים מסוג כלשהו, ומספר איברים ראשונים, ומחזירות את הגדול מביניהם. הדרישות היחידות על הטיפוס של המערך הן שניתן לבצע השוואה בין שני איברים באמצעות האופרטור '&gt;', ושניתן לבצע העתקה של איבר. לכן ניתן להעביר כפרמטרים שני ערכים מטיפוס int, או double, או של מחלקה שהגדיר המשתמש ושמממשת את האופרטור '&gt;'.
במקרה זה, בכל קריאה לפונקציה max יבדוק המהדר אם כבר הוגדרה פונקציה כזאת עבור הטיפוס המסוים המופיע בקריאה, ואם לא הוא ייצור פונקציה חדשה שהיא מופע של התבנית שהגדרנו.
ספריית התבניות התקנית של שפת ++C היא דוגמה לתכנות גנרי, ומבוססת ברובה על פולימורפיזם פרמטרי ושימוש בתבניות.
שפת עדה מאפשרת אף היא תכנות גנרי באופן דומה לתבניות של ++C.
שפת ML.
בשפת ML, התומכת בפולימורפיזם מלא ומתוחכם, הדבר מתבצע כך: המתכנת כותב פונקציה, למשל פונקציה שהופכת רשימה, בדרך הטבעית לו, ללא הצהרה על הטיפוס אותו הפונקציה מקבלת. המהדר של השפה מסיק לבד, על פי הביטויים המשמשים בפונקציה, אילו טיפוסים ניתן להעביר אליה. טיפוסים אלה עשויים להיות קונקרטיים, כגון int שהוא מספר שלם, או גנריים, ואז הם יסומנו בתג ושם, למשל a' - זה בעצם "משתנה טיפוס" (type variable). המנגנון ידרוש עקביות: אם פונקציה מקבל שני ארגומנטים מטיפוס a', ניתן להעביר אליה שני משתנים שהם מספר שלם או שני משתנים שהם מספר ממשי, אך לעולם לא משתנה אחד ממשי ואחד שלם, כיוון ששני הארגומנטים חייבים להיות מאותו טיפוס.
דוגמה לפונקציה פולימורפית בשפת ML:
fun f x = [x,x,x];
פונקציה זאת מקבל ערך x מכל טיפוס שהוא, ומחזירה רשימה של שלושה איברים שערכם x. הטיפוס של הפונקציה הוא "פונקציה מטיפוס a אל רשימה של a". בניגוד לתבניות של שפת ++C פונקציה פולימורפית היא פונקציה לכל דבר.
גם שפת Haskell תומכת בפולימורפיזם פרמטרי באופן דומה, ואף נרחב יותר.
שפת ג'אווה.
בשפות ג'אווה יש מתודות גנריות (החל מגרסה 7 ואילך), אך בדומה למחלקות גנריות בג'אווה, המידע לגבי הטיפוסים משמש רק לבדיקה בזמן קומפילציה ואיננו קיים בזמן ריצה, כך שזהו למעשה פולימורפיזם של הכלה (תת-טיפוסים) הנבדק בזמן הידור.
שפת #C.
בשפת C# ישנן מתודות גנריות.
פולימורפיזם של תת-טיפוסים.
שפות תכנות התומכות בפולימורפיזם מאפשרות להגדיר מושגים כלליים ולגזור מהם מקרים ספציפיים יותר. בנוסף, מאפשרת השפה להתייחס למושג בצורה מופשטת. התייחסות למושג בצורה מופשטת, מאפשרת להשתמש בקוד כללי הפועל על סוגים רבים ושונים של מושגים דומים לו. למעשה, ניתן להשתמש בקוד המתאים לעצם הכל טיפוס המהווה מקרה פרטי של המושג הכללי.
למשל, אף שיש מרחק רב בין מנוע של מעבורת חלל לבין מנוע של מכסחת דשא, באמצעות קונספט זה שיש בו הפשטה מושגית (מנוע יוגדר כמכונה הממירה אנרגיה לעבודה מכנית), אפשר להתייחס אליהם ואל מגוון האפשרויות שביניהם (מנועים של מטוס קרב סילוני, מטוס בואינג, מטוס קל, אוניה, טנק, משאית, מכונית, אופנוע, קטנוע), מתוך מחלקה תכנותית אחת, תוך ביצוע התאמות לכל אחד מהמנועים על פי המאפיינים הייחודיים שלו. גישה זו שיוצאת מהאבסטרקט ומגיע אל הקונקרטי ביותר (למשל בתיאור מפורט ומלא של מנוע מכונית מדגם מזדה 3), מונעת את הצורך לכתוב מחדש מחלקות נפרדות, עבור כל אחד ואחד מטיפוסי המנועים השונים, והיישומים המעשיים של כל אחד ואחד מהם (שהרי יש מנועי 4 פעימות של חברות שונות), ומסירה מהתכנות את הסרבול של הכתיבה החוזרת ונשנית ובכך הופכת אותו לריכוזי, יעיל ואלגנטי.
בשפת C# אפשר לבצע פולימורפיזם של תת-טיפוס בכמה דרכים: באמצעות שיטות וירטואליות, באמצעות ממשקים ובאמצעות מחלקה אבסטרקטית.
מנגנונים הרווחים ברב צורתיות.
מנגנונים הרווחים ברב צורתיות, הם:
דוגמאות.
כדי להדגים את הכוח של המנגנון, אפשר לחשוב על דוגמה שכיחה מחיי היום יום, אלגוריתם להחלפת גלגל ברכב:
האלגוריתם, כפי שתואר, מתאים לכל סוג רכב, לאוטובוסים ומשאיות וגם למכוניות קטנות. כמעט כל פעולה המתוארת באלגוריתם תבוצע בצורה שונה בכל סוג רכב. הפעלת מגבה של אוטובוס, לדוגמה, שונה מאוד מהפעלת מגבה של מכונית קטנה. העובדה שהצלחנו לנסח אלגוריתם כללי (גנרי) לכל כלי הרכב אף על פי שבעצם בכל רכב האלגוריתם הוא אחר היא בזכות ההתייחסות המופשטת שלנו לרכב. בזמן שניסחנו את האלגוריתם לא התעניינו באיזה רכב מדובר אלא דיברנו על רכב כלשהו. כל דבר שאמרנו על רכב, דאגנו שיהיה נכון לכל רכב. לדוגמה, אמרנו "חזקו מעט את הברגים" ולא אמרנו "חזקו מעט את ארבעת הברגים".
אלגוריתם זה נכתב פעם אחת ומופעל פעמים רבות על כלי-רכב שונים. בכל פעם האלגוריתם מקבל צורה אחרת, בהתאם לסוג הרכב. ללא מנגנון הפולימורפיזם היה צריך לכתוב את האלגוריתמים הספציפיים עבור כל סוגי כלי-הרכב ולא ניתן היה למצות את המכנה המשותף של כולם.
דוגמה נוספת, מתחום המתמטיקה: אלגוריתם למציאת בסיס אורתונורמלי במרחב מכפלה פנימית (תהליך גרם שמידט). כאשר נתון בסיס קיים:
כדי להיות וקטור יש למלא סט קצר של דרישות שיכול להתמלא על ידי אובייקטים מתמטיים שונים מאוד זה מזה. מכפלה פנימית גם היא יכולה להיות מוגדרת בדרכים רבות מאוד בעלות משמעויות שונות. הוכחת נכונות האלגוריתם מסתמכת רק על סט התכונות שמקיימים כל וקטור וכל מכפלה פנימית ולכן היא תקפה תמיד.
מימוש בתכנות.
פולימורפיזם יכול להיות ממומש בזמן ריצה או בזמן הידור. ב- C++ ממומש פולימורפיזם של זמן ריצה על ידי מצביע מסוג מופשט, המצביע לאובייקט קונקרטי. הקוד הבא מדגים איך ניתן לעשות זאת:
class Car {...};
class Bus: public Car{...};
class PrivateCar: public Car{...};
void changeWheel(Car *p) {
p-&gt;stop();
p-&gt;placeJack();
p-&gt;lift();
int main() {
Car* array[3];
array[0]=new Bus();
array[1]=new PrivateCar();
array[2]=new Bus();
for(int i=0;i&lt;3; ++i)
changeWheel(array[i]);
בדוגמה זו, הפונקציה changeWheel משתמשת במנגנון הפולימורפיזם על מנת לתאר את אופן החלפת הגלגל במכונית כלשהי.