مقدمة: ثلاثة أسماء لفئة واحدة – كيف لا تختلط عليك الأمور؟
كل مطور Java، ولو مرة واحدة نظر إلى عالم الانعكاس (Reflection)، واجه ثلاثة طرق تبدو متشابهة: getName() و getSimpleName() و getCanonicalName(). للوهلة الأولى، جميعها تُرجع اسم الفئة. ولكن بمجرد أن تدخل الفئات الداخلية أو المصفوفات أو الفئات المجهولة في اللعبة، يبدأ الالتباس. هذا السؤال بالتحديد جمع آلاف المشاهدات على Stack Overflow، واليوم سنفهم الفرق مرة واحدة وإلى الأبد.
في هذه المقالة، لن نقتصر على سرد التعريفات، بل بأمثلة كود حقيقية سنوضح كيفية عمل هذه الطرق. ستعرف متى تستخدم كل منها وما هي المزالق التي تنتظرك. هيا بنا!
1. المفاهيم الأساسية: ماذا يُرجع كل طريقة؟
قبل أن نغوص في التفاصيل، دعنا نحدد ما تعنيه هذه المصطلحات في سياق Java Class API.
- Simple Name (
getSimpleName()): أقصر اسم. هو ببساطة اسم الفئة بدون الحزمة (package). بالنسبة للفئات الداخلية، هو فقط اسم الفئة الأعمق. - Class Name (
getName()): الاسم الكامل، ولكن مع مراعاة البنية الداخلية لـ JVM. للفئات العادية، هو package + class. للفئات الداخلية، يُستخدم الفاصل$. للمصفوفات، تُضاف أقواس مربعة. - Canonical Name (
getCanonicalName()): الاسم "القانوني" الذي يمكن استخدامه في الكود المصدري لتعريف متغير. هو أقرب ما يكون إلى طريقة كتابتك للكود: بنقاط بدلاً من علامات الدولار.
يبدو الأمر مجردًا؟ دعنا نثبته بالكود.
2. مثال مع فئة عادية
لنبدأ بأبسط شيء – فئة عادية بدون تداخلات.
// الملف: com/example/MyClass.javapackage com.example;
public class MyClass { public static void main(String[] args) { System.out.println("Simple Name: " + MyClass.class.getSimpleName()); System.out.println("Class Name: " + MyClass.class.getName()); System.out.println("Canonical Name: " + MyClass.class.getCanonicalName()); }}النتيجة:
Simple Name: MyClassClass Name: com.example.MyClassCanonical Name: com.example.MyClassكما ترى، بالنسبة للفئة العادية، getName() و getCanonicalName() متطابقان تمامًا. getSimpleName() يتجاهل الحزمة. لا سحر هنا.
3. الفئات الداخلية (المتداخلة): حيث يبدأ السحر
والآن تخيل أن لدينا فئة داخلية. هنا يظهر الفرق الرئيسي.
package com.example;
public class OuterClass { public static class InnerClass { public void printNames() { System.out.println("Simple: " + getClass().getSimpleName()); System.out.println("Class: " + getClass().getName()); System.out.println("Canonical: " + getClass().getCanonicalName()); } }
public static void main(String[] args) { InnerClass inner = new InnerClass(); inner.printNames(); }}النتيجة:
Simple: InnerClassClass: com.example.OuterClass$InnerClassCanonical: com.example.OuterClass.InnerClassها هو! الفرق يصبح واضحًا:
getSimpleName()أعاد ببساطةInnerClass.getName()يستخدم الرمز$لفصل الفئة الأم عن الفئة الداخلية. هذا هو التمثيل الداخلي لـ JVM.getCanonicalName()يستخدم النقطة – بالطريقة التي ستكتب بها في الكود:OuterClass.InnerClass.
بسبب هذا الرمز $ بالتحديد، يقع العديد من المبتدئين في أخطاء عند محاولة تحميل فئة عبر <