النسخة النهائية (جافا)

في لغة برمجة جافا ، تُستخدم finalالكلمة المفتاحية في سياقات متعددة لتعريف كيان لا يمكن تعيينه إلا مرة واحدة.

بمجرد finalإسناد قيمة لمتغير، يُفترض أن يحتفظ دائمًا بنفس القيمة. إذا كان finalالمتغير يحمل مرجعًا إلى كائن، فإنه يمكن تغيير حالة الكائن من خلال عمليات تُجرى عليه، لكن المتغير سيظل يشير دائمًا إلى نفس الكائن ( finalتُسمى هذه الخاصية عدم التعدي [ 1 ] ). ينطبق هذا أيضًا على المصفوفات، لأنها كائنات؛ فإذا كان finalالمتغير يحمل مرجعًا إلى مصفوفة، فإنه يمكن تغيير مكونات المصفوفة من خلال عمليات تُجرى عليها، لكن المتغير سيظل يشير دائمًا إلى نفس المصفوفة. [ 2 ]

الصفوف النهائية

لا يمكن إنشاء فئات فرعية من فئة نهائية . ولأن القيام بذلك قد يوفر مزايا أمنية وكفاءة، فإن العديد من فئات مكتبة جافا القياسية نهائية، مثل java.lang.Systemو java.lang.String.

مثال:

final class Final { // ... }// فئة مشتقة ممنوعة تمتد من الفئة النهائية { // ... }

الأساليب النهائية

لا يمكن تجاوز أو إخفاء أي دالة نهائية بواسطة الفئات الفرعية. [ 3 ] يُستخدم هذا لمنع حدوث سلوك غير متوقع من فئة فرعية تُغير دالة قد تكون حاسمة لوظيفة الفئة أو اتساقها. [ 4 ]

مثال:

public class Base { public void foo () { // ... }public final void bar () { // ... }public static void baz () { // ... }public static final void qux () { // ... } }public class Derived extends Base { // حسنًا، تجاوز Base::foo() @Override public void foo () { // ... }// ممنوع @Override public void bar () { // ... }// حسنًا، تم تجاوز Base::baz() @Override public static void baz () { // ... }// ممنوع @Override public static void qux () { // ... } }

من المفاهيم الخاطئة الشائعة أن تعريف دالة ما على أنها `final` finalيُحسّن الكفاءة، إذ يسمح للمُصرّف بإدراج الدالة مباشرةً أينما تم استدعاؤها (انظر التوسيع المضمن ). ولأن الدالة تُحمّل أثناء التشغيل ، فإن المُصرّفات لا تستطيع فعل ذلك. بيئة التشغيل ومُصرّف JIT هما فقط من يعرفان تحديدًا الفئات التي تم تحميلها، وبالتالي فهما فقط من يستطيعان اتخاذ القرارات بشأن وقت التضمين، وما إذا كانت الدالة نهائية أم لا. [ 5 ]

تُعدّ مُجمّعات لغة الآلة التي تُنتج شفرة آلة قابلة للتنفيذ مباشرةً ومُخصصة لمنصة مُعينة ، استثناءً. عند استخدام الربط الثابت ، يُمكن للمُجمّع أن يفترض بأمان إمكانية تضمين الدوال والمتغيرات التي يُمكن حسابها في وقت الترجمة .

المتغيرات النهائية

لا يمكن تهيئة المتغير النهائي إلا مرة واحدة، إما عبر دالة تهيئة أو عبارة إسناد. لا يلزم تهيئته عند تعريفه، ويُسمى هذا النوع من المتغيرات "متغيرًا نهائيًا فارغًا". يجب إسناد قيمة لمتغير نهائي فارغ من نوع مثيل في كل مُنشئ للفئة التي تم تعريفه فيها؛ وبالمثل، يجب إسناد قيمة لمتغير نهائي ثابت فارغ في دالة تهيئة ثابتة للفئة التي تم تعريفه فيها؛ وإلا سيحدث خطأ أثناء الترجمة في كلتا الحالتين. [ 6 ] (ملاحظة: إذا كان المتغير مرجعًا، فهذا يعني أنه لا يمكن إعادة ربطه للإشارة إلى كائن آخر. لكن الكائن الذي يشير إليه يظل قابلًا للتغيير ، إذا كان قابلًا للتغيير في الأصل.)

على عكس قيمة الثابت ، فإن قيمة المتغير النهائي ليست معروفة بالضرورة وقت الترجمة. ومن الممارسات الجيدة تمثيل الثوابت النهائية بأحرف كبيرة، مع استخدام الشرطة السفلية للفصل بين الكلمات. [ 7 ]

مثال:

public class Sphere { // باي ثابت عالمي، وهو ثابت تقريبًا بقدر ما يمكن أن يكون أي شيء. public static final double PI = 3.141592653589793 ;public final double radius ; public final double xPos ; public final double yPos ; public final double zPos ;Sphere ( double x , double y , double z , double r ) { radius = r ; xPos = x ; yPos = y ; zPos = z ; }// ... }

أي محاولة لإعادة تعيين قيمة radiusالمتغير النهائي (final xPos) yPosستؤدي zPosإلى خطأ في الترجمة. في الواقع، حتى لو لم يقم المُنشئ بتعيين قيمة نهائية، فإن محاولة تعيينها خارج المُنشئ ستؤدي إلى خطأ في الترجمة .

ولتوضيح أن الحسم لا يضمن عدم التغيير: لنفترض أننا استبدلنا متغيرات الموضع الثلاثة بمتغير واحد:

public final Position pos ;

حيث posيكون لدينا كائن بثلاث خصائص pos.x، pos.yو و pos.z. posلا يمكن إسناد قيمة إلى ، ولكن يمكن إسناد الخصائص الثلاث، إلا إذا كانت نهائية بحد ذاتها.

كما هو الحال مع عدم قابلية التغيير التامة ، فإن استخدام المتغيرات النهائية له مزايا كبيرة، خاصة في تحسين الأداء. على سبيل المثال، Sphereمن المحتمل أن تحتوي دالة ما على حجمها؛ ومعرفة أن نصف قطرها ثابت يسمح لنا بتخزين الحجم المحسوب مؤقتًا. إذا كان لدينا Sphereعدد قليل نسبيًا من هذه المتغيرات ونحتاج إلى أحجامها بشكل متكرر، فقد يكون التحسن في الأداء كبيرًا. إن جعل نصف قطر المجسم ثابتًا Spherefinalيُعلم المطورين والمترجمات بأن هذا النوع من التحسين ممكن في جميع التعليمات البرمجية التي تستخدم Sphereهذه المتغيرات.

على الرغم من أن ذلك يبدو مخالفاً finalللمبدأ، إلا أن البيان التالي هو بيان قانوني:

for ( final SomeObject obj : someList ) { // do something with obj }

بما أن objالمتغير يخرج عن نطاق التعريف مع كل تكرار للحلقة، فإنه يُعاد تعريفه في كل تكرار، مما يسمح objباستخدام الرمز نفسه (أي ) لتمثيل متغيرات متعددة. [ 8 ]

المتغيرات النهائية في الكائنات المتداخلة

يمكن استخدام المتغيرات النهائية لإنشاء هياكل شجرية من الكائنات غير القابلة للتغيير. بمجرد إنشائها، يُضمن عدم تغيير هذه الكائنات. ولتحقيق ذلك، يجب أن تحتوي الفئة غير القابلة للتغيير على حقول نهائية فقط، ويجب أن تكون أنواع هذه الحقول النهائية غير قابلة للتغيير. أنواع البيانات الأولية في جافا غير قابلة للتغيير، وكذلك السلاسل النصية والعديد من الفئات الأخرى.

إذا تم انتهاك البنية المذكورة أعلاه بوجود كائن غير ثابت في الشجرة، فإن التوقع لا يتحقق بأن أي شيء يمكن الوصول إليه عبر المتغير الأخير ثابت. على سبيل المثال، يُعرّف الكود التالي نظام إحداثيات يجب أن يكون أصله دائمًا عند(0،0){\displaystyle (0,0)}يتم تنفيذ الأصل باستخدام java.awt.Pointفئة، وتُعرّف هذه الفئة حقولها على أنها عامة وقابلة للتعديل. هذا يعني أنه حتى عند الوصول إلى الكائن originعبر مسار وصول يحتوي على متغيرات نهائية فقط، فإنه لا يزال من الممكن تعديل هذا الكائن، كما يوضح مثال الكود أدناه.

استيراد java.awt.Point ؛public class FinalDemo {static class CoordinateSystem { private final Point ORIGIN = new Point ( 0 , 0 );public Point getOrigin () { return ORIGIN ; } }public static void main ( String [] args ) { CoordinateSystem coordinateSystem = new CoordinateSystem ();coordinateSystem.getOrigin ( ) . x = 15 ;assert coordinateSystem.getOrigin () . getX ( ) == 0 ; } }

والسبب في ذلك هو أن تعريف متغير على أنه نهائي (final) يعني فقط أن هذا المتغير سيشير إلى نفس الكائن في أي وقت. مع ذلك، فإن الكائن الذي يشير إليه المتغير لا يتأثر بهذا المتغير النهائي. في المثال أعلاه، الأصل (origin)x{\displaystyle x}وy{\displaystyle y}يمكن تعديل الإحداثيات بحرية.

لتجنب هذا الوضع غير المرغوب فيه، يُشترط عادةً أن تكون جميع حقول الكائن غير القابل للتغيير نهائية، وأن تكون أنواع هذه الحقول نفسها غير قابلة للتغيير. وهذا يستبعد استخدام فئات معينة، مثل فئة `and`، في مثل هذه الكائنات غير القابلة للتغيير java.util.Date.java.awt.Point

الصفوف النهائية والداخلية

عند تعريف فئة داخلية مجهولة ضمن جسم دالة، finalتصبح جميع المتغيرات المُعلنة في نطاق تلك الدالة قابلة للوصول من داخل الفئة الداخلية. بالنسبة للقيم العددية، بمجرد إسناد قيمة للمتغير، finalلا يمكن تغيير قيمته. أما بالنسبة للقيم الكائنية، فلا يمكن تغيير مرجعها. يسمح هذا لمترجم جافا بـ"التقاط" قيمة المتغير أثناء التشغيل وتخزين نسخة منها كحقل في الفئة الداخلية. بمجرد انتهاء الدالة الخارجية وإزالة إطار مكدسها ، يختفي المتغير الأصلي، لكن النسخة الخاصة بالفئة الداخلية تبقى في ذاكرة الفئة نفسها.

package org.wikipedia.examples ;استيراد javax.swing.JButton ؛ استيراد javax.swing.JFrame ؛ استيراد javax.swing.SwingUtilities ؛public class FooGUI { public static void main ( String [] args ) { // تهيئة مكونات واجهة المستخدم الرسومية final JFrame jf = new JFrame ( "Hello world!" ); // يسمح بالوصول إلى jf من داخل الفئة jf . add ( new JButton ( "Click me" ));// تجميع العناصر وجعلها مرئية في سلسلة معالجة الأحداث SwingUtilities.invokeLater ( new Runnable () { @Override public void run () { jf.pack ( ); // سيؤدي هذا إلى خطأ في وقت الترجمة إذا لم يكن jf نهائيًا jf.setLocationRelativeTo ( null ) ; jf.setVisible ( true ) ; } } ) ; } }

نهاية فارغة

المتغير النهائي الفارغ ، الذي تم تقديمه في جافا 1.1، هو متغير نهائي لا يحتوي تعريفه على قيمة ابتدائية. [ 9 ] [ 10 ] قبل جافا 1.1، كان يُشترط وجود قيمة ابتدائية للمتغير النهائي. وبحسب تعريف "النهائي"، لا يمكن إسناد قيمة للمتغير النهائي الفارغ إلا مرة واحدة، أي يجب إلغاء إسناد القيمة إليه عند كل عملية إسناد. وللقيام بذلك، يُجري مُصرّف جافا تحليلًا للتدفق للتأكد من أنه، لكل عملية إسناد إلى متغير نهائي فارغ، يتم إلغاء إسناد القيمة إلى المتغير قبل الإسناد؛ وإلا سيحدث خطأ أثناء الترجمة. [ 11 ]

final boolean hasTwoDigits ; if ( number >= 10 && number < 100 ) { hasTwoDigits = true ; } if ( number > -100 && number <= -10 ) { // خطأ في الترجمة لأن المتغير النهائي قد يكون مُسندًا بالفعل. hasTwoDigits = true ; }

بالإضافة إلى ذلك، يجب تحديد ورقة الامتحان النهائي الفارغة بشكل نهائي قبل الوصول إليها. [ 11 ]

القيمة المنطقية النهائية isEven ;إذا كان ( العدد % 2 == 0 ) { isEven = true ; }// خطأ في الترجمة لأن المتغير لم يتم تعيينه في حالة else. System.out.println ( isEven ) ;

مع ذلك، تجدر الإشارة إلى أنه يجب أيضاً تعيين قيمة نهائية للمتغير المحلي قبل الوصول إليه. [ 11 ]

boolean isEven ; // ليس نهائيًاإذا كان ( العدد % 2 == 0 ) { isEven = true ; }// نفس خطأ الترجمة لأن المتغير غير النهائي لم يتم تعيينه في حالة else. System.out.println ( isEven ) ;

انعكاس

على الرغم من أن الحقول النهائية المُهيأة مصممة لتكون غير قابلة للتغيير، إلا أنه يمكن تغييرها فعليًا باستخدام تقنية الانعكاس. java.lang.reflectتحتوي الحزمة على دوال تُتيح الوصول إلى أي حقل وتغييره. اعتبارًا من عام 2026، توجد خطط لإزالة هذه الخاصية في إصدار مستقبلي من جافا. [ 12 ]

نظير المتغيرات النهائية في لغة C/C++

في لغتي C و C++ ، يُستخدم مصطلح `const` للدلالة على البنية المُماثلة . يختلف هذا constالمصطلح اختلافًا جوهريًا عن نظيره finalفي Java، ويكمن جوهره في كونه مُحددًا للنوع : constفهو جزء من النوع ، وليس جزءًا من المُعرّف (المتغير) فقط. هذا يعني أيضًا أنه يُمكن تغيير ثبات القيمة عن طريق التحويل الصريح للنوع ، والذي يُعرف في هذه الحالة باسم "تحويل const". مع ذلك، فإن إزالة خاصية الثبات ثم تعديل الكائن يؤدي إلى سلوك غير مُحدد إذا كان الكائن مُعلنًا عنه في الأصل كـ `final` const. يُعدّ `final` في Java finalقاعدة صارمة بحيث يستحيل تجميع التعليمات البرمجية التي تُخالف أو تتجاوز قيود `final` بشكل مباشر. باستخدام الانعكاس ، يُمكن غالبًا تعديل المتغيرات `final`. تُستخدم هذه الميزة بشكل أساسي عند إلغاء تسلسل الكائنات التي تحتوي على أعضاء `final`.

علاوة على ذلك، ولأن لغتي C وC++ تُعرّفان المؤشرات والمراجع بشكل مباشر، فهناك فرق بين ما إذا كان المؤشر نفسه ثابتًا، وما إذا كانت البيانات التي يُشير إليها المؤشر ثابتة. تطبيق `const` constعلى المؤشر نفسه، كما في المثال `const `index.const` SomeClass* const ptr، يعني أنه يُمكن تعديل المحتوى المُشار إليه، ولكن لا يُمكن تعديل المرجع نفسه (بدون تحويل نوع البيانات). ينتج عن هذا الاستخدام سلوك يُحاكي سلوك finalمرجع المتغير في Java. في المقابل، عند تطبيق `const` على البيانات المُشار إليها فقط، كما في المثال `const `index.const` const SomeClass* ptr، لا يُمكن تعديل المحتوى (بدون تحويل نوع البيانات)، ولكن يُمكن تعديل المرجع نفسه. يُمكن تعريف كل من المرجع والمحتوى المُشار إليه على أنهما ثابتان const.

في لغة C++، finalتُستخدم الكلمة المفتاحية للدلالة على أنه لا يمكن إعادة تعريف دالة ما. كما تُستخدم بشكل مشابه للغة Java لتعريف فئة على أنها نهائية (لا يمكن توسيعها).

// يُشير استخدام final في تعريف الصنف إلى أنه لا يمكن توسيع الصنف class Z final : public X , public Y { public : // يُشير استخدام final في توقيع الدالة إلى أنه لا يمكن إعادة تعريف الدالة void someOperation () override final { // نفّذ شيئًا ما هنا } };

نظائر C# للكلمة المفتاحية النهائية

يمكن اعتبار لغة C# مشابهة للغة Java من حيث ميزات اللغة وبنية الجملة الأساسية: Java لديها JVM، وC# لديها .Net Framework ؛ Java لديها بايت كود ، وC# لديها CIL ؛ Java ليس لديها دعم للمؤشرات (الذاكرة الحقيقية)، وC# هي نفسها.

فيما يتعلق بالكلمة المفتاحية الأخيرة، تحتوي لغة C# على كلمتين مفتاحيتين مرتبطتين:

  1. الكلمة المفتاحية المكافئة للطرق والفئات هيsealed
  2. الكلمة المفتاحية المكافئة للمتغيرات هي readonly[ 13 ]

لاحظ أن الفرق الرئيسي بين الكلمة المفتاحية المشتقة في C/C++ constوالكلمة المفتاحية في C# readonlyهو أن constيتم تقييمها في وقت الترجمة، بينما readonlyيتم تقييمها في وقت التشغيل، وبالتالي يمكن أن يكون لها تعبير يتم حسابه وتثبيته لاحقًا (في وقت التشغيل).

انظر أيضاً

مراجع

  1. كوبلنز، مايكل؛ صن شاين، جوشوا؛ ألدريتش، جوناثان؛ مايرز، براد؛ ويبر، سام؛ شول، فورست (14-22 مايو 2016). "استكشاف دعم اللغة لعدم قابلية التغيير". المؤتمر الدولي الثامن والثلاثون لهندسة البرمجيات .
  2. مواصفات لغة جافا4.12.4
  3. "الفصل 8. الفئات" . docs.oracle.com . تم الاطلاع عليه بتاريخ 25-04-2024 .
  4. "كتابة الفئات والأساليب النهائية" . docs.oracle.com . تم الاطلاع عليه بتاريخ 25-04-2024 .
  5. "نظرية جافا وتطبيقها: هل هذه هي إجابتك النهائية؟" . developer.ibm.com . مؤرشف من الأصل بتاريخ 8 فبراير 2009. تم الاطلاع عليه بتاريخ 25 أبريل 2024 .
  6. مواصفات لغة جافا #8.3.1.2.
  7. "إرشادات أسلوب برمجة جافا" . petroware.no . تم ​​الاطلاع عليه بتاريخ 25-04-2024 .
  8. باتيس، ريتشارد إي. "المزيد من جافا" . البرمجة المتقدمة/التطبيق العملي 15-200 . كلية علوم الحاسوب، جامعة كارنيجي ميلون . تم الاطلاع عليه بتاريخ 23 يوليو 2010 .
  9. فلاناغان، ديفيد (مايو 1997). "الفصل 5: الفئات الداخلية وميزات لغوية جديدة أخرى: 5.6 ميزات جديدة أخرى في جافا 1.1". جافا باختصار ( الطبعة الثانية). أورايلي. ISBN  1-56592-262-X.
  10. "الفصل 4. الأنواع والقيم والمتغيرات" . مواصفات لغة جافا® (إصدار جافا SE 8) . شركة أوراكل أمريكا، 2015. تم الاطلاع عليه بتاريخ 23 فبراير 2015 .
  11. 1 2 3 "التخصيص المحدد" . مواصفات لغة جافا® (إصدار جافا SE 8) . شركة أوراكل أمريكا، 2015. تم الاطلاع عليه بتاريخ 29 أكتوبر 2016 .
  12. "JEP 500: الاستعداد لجعل النهائي نهائيًا" . openjdk.org . تم الاطلاع عليه بتاريخ 12 يناير 2026 .
  13. ما هو المكافئ لـ final في لغة جافا في لغة سي شارب؟