Wildcards ذات الحد العلوي
? extends T للمنتِجات
Wildcards ذات الحد العلوي درس مجاني في Java Academy على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Java Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Java Academy 4 دروس في المجموع.
الأنواع العامة غير متغايرة
في Java، الأنواع العامة غير متغايرة. فـ List<Integer> ليست List<Number>، رغم أن Integer من النوع Number.
قد يفاجئ هذا المبتدئين، لكنه يحافظ على سلامة نظام الأنواع. وتخفف أحرف البدل هذا القيد بأمان.
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> ints = List.of(1, 2, 3);
// List<Number> nums = ints; // would NOT compile
System.out.println("List<Integer> is not List<Number>");
System.out.println(ints);
}
}حرف البدل ذو الحد الأعلى
? extends T هو حرف بدل ذي حد أعلى. ويعني "نوعًا مجهولًا ما يكون T أو نوعًا فرعيًا من T".
يمكن أن تشير List<? extends Number> إلى List<Integer> أو List<Double> أو List<Number>.
import java.util.List;
public class Main {
public static void main(String[] args) {
List<? extends Number> a = List.of(1, 2, 3);
List<? extends Number> b = List.of(1.5, 2.5);
System.out.println(a.get(0));
System.out.println(b.get(0));
}
}القراءة من منتج
باستخدام ? extends Number، يمكنك قراءة العناصر بأمان باعتبارها Number.
وبغض النظر عن نوع العناصر الفعلي، فمن المؤكد أنه Number على الأقل، ولذلك تكون القراءة آمنة من ناحية الأنواع.
import java.util.List;
public class Main {
static double sum(List<? extends Number> list) {
double total = 0;
for (Number n : list) total += n.doubleValue();
return total;
}
public static void main(String[] args) {
System.out.println(sum(List.of(1, 2, 3)));
System.out.println(sum(List.of(1.5, 2.5)));
}
}لا يمكنك الكتابة فيه
إليك النقطة المهمة: لا يمكنك إضافة عناصر إلى قائمة من النوع ? extends Number (باستثناء null).
لا يعرف المصرّف نوع العنصر الدقيق. فقد تكون القائمة من النوع List<Integer>، ولذلك ستكون إضافة Double غير آمنة.
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<? extends Number> list = new ArrayList<Integer>();
// list.add(1); // does NOT compile
// list.add(1.5); // does NOT compile
System.out.println("Cannot add to ? extends Number");
System.out.println("size = " + list.size());
}
}لماذا يُمنع الكتابة
تخيّل لو كان ذلك مسموحًا. كان بإمكانك تمرير List<Integer> على أنه List<? extends Number>، ثم إضافة Double.
عندئذٍ سيحصل الكود الذي يقرأ القائمة لاحقًا على أنها List<Integer> على ClassCastException. يمنع المصرّف حدوث ذلك بحظر عمليات الكتابة.
import java.util.List;
public class Main {
public static void main(String[] args) {
// Conceptual: this is why add is blocked
System.out.println("Writing a Double into a List<Integer> would corrupt it");
System.out.println("so ? extends bans all adds");
}
}حالة استخدام المنتِج
استخدم ? extends T عندما تكون الطريقة تنتج القيم لك فقط، أي تقرؤها من أجلك.
الطريقة التي تنسخ العناصر من مجموعة مصدر مثال مثالي على ذلك.
import java.util.ArrayList;
import java.util.List;
public class Main {
static List<Number> copyOut(List<? extends Number> source) {
List<Number> dest = new ArrayList<>();
for (Number n : source) dest.add(n);
return dest;
}
public static void main(String[] args) {
System.out.println(copyOut(List.of(1, 2, 3)));
}
}معلمات الأنواع ذات الحدود مقارنةً بمحارف البدل
لا تخلط بين <T extends Number> (وهي معلمة نوع ذات حد) وبين ? extends Number (وهو محرف بدل).
- معلمة النوع: تسمّي النوع لاستخدامه في أنحاء الطريقة.
- محرف البدل: نوع فرعي مجهول وغير مسمّى، يُستخدم عندما لا تحتاج إلى تسميته.
import java.util.List;
public class Main {
static <T extends Number> T first(List<T> list) { return list.get(0); }
public static void main(String[] args) {
Integer i = first(List.of(10, 20));
System.out.println(i);
}
}حدود متعددة
يمكن أن تكون لمعلمة النوع حدود متعددة باستخدام &، مثل <T extends Number & Comparable<T>>.
لا تدعم محارف البدل سوى حد علوي واحد، لذلك تحتاج الحدود المتعددة إلى معلمة نوع مسمّاة.
import java.util.List;
public class Main {
static <T extends Number & Comparable<T>> T max(List<T> list) {
T best = list.get(0);
for (T t : list) if (t.compareTo(best) > 0) best = t;
return best;
}
public static void main(String[] args) {
System.out.println(max(List.of(3, 9, 1, 7)));
}
}محارف البدل مع Collections.max
يستخدم JDK الحدود العليا على نطاق واسع، مثلًا عند جمع قيم مجموعة للقراءة فقط أو فحصها.
نبحث هنا عن أكبر عنصر في قائمة معرّفة على أنها منتِج.
import java.util.List;
public class Main {
static double largest(List<? extends Number> nums) {
double max = Double.NEGATIVE_INFINITY;
for (Number n : nums) max = Math.max(max, n.doubleValue());
return max;
}
public static void main(String[] args) {
System.out.println(largest(List.of(4, 2, 9, 1)));
System.out.println(largest(List.of(0.5, 9.9, 3.3)));
}
}القيمة الوحيدة القابلة للكتابة هي null
الشيء الوحيد الذي يمكنك إضافته إلى مجموعة من النوع ? extends T هو null، لأن null قابل للإسناد إلى أي نوع مرجعي.
هذا نادرًا ما يكون مفيدًا، لكنه يوضح أن الإضافة ليست محظورة تمامًا، بل محظورة عمليًا.
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<? extends Number> list = new ArrayList<Number>();
// Only null is assignable; demonstrated conceptually
System.out.println("Only null could be added; we avoid that");
System.out.println("size = " + list.size());
}
}تنجح القراءة دائمًا باعتبار العنصر Object
بما أن كل نوع يمتد من Object، يمكنك دائمًا قراءة عناصر أي قائمة ذات محرف بدل باعتبارها Object.
ومع وجود حد علوي هو Number، تحصل على ميزة إضافية تتيح لك قراءتها مباشرة باعتبارها Number.
import java.util.List;
public class Main {
public static void main(String[] args) {
List<? extends Number> nums = List.of(1, 2.0, 3L);
for (Object o : nums) System.out.println(o.getClass().getSimpleName());
}
}تحقق سريع
اختبر مدى فهمك للحدود العليا.
مراجعة
لقد تعلمت محارف البدل ذات الحدود العليا:
? extends Tيعني نوعًا فرعيًا غير معروف من T.- يمكنك قراءة العناصر باعتبارها T (أي أن المجموعة منتِج).
- لا يمكنك إضافة عناصر غير فارغة.
- الأنواع العامة غير متغايرة، وتتيح محارف البدل مرونة آمنة فيها.
بعد ذلك، سنتناول الصورة المعاكسة: محارف البدل ذات الحدود السفلى.
import java.util.List;
public class Main {
public static void main(String[] args) {
List<? extends Number> nums = List.of(1, 2, 3);
System.out.println("Upper bound recap: read " + nums.get(0));
}
}الأسئلة الشائعة
هل درس «Wildcards ذات الحد العلوي» مجاني؟
نعم — نص درس «Wildcards ذات الحد العلوي» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Java Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Java Academy 4 دروس في المجموع.
ماذا ستتعلم في «Wildcards ذات الحد العلوي»؟
? extends T للمنتِجات تتمرن على Java Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Java Academy؟
لا تُشترط خبرة سابقة. Java Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.
كم من الوقت يستغرق درس «Wildcards ذات الحد العلوي»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Java Academy هذا؟
نعم. كل درس في Java Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- Wildcards ذات الحد العلوي
- Wildcards ذات الحد السفلي
- مبدأ PECS
- Wildcards في واجهات البرمجة