אנא עיין בשאלות הנפוצות הבאות למידע נוסף:
http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html#SECTION1
הנה כמה נקודות חשובות מהשאלה הנפוצה הזו.
דוגמה (סוג גנרי):
class Pair<X,Y> {
פרטי X ראשון;
פרטי Y שני; זוג ציבורי (X a1, Y a2) {
ראשון = a1;
שני = a2;
}
public X getFirst() { return first; }
public Y getSecond() { return second; }
public void setFirst(X arg) { first = arg; }
public void setSecond(Y arg) { second = arg; }
}
הכיתה זוּג יש שני פרמטרים מסוג X ו י . הם מוחלפים על ידי ארגומנטים מסוג כאשר הסוג הגנרי זוּג מופעלת. למשל, בהצהרה Pair<String, Date> פרמטר הסוג X מוחלף בארגומנט הסוג חוּט ו י מוחלף על ידי תַאֲרִיך .
היקף המזהים X ו י הוא כל ההגדרה של הכיתה. בהיקף זה שני פרמטרי הסוג X ו י עשמומים משעשים היו טיפועים (עם עם עברה גבלות). בדוגמה למעלה, הפרמטרים של הטיפוס משמשים כסוג הארגומנט והחזרת שיטות הביטוי וסוגי השדות של הביטוי.
نتو لهکریز على فرمتری سوگ عم غوولوت boundaries give access to methods of the unknown type. בדוגמה שלנו, אנחנו לא מפעילים שום שיטות שאינן ידועות X ו י . מסיבה זו, שני פרמטרים מהסוג אינם מוגבלים.
דוגמה (סוג פרמטר בטון):
public void printPair( Pair<String,Long> זוג) {
System.out.println("("+pair.getFirst()+","+pair.getSecond()+")");
} Pair<String,Long> limit = חדש Pair<String,Long> ("מקסימום",1024L);
printPair(limit);
דוגמה (של סוג פרמטר עם תווים כלליים):
public void printPair( Pair<?,?> זוג) {
System.out.println("("+pair.getFirst()+","+pair.getSecond()+")");
} Pair<?,?> limit = חדש Pair<String,Long> ("מקסימום",1024L);
printPair(limit);
כל המופעים מסוג גנרי חולקים את אותו ייצוג סוג זמן ריצה, כלומר הייצוג של הסוג הגולמי. למשל, מופעים מסוג גנרי רְשִׁימָה , כגון רשימה<Date> , רשימה<String> , רשימה<Long> , וכו' ’ יש סוגים סטטיים שונים בזמן הקומפילציה, אבל אותו סוג דינמי רְשִׁימָה תוך כדי ריצה.
צוות השחקנים מורכב משני חלקים:
- בדיקת סוג סטטית שבוצעה על ידי המהדר בזמן ההידור ו
- בדיקת סוג דינמית שבוצעה על ידי המכונה הווירטואלית בזמן ריצה.
החלק הסטטי ממיין ליהוקים שטותיים, שלא יכולים להצליח, כמו הקאסט מ חוּט זֶה תַאֲרִיך או מ רשימה<String> זֶה רשימה<Date> .
החלק הדינמי משתמש במידע על סוג זמן ריצה ומבצע בדיקת סוג בזמן ריצה. זה מאלה א ClassCastException אם הסוג הדינמי של האובייקט אינו סוג המטרה (או תת-סוג של סוג המטרה) של השחקנים. דוגמאות לקאסטים עם חלק דינמי הם הקאסט מ לְהִתְנַגֵד זֶה חוּט או מ לְהִתְנַגֵד זֶה רשימה<String> . אלו המה שכנער מושפלים, מסופר-פיפוז מתחת וד תת-פיף.
דוגמאות לסוגי פרמטר בטון הן רשימה<String> , Map<String,Date> , אבל לא רשימת <? מרחיב את Number> אוֹ Map<String,?> .
עבור עבור כי א רשימה<Object> יהיה טיפוס על של א רשימה<String> , כי לְהִתְנַגֵד הוא טיפוס על של חוּט . ציפייה זו נובעת מהעובדה שסוג כזה של קשר קיים עבור מערכים: לְהִתְנַגֵד[] הוא טיפוס על של חוּט[] , כי לְהִתְנַגֵד הוא טיפוס על של חוּט . (סוג זה של מערכת יחסים ידוע בשם שיתופיות .) יחסי-הסופר-תת-טיפוס של סוגי הרכיבים משתרעים לסוגי המערך המתאימים. לא קיים קשר טיפוס כזה עבור מופעים של טיפוסים גנריים. (סוגים עם פרמטרים הם לֹא קווריאנט.)
להיעדר עשע-על-תת-טיפוש בין משעעים מאותו סוג גנרי יש עשעשון שלון. הנה דוגמה.
דוּגְמָה:
void printAll(ArrayList<Object> c) {
עבור (Object o : c)
System.out.println(o);
} ArrayList<String> list = new ArrayList<String>();
... למלא רשימה ...
printAll(רשימה); // שגיאה
א ArrayList<String> לא ניתן להעביר אובייקט כארגומנט למתודה המבקשת א ArrayList<Object> כי שני הסוגים הם מופעים מאותו סוג גנרי, אבל עבור שׁוֹנֶה सुग रगुमन्तिमिम, וप्रण्वार्ज जू हो।ह। तुम्यम् जे जे जा.
سوگی فرمتری بتون هم شوگایم كونکرتیمی سوگ غنری. הם כמעט כמו טיפוסים; יש רק כמה מגבלות. לא ניתן להשתמש בהם למטרות הבאות:
- ליצירת מערכים
- בטיפול חריג
- בכיתה מילולית
- ב- מופע של הַבָּעָה
מערכים המכילים אלמנטים שהסוג שלהם הוא סוג פרמטר הטון אינם חוקיים.
דוגמה (לדוגמה מערך):
static void test() {
Pair<Integer,Integer>[] intPairArr = חדש Pair<Integer,Integer>[10] ; // שגיאה
addElements(intPairArr);
Pair<Integer,Integer> pair = intPairArr[1];
מספר שלם i = pair.getFirst();
pair.setSecond(i);
}
static void addElements( לְהִתְנַגֵד[] objArr) {
objArr[0] = New Pair<Integer,Integer>(0,0);
objArr[1] = New Pair<String,String>("",""); // אמור להיכשל עם ArrayStoreException
}
המהדר אוסר על יצירת מערכים שסוג הרכיב שלהם הוא סוג פרמטר קונקרטי, כמו Pair<Integer,Integer> בדוגמה שלנו. דנו בערך הקודם מדוע זה סביר שהמהדר מתאים לא Pair<Integer,Integer>[] כבלתי חוקי. הבעיה העיקרית היא שהמהדר ומערכת זמן הריצה חייבים להבטיח שמערך הוא א הוֹמוֹגֵנִי רצף של אלמנטים מאותו סוג. אחת מבדיקות הסוג, כלומר בדיקת המערך-מחסן שבוצעה על ידי המכונה הווירטואלית בזמן הריצה, לא מצליחה לזהות את ההחדרה הפוגענית של אלמנט זר. בדוגמה ההוספה השנייה ב- addElements השיטה הייתה אמורה להיכשל כי הם הוסיפו זוג מחרוזות למערך הערכים האינטגרליים, אך היא לא נכשלה כצפוי. הסיבות נדונו במאמר הקודם.
אם אנחנו לא יכולים להשתמש בערכים המכילים אלמנטים שהסוג שלהם הוא פרמטרים קונקרטיים, איך אנחנו משתמשים בהם?
בואו נסתכל על 3 דרכים לעקוף את הבעיה:
- מערך סוגי גלם
- מערך של סוג פרמטר ללא תווים כלליים
- אוסף במקום מערך
בעוד הערכים של סוגי הפרמטרים בטון אינם תקפים, הערכים של סוגי הפרמטרים בטון מותרים.
סוג הביטוי עם תווים כלליים הוא מופע מסוג גנרי עם ארגומנט אחד לפחות. דוגמאות לסוגים עם פרמטרים Collection<?> , רשימת <? מרחיב את Number> , Comparator<? סופר String> ו Pair<String,?> . סוג פרמטר עם תווים כלליים מציין משפחה של טיפוסים הכוללת מופעים קונקרטיים מסוג גנרי. סוג התו הכללי שבו נעשה שימוש קובע אילו סוגי פרמטרים בטון שייכים למשפחה. לדוגמה, הסוג בעל הפרמטרים של התווים הכלליים Collection<?> מציין את המשפחה של כל המופעים של ה גְבִיָה ממשק ללא קשר לארגומנט הסוג. הסוג בעל הפרמטרים של התווים הכלליים רשימת <? מרחיב את Number> מציין את המשפחה של כל סוגי הרשימות שבהם סוג האלמנט הוא תת-סוג של מִספָּר . הסוג בעל הפרמטרים של התווים הכלליים Comparator<? סופר String> היא המשפחה של כל המופעים של ה משווה ממשק עבור טיפוסי ארגומנטים שהם טיפוסי על של חוּט.
טיפוס פרמטר עם תווים כלליים אינו טיפ במובן הרגיל (שונה ממחלקה/ממשק ללא פרמטרים או סוג גולמי).
ניתן להשתמש בסוגים עם פרמטרים של תווים כלליים להקלדה (כמו מחלקות וממשקים ללא פרמטרים):
- כסוגי ארגומנט והחזרה של שיטות
- כסוג של שדה או משתנה ייחוס מקומי
- כסוג רכיב של ארגומנט מסוג מערכים מסוגים אחרים עם פרמטרים
- כסוג מטרה בגבס
לא רק טיפוסים יכולים להיות גנריים, אלא ששיטות יכולות להיות כלליות. שיטות סטטיות ולא סטטיות, כמו גם בונים, יכולות להיות פרמטרים מסוג. תחביר להכרזה על פרמטרי הסוג הפורמלי דומה לתחביר של טיפוסים גנרים. קטע פרמטר הסוג מופרד בסוגרים של אחוטלו ומעבורים מפשוג ההכרטע של אשטיטה. תחביר והמשמעות שלו זה ללישיטה פרמטרי הסוג של סוג גנרי.
הנה דוגמה לגנריות מקסימום שיטה המחשבת את הערך הגדול ביותר באוסף של אלמנטים מסוג לא ידוע א .
דוגמה (לשיטה גנרית):
אוספי כיתה {
סטטי ציבורי <A מרחיב Comparable<A>> A max(Collection< א > xs) {
Iterator< א > xi = xs.iterator();
א w = xi.next();
while (xi.hasNext()) {
א x = xi.next();
if (w.compareTo(x) < 0) w = x;
}
חזרה w;
}
}
ה מקסימום לשיטה יש פרמטר סוג אחד, בשם א . זהו מחזיק מקום לסוג האלמנט של האוסף עליו עובדת השיטה. לפרמטר הסוג יש גבול; זה חייב להיות סוג שהוא תת-סוג של ניתן להשוואה <A>
דוגמה (מאותו סוג גנרי, הפעם עִם גבולות):
ממשק ציבורי Comparable<T> {
public int compareTo(T arg);
}
כיתה ציבורית TreeMap<Key מרחיב Comparable<Key> ,Data>{
מחלקה סטטית פרטית Entry<K,V> {
מפתח K;
ערך V;
Entry<K,V> left = null;
Entry<K,V> right = null;
Entry<K,V> אב;
}
Entry transient private<Key,Data> root = null;
...
פרטי כניסה<Key,Data> getEntry(מפתח מפתח) {
Entry<Key,Data> p = root;
מפתח k = מפתח;
while (p != null) {
int cmp = k. compareTo(p.key) ;
if (cmp == 0)
החזר p;
אחרת אם (cmp < 0)
p = p.left;
אַחֵר
p = p.right;
}
החזר null;
}
public Boolean containsKey(Key key) {
return getEntry(key) != null;
}
...
}
בדוגמה למעלה, פרמטר הסוג מַפְתֵחַ יש את הכבול ניתן להשוואה <Key> . למפרט של כבול יש שתי השפעות:
- היא נותנת גישה לשיטות שהגבול מציין . בדוגמה, המחובר ניתן להשוואה <Key> נותן גישה ל השווה ל שיטה שאנו רוצים להפעיל ביישום שלנו מפת עץ מַחלָקָה.
- אתה יכול להשתמש ב-“ רק במגבלות ” עבור התקנה של הסוג הגנרי. בדוגמה, סוג בעל פרמטרים כגון TreeMap<Number,String> יידחה, כי הסוג מִספָּר אינו תת סוג של ניתן להשוואה <Number> . סוג פרמטר כמו TreeMap<String,String> יתקבל, מכיוון שהסוג String נמצא בגבולות, כלומר הוא תת-סוג של ניתן להשוואה <String> .
כל אלמחקלטים, הממשקים וסוגי ה-enum יכולים לשמש כמאגד פרמטרי, כולל סוגי מקוננים ופנימיים. לא פיפוזים פרימיטיביים ולא טיפופים של מערכים משעשים כגבול פרמטרי טיפוף.
דוגמאות:
מחלקה X0 <T מתארכת int > { ... } // שגיאה
מחלקה X1 <T מתארכת לְהִתְנַגֵד[] > { ... } // שגיאה
מחלקה X2 <T מתארכת מִספָּר > { ... }
מחלקה X3 <T מתארכת חוּט > { ... }
מחלקה X4 <T מתארכת ניתן להרצה > { ... }
מחלקה X5 <T מתארכת שרשור.מדינה > { ... }
מחלקה X6 <T מתארכת רְשִׁימָה > { ... }
מחלקה X7 <T מתארכת רשימה<String> > { ... }
מחלקה X8 <T מתארכת רשימת <? מרחיב את Number> > { ... }
מחלקה X9 <T מתארכת ניתן להשוות <? super Number> > { ... }
מחלקה X10<T מתרחבת Map.Entry<?,?> > { ... }
דוגמת הקוד מראה שטיפוסים פרימיטיביים כגון int וסוגי מערכים כגון לְהִתְנַגֵד[] אין מותרים כפרמטר סוג סוג
סוגי כיתות, כגון מִספָּר אוֹ חוּט , וסוגי ממשקים, כגון ניתן להרצה , מותרים כגבול פרמטר סוג.
סוגי Enum כגון שרשור.מדינה מותרים גם כפרמטר מסוג סוג. שרשור.מדינה הוא מקוחם לטיפוז מקוננן משמש כסוג כפרמטר סווג. מותרים חעם ביקורות פנישים נעייטים סטיטים.
סוגים רְשִׁימָה מהווה דוגמה.
פיפוסים עבור פארמטרים מותרות כפארמטרים סווגים, קוליטים עבור פארמטרים קטרטים עשוטים רשימה<String> , סוגים בעלי פרמטרים עם תווים כלליים מוגבלים כגון רשימת <? מרחיב את Number> ו ניתן להשוות <? סופר Long> , וסוגים בעלי פרמטרים ללא תווים כלליים כגון Map.Entry<?,?> . bound שעוע שוג פרמטר עמ תים כלים מעשים כארגומנט סוג את כל הסוגים היייקם למצעת הטיפוים שהתו כלי מצענים. הסוג עם הפארמטרים של תוים הכליליים מידע עצים עבור לשדות ולמשידות; ההגבלות תלויות בסוג הכללי.
דוּגמָה
מחלקה X< T מרחיב את List<? מרחיב את Number> > {
public void somemethod(T t) {
t.add(new Long(0L)); // שגיאה
מספר n = t.remove(0);
}
}
מבחן כיתה {
public static void main(String[] args) {
X<ArrayList< אָרוֹך >> x1 = חדש X<ArrayList<Long>>();
X<ArrayList< חוּט >> x2 = חדש X<ArrayList<String>>(); // שגיאה
}
}
משתני התייחסות מסוג ט (פרמטר הסוג) מצפיטלים כמו משתני עברת סווח תוים כלייים (פרמטר הסוג מאוגד). בדוגמה שלנו, התוצאה היא שיטת ההפעלה של הארגומנט מסוג “, שאינו ידוע. רשימה.הוסף , כי הגבול הוא סוג של גבול עם גבול.
יחד עם רשימת <? מרחיב את Number> קובע את הסוגים שיכולים לשמש כארגומנטים מסוג. המהדר מקבל את כל ארגומנטי הטיפוס השייכים למשפחת הטיפוסים רשימת <? מרחיב את Number> , כלומר, כל תת הסוגים של רְשִׁימָה עם ארגומנט סוג שהוא תת-סוג של מִספָּר .
פרמטר סווג עבור כל גבול של סוג פרמטר אחר.
דוגמה (לפרמטר סוג בשימוש פרמטר סוג סוג):
מחלקה טריפל <T> {
פרטי T fst, snd, trd;
< ציבורי U מאריך את T , V מאריך את T , W מאריך את T > Triple(U arg1, V arg2, W arg3) {
fst = arg1;
snd = arg2;
trd = arg3;
}
}
בדוגמה זו פרמטר הסוג ט של המחלקה עם הפרמטרים משמשת כקובול של פרמטרי הסוג U , V ו W של ישעשות משעעע עם פרמטר של אותה לעדים.
הזדמנויות נוספות לשימוש בפרמטרי טיפוף כגולות של של פרמטרי טיפוף אתן אחרים מעברים מביון יהוה טיפוז קוננן מצפרטי טיפוז ככוולים של של פרמטרי טיפוז אתן אחריטים מצפרמטרי מקוננים מקטיפון אור מקומיט טיפוז ועשיטה גנריט. זה אפילו מותר להשתמש בפרמטר סוג כקובול של סוג פרמטר אחר את ספרט פרמטר סוג.
bound הוא מחלקה שנותנת גישה לכל חבריה הציבוריים, כלומר, שדות, שיטות ושיטות ציבוריים. רק בנאים יששים, כי אין ערובה שלת-מלאד של ה-bound יש אותם בנאים כמו ה-bound.
דוּגמָה
מעמד ציבורי SuperClass { // חברים סטטיים
EnumType ציבורי {THIS, THAT}
סטטי ציבורי אובייקט staticField;
public static void staticMethod() { ... }// חברים לא סטטיים
כיתה ציבורית InnerClass { ... }
Public Object nonStaticField;
public void nonStaticMethod() { ... }// בנאים
public SuperClass() { ... }// חברים פרטיים
אובייקט פרטי שדה פרטי;...
}שיעור הגמר הציבורי SomeClass<T מרחיב SuperClass > {
חפץ T פרטי;
public SomeClass(T t) { object = t; }public String toString() {
לַחֲזוֹר
"static Nested type : "+T.EnumType.class+"\n"
+"שדה סטטי : "+T.staticField+"\n"
+"static method : "+T.staticMethod()+"\n"
+"סוג מקונן לא סטטי: "+T.InnerClass.class+"\n"
+"non-static field : "+object.nonStaticField+"\n"
+"non-static method : "+object.nonStaticMethod()+"\n"
+"constructor : "+(חדש T())+"\n" // שגיאה
+"private member : "+object.privateField+"\n" // שגיאה
;
}
}
הכבול SuperClass נותן גישה לסוגים המקוננים שלו, שדות ושיטות סטטיים ושדות ושיטות לא סטטיים. רק הבנאי אינו נגיש. הסיבה לכך היא שבנאים אינם עוברים בירושה. כל תת מחלקה מגדירה בנאים משלה ואינה צריכה לתמוך בבנאים של מחלקת העל שלה. מכאן שאין ערובה שתת-מעמד של SuperClass המחלקה שלו.
ההששר שבו Enum<E מרחיב את Enum<E>> ” אחראתה היא הצהרת ה Enum כיתה בחבילה java.lang :
שיעור מופשט ציבורי Enum<E מרחיב את Enum<E>> {
...
}
הסוג Enum היא מחלקת הבסיס המשותפת של כל סוגי הספירה. ב-Java סוג ספירה כגון צֶבַע מתורגם לכיתה צֶבַע שמתארך Enum<Color> . מטרת כיתת העל Enum הוא מספק פונקציונליות המשותפת עם כל סוגי הספירה.
הנה ציור של הכיתה Enum :
שיעור מופשט ציבורי Enum< ה מרחיב את Enum<E>> מיישם Comparable< ה >, ניתן להסדרה {
שם מחרוזת סופית פרטית;
public final String name() { ... } גמר פרטי int ordinal;
public final int ordinal() { ... }protected Enum(שם מחרוזת, int ordinal) { ... }
מחרוזת ציבורית toString() { ... }
public final boolean equals(Object other) { ... }
public final int hashCode() { ... }
protected final Object clone() זורק CloneNotSupportedException { ... }
הגמר הציבורי int compareTo( ה o) { ... }הגמר הציבורי Class< ה > getDeclaringClass() { ... }
<T סטטי ציבורי מרחיב את Enum<T>> T valueOf(Class<T> enumType, שם מחרוזת) { ... }
}
התכונה המפתיעה היא “ Enum<E מרחיב את Enum<E>> “ היא העובדה שהמחלקה שהוגדרה לאחרונה Enum ופרמטר הסוג שהוגדר לאחרונה ה מופיעים בגבול של אותו פרמטר סוג. זה אומר שה Enum סוג חייב להיות מופע עבור אחד מתתי הסוגים שלו. על מנת להבין מדוע זה הגיוני, קחו בחשבון שכל סוג enum מתורגם לתת-סוג של Enum .
הנה סוג ה-enum מעעוצב צֶבַע :
enum צֶבַע {אדום, כחול, ירוק}
המהדר מתרגם את זה למחלקה הבאה:
שיעור הגמר הציבורי צֶבַע משתרע Enum<Color> {
public static final Color[] values() { return (Color[])$VALUES.clone(); }
public static Color valueOf(שם מחרוזת) { ... } private Color(String s, int i) { super(s, i); }סופי סטטי ציבורי צבע אדום;
סופי סטטי ציבורי צבע כחול;
סופי סטטי ציבורי צבע ירוק;צבע סופי סטטי פרטי $VALUES[];
סטטי {
RED = new Color("RED", 0);
BLUE = new Color("BLUE", 1);
GREEN = new Color("GREEN", 2);
$VALUES = (צבע חדש[] { אדום, כחול, ירוק });
}
}
הירושה משפיעה שה צֶבַע סוג יורש את כל השיטות המיושמות ב Enum<Color> . ביניהם הוא השווה ל שִׁיטָה. ה צֶבַע. השווה ל השיטה כנראה צריכה לקחת א צֶבַע בתור טיעון. על מנת לגרום לזה לקרות בכיתה Enum הוא גנרי וה Enum.compareTo השיטה לוקחת Enum פרמטר הסוג של ה בתור טיעון. כתוצאה מכך, הקלד צֶבַע נגזר מ Enum<Color> יורש א השווה ל שיטה שלוקחת א צֶבַע קמו ותיעון, בדיוק כמו שצריך.
אם ננתח את התצרהה” Enum<E מרחיב את Enum<E>> “anen עם שלדפוס הזאר יש קעמה היבטים.
ראשים, יש את פאחטים שפרמטר הסוג הקבובל הוא הטיפוז אתםומה: “ Enum <E מתארך Enum <E>> “. זה מודא שרק תת-סוגים של סוג Enum מותרים כארגומנטים מסוג. (תיאורטית, הקלד Enum יכול להיות מופע על עצמו, כמו ב Enum<Enum> , אבל זו בהחלט לא הכוונה וקשה לדמיין מצב שבו התוכנית תהיה שימושית.)
קשר, יש את פאחטים שוג הפרמטר bound הוא סוג הפרמטר Enum <E> , המשתמש בפרמטר type ה כארגומנט הסוג של ה-bound. הכרזה זו מוודאת שיחסי הירושה בין תת-סוג לבין מופע של Enum הוא מהצורה X מרחיב את Enum<X> “. תת-סוג כגון “ X מרחיב את Enum<Y> “ לא נפת להכריז בגלל הטיעון הסוג י לא יהיה בגבולות; רק תת-סוגים של Enum<X> נמצאים בגבולות.
תיליה, יש את פאחטי ש Enum הוא גנרי מלכתחילה. זה אומר שחלק מהשיטות של הכיתה Enum קח ארגומנט או החזר ערך מסוג לא ידוע (או תלוי בצורה אחרת בסוג לא ידוע). כפי שאנו כבר יודעים, סוג לא ידוע זה יהיה מאוחר יותר תת-סוג X שֶׁל Enum<X> . לפיכך, בסוג הפרמטר Enum<X> , שיטות אלה כרוכות בתת-הסוג X , והם עוברים בירושה לתוך תת-הסוג X . ה השווה ל שיטה היא דוגע לשיטה עזו; הוא עבר בירושה מעלדות האל כל תחת לאפרות ויש לו אפיטה ספציפית תפת-לאטר בקעט על עדה.
לסיכום, “ Enum<E מרחיב את Enum<E>> “niten לפענח כוול: Enum הוא סוג גנרי שניתן להפעיל רק עבור תת-הסוגים שלו, ותתי-הסוגים הללו ירשו כמה שיטות שימושיות, חלקן לוקחות ארגומנטים ספציפיים לתת-טיפוס (או תלויות אחרת בתת-הסוג).
פרמטרים מסוג גבול תחתון של מחלקות
לפרמטרי סוג יכולים להיות מספר גבולות, כמו ב class Box<T מרחיב את Number> {...} . אבל לפרמטר סוג לא יכול להיות גבול תחתון, כלומר, מבנה כגון מחלקה Box<T גָדוֹל Number> {...} אסור. למה לא? התשובה היא: זה העעם כי זה לא היה כונה לך כלום, ילו היה מותר. הבה שעקים מעבוע פראמטרים ספו עליון של מקלטון מבלבלים עדי בקינת מעדען של ספ עליון פרמטר סוג.
לגבול העליון של סוג הפרמטר יש שלוש השפעות:
- מופע מוגבל. הגבול העליון מגביל את קבוצת הטיפוסים שניתן להשתמש בהם לאינסטציה של הסוג הגנרי. אם נכריז על א class Box<T מרחיב את Number> {...} אז המהדר יבטיח שרק תת-סוגים של מִספָּר יכול לשמש כארגומנט סוג. כלומר, א Box<Number> או א Box<Long> מותר, אבל א Box<Object> אוֹ Box<String> יידחה
דוגמה (של מופע מוגבל עקב גבול עליון של פרמטר סוג):
class Box<T מרחיב את Number> {
ערך T פרטי;
public Box(T t) { value = t; }
...
} מבחן כיתה {
public static void main(String[] args) {
Box<Long> boxOfLong = חדש Box<Long>(0L); // בסדר
Box<String> boxOfString = new Box<String>(""); // שגיאה: המחרוזת אינה בגבולות
}
}
- גישה לחברים לא סטטיים. הגבול העליון נותן גישה לכל השיטות והשדות הציבוריים הלא סטטיים של הגבול העליון. ביישום שלנו class Box<T מרחיב את Number> {...} אנו יכולים להפעיל את כל השיטות הציבוריות הלא סטטיות המוגדרות בכיתה מִספָּר , כגון intValue() לְמָשָׁל. ללא הגבול העליון המהדר ידחה כל קריאה כזו. דוגמה (לגישה לאיברים לא סטטיים עקב גבול עליון בפרמטר סוג):
class Box<T מרחיב את Number> {
ערך T פרטי;
public Box(T t) { value = t; }
public int increment() { return value.intValue()+1; } // <= תהיה שגיאה ללא המספר מוגבל
...
}
- הקלד מחיקה. הגבול העליון השמאלי ביותר משמש למחיקת סוג ומחליף את פרמטר הסוג בקוד הבתים. אצלנו class Box<T מרחיב את Number> {...} כל ההתרחשויות של ט יוחלף בגבול העליון מִספָּר . למשל, אם כיתה קוּפסָה יש שדה פרטי של סוג ט ושיטה void set (תוכן T) להגדרת שדה פרטי זה, השדה יהיה מסוג מִספָּר לאחר מחיקת סוג והשיטה תתורגם לשיטה הוגדר ריק (תוכן מספר) .
דוּגמָה לִפנֵי סוג מחיקה):
מחלקה Box< T מרחיב מספר > {
פְּרָטִי ט עֵרֶך;
תיבה ציבורית( ט t) { value = t; }
...
}
- דוּגמָה
לְאַחַר
- סוג מחיקה):
class Box {
פְּרָטִי מִספָּר עֵרֶך;
תיבה ציבורית( מִספָּר t) { value = t; }
...
}
- בנוסף, הגבול העליון השמאלי ביותר מופיע במקומות נוספים, כגון יציאות שהוכנסו אוטומטית ושיטות גשר.
אם היו מותרים גבולות תחתונים בפרמטרים של סוג, אילו תופעות לוואי היו או צריכות להיות להן? אם מבנה כגון מחלקה Box<T גָדוֹל Number> {...} מה זה אומר? מה יגידו שלוש עפעען אפיטון של גבול פרמטר סווג עליון – משפע עבור לאיבר לא סתי, מחיקת סוג – עבור גבול תטון?
מופעים מוגבלים. המהדר יכול להגביל את קבוצת הטיפוסים שניתן להשתמש בהם לאינסטציה של הסוג הגנרי עם פרמטר טיפוס תחתון. לדוגמה, המהדר יכול לאפשר מופעים כגון Box<Number> ו Box<Object> מא Box<T גָדוֹל Number> ולדחות מופעים כגון Box<Long> אוֹ Box<Short> . זו תהיה השפעה בהתאם לתופעת הלוואי המגבילה שתוארה עבור גבולות פרמטרים מהסוג העליון.
גישה לחברים לא סטטיים. גבול פרמטר סוג נמוך יותר אינו נותן גישה לשיטות מסוימות מעבר לאלו שהועברו בירושה מהמחלקה לְהִתְנַגֵד . בדוגמה של Box<T גָדוֹל Number> טיפוסי העל של מִספָּר אין להם שום דבר במשותף, מלבד שהם טיפוסי התייחסות ולכן תת-סוגים של לְהִתְנַגֵד . המהדר לא יכול להניח ששדה הסוג ט הוא מסוג מִספָּר או תת-סוג שלו. במקום זאת, שדה הסוג ט יכול להיות מכל סוג על של מִספָּר , כגון ניתן להסדרה אוֹ לְהִתְנַגֵד . הזמנת שיטה כגון intValue() אינו בטוח יותר לסוג והמהדר יצטרך לדחות אותו. כתוצאה מכך, הגבול של פרמטר הסוג התחתון לא ייתן גישה לאיברים לא סטטיים מעבר לאלה שהוגדרו בכיתה לְהִתְנַגֵד ובכך יש את אותה השפעה כמו “lla bound”.
הקלד מחיקה . בעקבות קו ההיגיון הזה, אין זה הגיוני להחליף את המופעים של פרמטר הסוג בגבול התחתון השמאלי ביותר שלו. הכרזה על שיטה כמו הבנאי קוּפסָה( ט ט) בתור קונסטרוקטור קוּפסָה( מִספָּר ט) לא הגיוני בהתחשב בכך ט מוחלף על ידי טיפוס על של מִספָּר . א לְהִתְנַגֵד עשוי להיות מועבר בצדק לבנאי ברגע Box<Object> והקונסטרוקטור ידחה את זה. זה יהיה ממש שגוי. אז מחיקת סוג תחליף את כל המופעים של משתנה הסוג ט לפי סוג לְהִתְנַגֵד . שוב, לגבול הצערים תואה להשפיע כמו OZYX0ללא גבול”.
המעת אתה ואחרים מה זה אומר אמן עבור יורדים _ו_ הותר גולות עליונים? באופן אישי, אני אפילו לא רוצה לחשוב על זה, ואני רוצה להגיש את זה תחת “.
השורה התחתונה היא: כל זה “ גָדוֹל “כפופ יקנה לך היא ההגבלה שרק טיפוסי על של מִספָּר יכול לשמש כארגומנטים מסוג. ואפילו זה לא מובן לעתים קרובות. זה לא היה אומר את זה class Box<T super Number> {...} מכיל רק מקרים של טיפוסי על של מִספָּר . לעבור – קפי שמדגימה האחטה מעשע!
דוּגמָה לִפנֵי סוג מחיקה):
מחלקה Box< מספר T סופר > {
פְּרָטִי ט עֵרֶך;
תיבה ציבורית( ט t) { value = t; }
...
}
דוּגמָה לְאַחַר סוג מחיקה):
class Box {
פְּרָטִי לְהִתְנַגֵד עֵרֶך;
תיבה ציבורית( לְהִתְנַגֵד t) { value = t; }
...
}
א מחלקה Box<T גָדוֹל Number> {...} יתורגם על ידי סוג מחיקה ל-a קוּפסָה המכיל לְהִתְנַגֵד השדה והקונסטרוקטור שלו יתורגמו ל Box(Object t) . זה שונה מהותית מא מחלקה Box<T משתרע Number> {...} , שיתורגם לא קוּפסָה המכיל א מִספָּר השדה והקונסטרוקטור שלו יתורגמו ל תיבה (מספר t) . כתוצאה מכך, א Box<Number> מופע מתוך א class Box<T מרחיב את Number> {...} יהיה שונה מא Box<Number> מופע מא class Box<T super Number> {...} , מה שעלול עבור לבלבול. מכיוון שהגבולות אינם תקפים, הם אינם הגיוניים.
פרמטרים מסוג גבול תחתון של שיטות
בשילוב עם השיטות וסוגי הארגומנטים שלו, סוג הפרמטר עם גבול יכול להיות שימושי מדי פעם.
דוגמה (לשיטה שטפיק משפיק מפרמטר סוג עם גבול תחון):
class Pair<X,Y> {
פרטי X ראשון;
פרטי Y שני;
...
< ציבורי סופר X , ב סופר Y > B addToMap(Map<A,B> map) {// שגיאה: פרמטר הסוג לא יכול להיות בעל גבול נמוך יותר
return map.put(first, second);
}
}
מבחן כיתה {
public static void main(String[] args) {
Pair<String,Long> pair = New Pair<>("ABC",42L);
Map<CharSequence, מפת Number> = HashMap<CharSequence, Number>();
מספר מספר = pair.addToMap(map);
}
}
ה addToMap() השיטה מוסיפה את התוכן של הזוג למפה. כל מפה שיכולה להחזיק טיפוסי על של X ו י יעשה. של המפה לָשִׂים() השיטה מחזירה את הערך שנמצא במפה עבור המפתח הנתון, אם כבר יש ערך מפתח-ערך עבור המפתח במפה. ערך ההחזר של המפה לָשִׂים() השיטה תוחזר מה addToMap() שִׁיטָה. בנסיבות אלה, יש להכריז על השיטה כפי שמוצג לעיל: המפה עוברת פרמטרים עם טיפוסי-על של פרמטרי הסוג של הזוג וה- addToMap() סוג ההחזר של השיטה הוא סוג הערך של המפה.
מכיוון שהקומפיילר לא מאפשר גבולות נמוכים יותר בפרמטרים מהסוג שאנחנו צריכים להימנע ממנו.
פתרון אחד שעולה על הדעת הוא השימוש בתווים כלליים, מכיוון שלתווים כלליים יכול להיות גבול נמוך יותר. להלן הפתרון עוקף באמצעות תו כלי.
דוגמה (לעקיפת דרק עבור הדוגמה הקודמת באמצעות תוים כלייים):
class Pair<X,Y> {
פרטי X ראשון;
פרטי Y שני;
...
פּוּמְבֵּי לְהִתְנַגֵד addToMap(Map< ? סופר X , ? סופר Y מפת >) {
return map.put(first, second);
}
}
מבחן כיתה {
public static void main(String[] args) {
Pair<String,Long> pair = New Pair<>("ABC",42L);
Map<CharSequence, מפת Number> = HashMap<CharSequence, Number>();
מספר מספר = (מִספָּר) pair.addToMap(map);
}
}
זה עובד, חוץ מזה שאין דרך להצהיר על סוג ההחזרה כרצוי. זה יהיה טיפוס העל של י שהמהדר לוקד סווג המפה, אבל אין תקביר לציון זה. אסור לנו להכריז על שוג ההחשר כוזיקס ? סופר Y “, כי” ? סופר Y ” هو تو کللی ولا سوگ هو آنو مترح کسوگ הפקרה We have no choice but to use it לְהִתְנַגֵד במקום זאת כסוג ההחזרה של השיטה שלנו. סוג החזרה די לא ספציפי זה מאלץ בתורו מתקשרים של addToMap() שיטה להפיל את ערך ההחזרה מ- לְהִתְנַגֵד מהסוג האמיתי. זה לא בדיוק מה שחשבנו.
פתרון נוסף הוא שימוש בשיטות סטטיות. להלן פתרון עוקף עמן שטיטה סטטיט לא סטטיט לא סטטיט.
דוגמה (לעקיפת דרק עבור הדוגמה הקודמת באמצעות שיטה סטטית):
class Pair<X,Y> {
פרטי X ראשון;
פרטי Y שני;
...
פּוּמְבֵּי סטָטִי < א , ב ,X מרחיב את א ,Y מרחיב את ב > B addToMap(Pair<X, Y> pair, Map< א , ב מפת >) {
return map.put(pair.first,pair.second);
}
}
מבחן כיתה {
public static void main(String[] args) {
Pair<String,Long> pair = New Pair<>("ABC",42L);
Map<CharSequence, מפת Number> = HashMap<CharSequence, Number>();
מספר מספר = זוּג .addToMap( זוּג ,מַפָּה);
}
}
הגנרי addToMap() לשיטה יש ארבעה פרמטרים מסוג: שני מצייני מיקום X ו י עבור סוג הזוג ושני מצייני מיקום א ו ב לסוג המפה. א ו ב הם טיפוסי על של X ו י , כי X ו י מוצהרים עם א ו ב כגבולות העליונים שלהם. (שים לב, פרמטרי הסוג של השיטה הגנרית X ו י אין שום קשר ל זוּג של הכיתה X ו י פרמטרים. השמות X ו י נעשה בו שימוש חוזר לשיטה הגנרית כדי להפוך אותו לזיהוי בקלות כסמל הזוג. באמצעות ארבעה פרמטרים מהסוג שאנו יכולים לראות:
דוגמה (לשימוש באופן חוקי)
class SomeClass <T> {
// מאתחל סטטי, שדה סטטי, שיטה סטטית
סטָטִי {
SomeClass< ט מבחן > = חדש SomeClass< ט >(); // שגיאה
}
פְּרָטִי סטָטִי ט globalInfo; // שגיאה
פּוּמְבֵּי סטָטִי ט getGlobalInfo() { // שגיאה
החזר globalInfo;
}
// אתחול לא סטטי, שדה לא סטטי, שיטה לא סטטית
{
SomeClass< ט מבחן > = חדש SomeClass< ט >();
}
פְּרָטִי ט localInfo;
פּוּמְבֵּי ט getLocalInfo() {
החזר localInfo;
}
// סוגים מקוננים סטטיים
פּוּמְבֵּי סטָטִי מַחלָקָה כישלון מרחיב את החריגה {
גמר פרטי ט מידע; // שגיאה
כישלון ציבורי( ט t) { info = t; } // שגיאה
פּוּמְבֵּי ט getInfo() { return info; } // שגיאה
}
פְּרָטִי מִמְשָׁק ניתן להעתקה {
ט לְהַעְתִיק(); // שגיאה
}
פְּרָטִי enum מדינה {
תקף, לא תקף;
פְּרָטִי ט מידע; // שגיאה
public void setInfo( ט t) { info = t; } // שגיאה
פּוּמְבֵּי ט getInfo() { return info; } // שגיאה
}
// סוגים מקוננים לא סטטיים
פּוּמְבֵּי מַחלָקָה אביזר {
פּוּמְבֵּי ט getInfo() { return localInfo; }
}
}
הדוגמה ממחישה שלא שלא שלא משער פרמטר הסוג מעשמן סטיטי של מצערת גנריט. זה קיים מערכם שמשמשים מקונינים ועוגי ענום שעשותים מעברי סטיטי של האלחת. רק מחלקות פנימיות, כלומר מחלקות לא סטטיות, יכולות להשתמש בפרמטר הסוג של המחלקה הגנרית.
היקף הפרמטר, סוג הרשת, הוא כל הגדרת הרשת, למעט כל השדות או הסוגים. הסיבה לכך היא שדות וסוגים מקוננים המוגדרים בממשקרים הם סטטיים באופן מרומז.
דוגמה (לשימוש עם ממשק גנרי):
ממשק SomeInterface <T> {
// שָׂדֶה
SomeClass< ט ערך > = חדש SomeClass< ט >(); // שגיאה
// סוג מקונן
מַחלָקָה אביזר {
פּוּמְבֵּי ט getInfo() { // שגיאה
return value.getGlobalInfo();
}
}
// שיטות
ט getValue();
}
הדוגמה מראה ששדות של ממשק הם סטטיים באופן מרומז, כך שלא ניתן להשתמש בפרמטר הסוג בשום מקום בהצהרה על שדה של ממשק גנרי. באופן דומה, המחלקה המקוננת נחשבת למחלקה מקוננת סטטית, לא למחלקה פנימית, ומסיבה זו השימוש בפרמטר type בכל מקום במחלקה המקוננת אינו חוקי.
דרך לעקיפת מחלקות וממשקים סטטיים מקוננים:
במקרה של מחלקות וממשקים של שחקנים ממלכתיים, זו לא תאונה גדולה. כפתרון, נוכל ליצור מחלקה סטטית.
דוגמה (לעקיפת הפרטבע – צור את הסוג הסטטי מקוננן):
עטיפה בכיתה <T> {
גמר פרטי ט האובייקט;
עטיפה ציבורית( ט t) { theObject = t; }
פּוּמְבֵּי ט getWrapper() { return theObject; } ציבורי בלתי משתנה <T> makeImmutable() {
להחזיר ללא שינוי <T> (האובייקט);
}
Public Mutable <T> makeMutable() {
להחזיר Mutable חדש <T> (האובייקט);
}
פרטי סטטי <A> A makeClone(A theObject) { ... }מחלקה סופית סטטית ציבורית בלתי ניתנת לשינוי <A> { // הוא מחלקה גנרית כעת
גמר פרטי א האובייקט;ציבורי בלתי משתנה( א arg) {
theObject = makeClone(arg);
}
פּוּמְבֵּי א getWrappedItem() {
החזר makeClone(theObject);
}
}
מחלקה סטטית ציבורית ניתן לשינוי <A> {
... דומה ...
}
}
אין דרך לעקיפת הבעיה עבור סוגי enum מקוננים מכיוון שהם לא יכולים להיות גנריים.
דרך לעקיפת מחלקות סטטיות מקוננות:
אם הסוג הסטטי של הקוד הוא מחלקה, פתרון חלופי הוא לשנות את המחלקה הסטטית למחלקה פנימית שאינה סטטית.
דוגמה (כדי לעקוף את הבעיה – השתמש במחלקה הפנימית):
עטיפה בכיתה <T> {
גמר פרטי ט האובייקט;
עטיפה ציבורית( ט t) { theObject = t; }
פּוּמְבֵּי ט getWrapper() { return theObject; } public Mutable makeMutable() {
לַחֲזוֹר זֶה. new Mutable(theObject);
}
public Immutable makeImmutable() {
לַחֲזוֹר זֶה. new Immutable(theObject);
}
פרטי סטטי <A> A makeClone(A theObject) { ... }מחלקה סופית ציבורית ללא שינוי { // אינו עוד מחלקה סטטית
גמר פרטי ט האובייקט;ציבורי בלתי משתנה( ט arg) {
theObject = makeClone(arg);
}
פּוּמְבֵּי ט getWrappedItem() {
החזר makeClone(theObject);
}
}
מעמד ציבורי ניתן לשינוי
... דומה ...
}
}
פתרון זה מגיע עם מידה מסוימת של תיקון כי לכל המחלקות הפנימיות יש התייחסות נסתרת לסוג החיצוני, בדוגמה זו הם לא צריכים אותו ולא משתמשים בו; האשרעת הנסטרת היא רק חיוב. לעתים קרובות, מחלקות פנימיות משולבות עם שימושים על מעצון אל האפנירות פרט פרטים פרטים של המקיפה מקיפה. אנחנו יכולים לעשות את אותו הדבר כאן.
דוּגמָה
עטיפה בכיתה <T> {
גמר פרטי ט האובייקט;
עטיפה ציבורית( ט t) { theObject = t; }
פּוּמְבֵּי ט getWrapper() { return theObject; } Public Mutable <T> makeMutable() {
החזר את this.new Mutable(theObject);
}
ציבורי בלתי משתנה <T> makeImmutable() {
return this.new Immutable(theObject);
}
פרטי סטטי <A> A makeClone(A theObject) { ... }ממשק ציבורי Immutable<S> {
S getWrappedItem();
}
ממשק ציבורי Mutable<S> {
S getWrappedItem();
void setWrappedItem(S arg);
}
פְּרָטִי מחלקה אחרונה ImmutableImplementation מיישמת Immutable<T> {גמר פרטי T theObject;
public ImmutableImplementation(T arg) {
theObject = makeClone(arg);
}
public T getWrappedItem() {
החזר makeClone(theObject);
}
}
פְּרָטִי מחלקה MutableImplementation מיישמת Mutable<T> {
... דומה ...
}
}
כפי שאתה יכול לראות, הממשקים הציבוריים צריכים להיות גנריים בעוד שהמחלקות הפרטיות הפנימיות יכולות להשתמש בפרמטר הסוג של המחלקה כולה.

השאר תגובה