העמסת יתר של מפעילים בקוטלין

1. סקירה כללית

במדריך זה נדבר על המוסכמות שקוטלין מספק לתמיכה בעומס יתר של מפעילים.

2. ה מַפעִיל מילת מפתח

ב- Java, אופרטורים קשורים לסוגי Java ספציפיים. לדוגמה, חוּט וסוגים מספריים ב- Java יכולים להשתמש במפעיל + לשרשור ולהוספה, בהתאמה. אף סוג Java אחר אינו יכול לעשות שימוש חוזר במפעיל זה לטובתו. קוטלין, להיפך, מספק מערך מוסכמות לתמיכה מוגבלת העמסת יתר על המפעיל.

נתחיל בפשטות מחלקת נתונים:

נקודת מחלקת נתונים (val x: Int, val y: Int)

אנו הולכים לשפר את מחלקת הנתונים הזו עם כמה מפעילים.

על מנת להפוך פונקציה Kotlin עם שם מוגדר מראש למפעיל, עלינו לסמן את הפונקציה באמצעות מַפעִיל מַתקֵן. לדוגמא, אנו יכולים להעמיס על ה- “+” מַפעִיל:

כיף אופרטור Point.plus (אחר: Point) = נקודה (x + other.x, y + other.y)

בדרך זו אנו יכולים להוסיף שניים נקודות עם “+”:

>> val p1 = נקודה (0, 1) >> val p2 = נקודה (1, 2) >> println (p1 + p2) נקודה (x = 1, y = 3)

3. עומס יתר לפעולות יונירי

פעולות אוניות הן כאלה שעובדות על אופרנד אחד בלבד. לדוגמה, -a, ++ אוֹ הם פעולות unary. באופן כללי, פונקציות שהולכות להעמיס על אופרטורים לא אוחזים אין פרמטרים.

3.1. יונירי פלוס

מה דעתך לבנות א צוּרָה מסוג כלשהו עם כמה נקודות:

val s = צורה {+ נקודה (0, 0) + נקודה (1, 1) + נקודה (2, 2) + נקודה (3, 4)}

בקוטלין זה אפשרי לחלוטין עם ה- unaryPlus פונקציית מפעיל.

מאז צוּרָה הוא רק אוסף של נקודותואז נוכל לכתוב שיעור, ולעטוף כמה נְקוּדָהעם היכולת להוסיף עוד:

class Shape {נקודות פרטיות val = mutableListOf () אופרטור כיף Point.unaryPlus () {points.add (this)}}

ושימו לב שמה שנתן לנו את צורה {...} התחביר היה להשתמש ב- למבדה עם מקלטים:

צורה מהנה (init: Shape. () -> יחידה): Shape {val shape = Shape () shape.init () צורת החזרה}

3.2. מינוס יונירי

נניח שיש לנו נְקוּדָה נקרא "P" ואנחנו נשלול את התיאומים שלה באמצעות משהו כמו "-P". ואז, כל שעלינו לעשות הוא להגדיר פונקציית אופרטור בשם unaryMinus עַל נְקוּדָה:

כיף מפעיל Point.unaryMinus () = נקודה (-x, -y)

ואז, בכל פעם שאנחנו מוסיפים א “-“ קידומת לפני מופע של נְקוּדָה, מהדר מתרגם אותו ל- a unaryMinus שיחת פונקציה:

>> val p = נקודה (4, 2) >> println (-p) נקודה (x = -4, y = -2)

3.3. תוֹסֶפֶת

אנו יכולים להגדיל כל קואורדינטות אחת על ידי יישום פונקציית אופרטור בשם inc:

fun fun Point.inc () = נקודה (x + 1, y + 1)

הפוסט-תיקון “++” מפעיל, מחזיר תחילה את הערך הנוכחי ואז מגדיל את הערך באחד:

>> var p = נקודה (4, 2) >> println (p ++) >> println (p) נקודה (x = 4, y = 2) נקודה (x = 5, y = 3)

להפך, הקידומת “++” אופרטור, מגדיל תחילה את הערך ואז מחזיר את הערך המצטבר לאחרונה:

>> println (++ p) נקודה (x = 6, y = 4)

גַם, מאז “++” המפעיל מקצה מחדש את המשתנה שהוחל, ואיננו יכולים להשתמש בו val איתם.

3.4. ירידה

די דומה לתוספת, אנו יכולים להפחית כל קואורדינטות על ידי יישום ה- דצמבר פונקציית מפעיל:

כיף מפעיל Point.dec () = נקודה (x - 1, y - 1)

דצמבר תומך גם בסמנטיקה המוכרת למפעילים לפני ואחרי הפחתה כמו בסוגים מספריים רגילים:

>> var p = נקודה (4, 2) >> println (p--) >> println (p) >> println (- p) נקודה (x = 4, y = 2) נקודה (x = 3, y נקודה (x = 2, y = 0)

גם כמו ++ אנחנו לא יכולים להשתמש עם valס.

3.5. לֹא

מה דעתך להעיף את הקואורדינטות רק על ידי ! עמ '? אנחנו יכולים לעשות זאת עם לֹא:

כיף מפעיל נקודה. לא () = נקודה (y, x)

במילים פשוטות, המהדר מתרגם כל דבר "! P" לשיחת פונקציה אל "לֹא" פונקציית מפעיל unary:

>> val p = נקודה (4, 2) >> println (! p) נקודה (x = 2, y = 4)

4. עומס יתר לפעולות בינאריות

מפעילים בינאריים, כשמם, הם אלה שעובדים על שני אופרנדים. לכן, פונקציות שמעמיסות על מפעילים בינאריים צריכות לקבל לפחות טיעון אחד.

נתחיל במפעילי החשבון.

4.1. בנוסף מפעיל חשבון

כפי שראינו קודם, אנו יכולים להעמיס על מפעילים מתמטיים בסיסיים בקוטלין. אנחנו יכולים להשתמש “+” להוסיף שניים נקודות יַחַד:

כיף מפעיל Point.plus (אחר: נקודה): נקודה = נקודה (x + אחר.קס, y + אחר.י)

אז נוכל לכתוב:

>> val p1 = נקודה (1, 2) >> val p2 = נקודה (2, 3) >> println (p1 + p2) נקודה (x = 3, y = 5)

מאז ועוד היא פונקציה של אופרטור בינארי, עלינו להכריז על פרמטר לפונקציה.

כעת, רובנו חווינו את האלגנטיות של צירוף שניים ביג-שלםs:

BigInteger zero = BigInteger.ZERO; BigInteger one = BigInteger.ONE; one = one.add (אפס);

כפי שמתברר, יש דרך טובה יותר להוסיף שתיים BigIntegers בקוטלין:

>> val one = BigInteger.ONE println (אחד + אחד)

זה עובד כי הספרייה הסטנדרטית של קוטלין עצמה מוסיפה את חלקה ההוגן של מפעילי הרחבות בסוגים מובנים כמו ביג-שלם.

4.2. מפעילי חשבון אחרים

דומה ל ועוד, חִסוּר, כֶּפֶל, חֲלוּקָה, ו השאר עובדים באותה צורה:

fun fun Point.minus (אחר: Point): Point = נקודה (x - other.x, y - other.y) fun fun Point.times (אחר: נקודה): נקודה = נקודה (x * other.x, y * other.y) fun fun Point.div (אחר: Point): Point = נקודה (x / other.x, y / other.y) fun fun Point.rem (אחר: נקודה): נקודה = נקודה (x% אחר. x, y% אחר. y)

לאחר מכן, מהדר Kotlin מתרגם כל שיחה ל- “-“, “*”, "/", או "%" ל "מִינוּס", "פִּי", "Div", או "rem" , בהתאמה:

>> val p1 = נקודה (2, 4) >> val p2 = נקודה (1, 4) >> println (p1 - p2) >> println (p1 * p2) >> println (p1 / p2) נקודה (x = 1, y = 0) נקודה (x = 2, y = 16) נקודה (x = 2, y = 1)

או, מה דעתך על שינוי קנה המידה א נְקוּדָה לפי גורם מספרי:

Fun fun Point.times (factor: Int): Point = Point (x * factor, y * factor)

בדרך זו אנו יכולים לכתוב משהו כמו "P1 * 2":

>> val p1 = נקודה (1, 2) >> println (p1 * 2) נקודה (x = 2, y = 4)

כפי שאנו יכולים להבחין מהדוגמה הקודמת, אין חובה ששני אופרנדים יהיו מאותו סוג. הדבר נכון גם לגבי סוגי החזרה.

4.3. קומוטטיביות

לא תמיד מפעילים עמוסים חִלוּפִי. זה, אנחנו לא יכולים להחליף את המפקדים ולצפות שהדברים יעבדו בצורה חלקה ככל האפשר.

לדוגמא, אנו יכולים לשנות קנה מידה נְקוּדָה על ידי גורם אינטגרלי על ידי הכפלתו ל- Int, אמר "P1 * 2", אבל לא להיפך.

החדשות הטובות הן שאנחנו יכולים להגדיר פונקציות אופרטור בסוגים מובנים Kotlin או Java. על מנת להפוך את "2 * p1" עבודה, אנו יכולים להגדיר מפעיל ב- Int:

כיף מפעיל Int.times (נקודה: נקודה): נקודה = נקודה (point.x * זה, point.y * זה)

עכשיו אנחנו יכולים להשתמש בשמחה "2 * p1" גם כן:

>> val p1 = נקודה (1, 2) >> println (2 * p1) נקודה (x = 2, y = 4)

4.4. מטלות מורכבות

עכשיו כשאנחנו יכולים להוסיף שניים BigIntegers עם ה “+” אולי נוכל להשתמש במקצה המתחם עבור “+” שהוא “+=”. בואו ננסה את הרעיון הזה:

var one = BigInteger.ONE אחד + = אחד

כברירת מחדל, נניח כאשר אנו מיישמים את אחד ממפעילי החשבון "ועוד", קוטלין לא רק תומך במוכר “+” מַפעִיל, זה גם עושה את אותו הדבר עבור המקביל מטלה מורכבת, שהוא "+ =".

פירוש הדבר, ללא כל עבודה נוספת, אנו יכולים גם לבצע:

נקודה var = נקודה (0, 0) נקודה + = נקודה (2, 2) נקודה - = נקודה (1, 1) נקודה * = נקודה (2, 2) נקודה / = נקודה (1, 1) נקודה / = נקודה ( 2, 2) נקודה * = 2

אבל לפעמים התנהגות ברירת המחדל הזו היא לא מה שאנחנו מחפשים. נניח שנשתמש “+=” להוסיף אלמנט ל- a אוסף משתנה.

עבור תרחישים אלה, אנו יכולים להיות מפורשים לגביו על ידי יישום פונקציית אופרטור בשם plusAssign:

fun fun MutableCollection.plusAssign (אלמנט: T) {הוסף (אלמנט)}

לכל מפעיל חשבון, יש מפעיל מקצה תרכובות מקביל אשר לכולם יש את "לְהַקְצוֹת" סִיוֹמֶת. כלומר, יש plusAssign, minusAssign, timesAssign, divAssign, ו remAssign:

>> צבעי val = mutableListOf ("אדום", "כחול") >> צבעים + = "ירוק" >> println (צבעים) [אדום, כחול, ירוק]

כל הפונקציות של מפעילי הקצאות מורכבות חייבות לחזור יחידה.

4.5. אמנת שווה

אם נעקוף את שווים בשיטה, אז נוכל להשתמש ב- “==” ו “!=” מפעיליםגם:

class כסף (סכום val: BigDecimal, מטבע val: מטבע): השווה {// הושמט לעקוף שווה כיף (אחר: כל?): בוליאני {אם (זה === אחר) להחזיר נכון אם (אחר! זה כסף) להחזיר שקר אם (סכום! = אחר.סכום) להחזיר שקר אם (מטבע! = אחר.מטבע) להחזיר שקר כוזב נכון} // שווה ליישום hashcode תואם 

קוטלין מתרגם כל שיחה ל “==” ו “!=” מפעילים ל- שווים שיחת פונקציה, כמובן על מנת לבצע את “!=” עבודה, התוצאה של שיחת הפונקציה הופכת. שים לב שבמקרה זה איננו זקוקים ל מַפעִיל מילת מפתח.

4.6. מפעילי השוואה

הגיע הזמן להתחמם ביג-שלם שוב!

נניח שננהל הגיון בתנאי אם כזה ביג-שלם גדול מהאחר. בג'אווה, הפתרון לא כל כך נקי:

אם (BigInteger.ONE.compareTo (BigInteger.ZERO)> 0) {// קצת היגיון}

כאשר משתמשים באותו דבר ביג-שלם בקוטלין נוכל לכתוב זאת בקסם:

אם (BigInteger.ONE> BigInteger.ZERO) {// אותו ההיגיון}

הקסם הזה אפשרי בגלל לקוטלין טיפול מיוחד בג'אווה ניתן להשוות.

במילים פשוטות, אנחנו יכולים להתקשר ל בהשוואה ל שיטה ב ניתן להשוות ממשק על ידי כמה מוסכמות קוטלין. למעשה, כל השוואה שנעשתה על ידי “<“, “”, אוֹ “>=” יתורגם ל- בהשוואה ל שיחת פונקציה.

על מנת להשתמש במפעילי השוואה מסוג Kotlin, עלינו ליישם את זה ניתן להשוות מִמְשָׁק:

class כסף (סכום שווי: BigDecimal, מטבע שווי: מטבע): השוואה {לעקוף כיף השווה ל- (אחר: כסף): Int = להמיר (מטבע דולרים). להשוות (אחר. להמיר (מטבע דולרים)) המרה מהנה מטבע): BigDecimal = // הושמט}

אז נוכל להשוות ערכים כספיים פשוטים כמו:

val oneDollar = כסף (BigDecimal.ONE, Currency.DOLLARS) val tenDollars = Money (BigDecimal.TEN, Currency.DOLLARS) אם (oneDollar <tenDollars) {// הושמט}

מאז בהשוואה ל פונקציה ב ניתן להשוות ממשק כבר מסומן באמצעות מַפעִיל משנה, אנחנו לא צריכים להוסיף את זה בעצמנו.

4.7. בכנס

על מנת לבדוק אם אלמנט שייך ל- עמוד, אנחנו יכולים להשתמש ב- "ב" אֲמָנָה:

fun fun Page.contains (אלמנט: T): בוליאני = אלמנט באלמנטים ()

שוב, המהדר היה מתרגם "ב" ו "! ב" מוסכמות לפונקציה קוראות ל מכיל פונקציית מפעיל:

>> val page = firstPageOfSomething () >> "זה" בעמוד >> "That"! בעמוד

האובייקט בצד שמאל של "ב" יועבר כטיעון ל מכיל וה מכיל הפונקציה תיקרא באופראנד הימני.

4.8. קבל אינדקס

אינדקסים מאפשרים להוסיף אינדקס למופעים מסוג בדיוק כמו מערכים או אוספים. נניח שנדגמן אוסף אלמנטים מדומה כ- עמוד, קורע ללא בושה רעיון מ- Spring Data:

ממשק דף {fun pageNumber (): Int fun pageSize (): אלמנטים מהנים (): MutableList}

בדרך כלל, על מנת לאחזר אלמנט מ- עמוד, עלינו להתקשר תחילה ל אלמנטים פוּנקצִיָה:

>> דף val = firstPageOfSomething () >> page.elements () [0]

מאז עמוד עצמה היא רק עטיפה מהודרת לאוסף אחר, אנו יכולים להשתמש במפעילי האינדקסרים כדי לשפר את ה- API שלו:

כיף למפעיל Page.get (אינדקס: Int): T = רכיבים () [אינדקס]

מהדר קוטלין מחליף כל אחד מהם עמוד [אינדקס] על עמוד אל א קבל (אינדקס) שיחת פונקציה:

>> דף val = firstPageOfSomething () >> דף [0]

אנחנו יכולים להמשיך רחוק יותר על ידי הוספה של כמה טיעונים שאנחנו רוצים לקבל הצהרת שיטה.

נניח שנשיג חלק מהאוסף העטוף:

fun fun Page.get (start: Int, endExclusive: Int): List = elements (). subList (start, endExclusive)

ואז נוכל לפרוס א עמוד כמו:

>> עמוד val = firstPageOfSomething () >> עמוד [0, 3]

גַם, אנו יכולים להשתמש בכל סוגי הפרמטרים עבור ה- לקבל פונקציה של מפעיל, לא רק Int.

4.9. הגדר אינדקס

בנוסף לשימוש באינדקסים ליישום סמנטיקה כמו, אנו יכולים להשתמש בהם כדי לחקות פעולות דמויי סטגם. כל שעלינו לעשות הוא להגדיר פונקציית אופרטור בשם מַעֲרֶכֶת עם שני טיעונים לפחות:

כיף למפעיל Page.set (index: Int, value: T) {elements () [index] = value}

כאשר אנו מכריזים על א מַעֲרֶכֶת פונקציה עם שני ארגומנטים בלבד, יש להשתמש בראשון בתוך הסוגר ובאחד אחר אחרי מְשִׁימָה:

עמוד val: דף = firstPageOfSomething () עמוד [2] = "משהו חדש"

ה מַעֲרֶכֶת לפונקציה יכולים להיות יותר משני טיעונים בלבד. אם כן, הפרמטר האחרון הוא הערך ואת שאר הטיעונים יש להעביר בתוך הסוגריים.

4.10. לעורר

בקוטלין ובשפות תכנות רבות אחרות, ניתן להפעיל פונקציה באמצעות functionName (args) תחביר. אפשר גם לחקות את תחביר שיחת הפונקציה עם ה- לעורר פונקציות מפעיל. לדוגמא, על מנת להשתמש עמוד (0) במקום עמוד [0] כדי לגשת לאלמנט הראשון, אנו יכולים להכריז על הרחבה:

fun fun Page.invoke (אינדקס: Int): T = רכיבים () [אינדקס]

לאחר מכן נוכל להשתמש בגישה הבאה כדי לאחזר רכיב עמוד מסוים:

assertEquals (עמוד (1), "Kotlin")

כאן, קוטלין מתרגם את הסוגריים לשיחה ל- לעורר שיטה עם מספר טיעונים מתאים. יתר על כן, אנו יכולים להכריז על לעורר מפעיל עם מספר כלשהו של טיעונים.

4.11. ועידת איטרטור

מה דעתך לחזור על א עמוד כמו אוספים אחרים? עלינו רק להכריז על פונקציית מפעיל בשם איטרטר עם איטרטור כסוג ההחזרה:

כיף למפעיל. Page.iterator () = אלמנטים (). iterator ()

ואז נוכל לחזור באמצעות א עמוד:

val page = firstPageOfSomething () עבור (e בדף) {// עשו משהו עם כל אלמנט}

4.12. ועידת מטווח

בקוטלין, אנו יכולים ליצור טווח באמצעות “..” מַפעִיל. לדוגמה, “1..42” יוצר טווח עם מספרים בין 1 ל 42.

לפעמים זה הגיוני להשתמש במפעיל הטווח על סוגים אחרים שאינם מספריים. הספרייה הסטנדרטית של קוטלין מספקת א טווח אל אמנה על הכל השוואות:

כיף מפעיל  T.rangeTo (that: T): ClosedRange = ComparableRange (זה, זה)

אנו יכולים להשתמש בזה בכדי לקבל כמה ימים רצופים כטווח:

val now = LocalDate.now () val days = now..now.plusDays (42)

כמו אצל מפעילים אחרים, מהדר קוטלין מחליף כל אחד מהם “..” עם טווח אל שיחת פונקציה.

5. השתמש במפעילים בשיקול דעת

העמסת יתר על המפעיל היא תכונה חזקה בקוטלין מה שמאפשר לנו לכתוב קודים תמציתיים יותר ולעיתים קריאים יותר. עם זאת, בכוח רב באה אחריות גדולה.

עומס יתר על ידי מפעיל יכול להפוך את הקוד שלנו למבלבל או אפילו קשה לקריאה כאשר משתמשים בה לעתים קרובות מדי או משתמשים בהם לרעה.

לכן, לפני שתוסיף מפעיל חדש לסוג מסוים, ראשית, שאל אם המפעיל מתאים סמנטית למה שאנחנו מנסים להשיג. או שאל אם אנו יכולים להשיג את אותו אפקט בעזרת הפשטות רגילות ופחות קסומות.

6. סיכום

במאמר זה למדנו עוד על המכניקה של עומס יתר על המפעיל בקוטלין וכיצד היא משתמשת במערך מוסכמות כדי להשיג זאת.

ניתן למצוא את היישום של כל הדוגמאות וקטעי הקוד בפרויקט GitHub.


$config[zx-auto] not found$config[zx-overlay] not found