مرحبا يا هبر! أقدم إليكم ترجمة مقالة "البرمجة الشيئية في Python vs Java" للمخرج جون فينشر.
يختلف تنفيذ البرمجة الموجهة للكائنات (OOP) في Java و Python. يمكن أن يسبب مبدأ العمل مع الكائنات وأنواع المتغيرات والقدرات اللغوية الأخرى صعوبة في التبديل من لغة إلى أخرى. هذه المقالة ، التي يمكن أن تكون مفيدة لكل من مبرمجي Java الذين يرغبون في تعلم Python ، ولمبرمجي Python الذين يرغبون في تعلم Java بشكل أفضل ، تعطي أوجه التشابه والاختلاف الرئيسية بين هذه اللغات فيما يتعلق بـ OOP.
مزيد من التفاصيل - تحت الخفض.
أمثلة فئة في بيثون وجافا
للبدء ، دعونا ننفذ أبسط فصل في Python و Java لتوضيح بعض الاختلافات في هذه اللغات ، وسنقوم تدريجياً بإجراء تغييرات على هذه الفئة.
تخيل أن لدينا التعريف التالي لفئة Car في Java:
1 public class Car { 2 private String color; 3 private String model; 4 private int year; 5 6 public Car(String color, String model, int year) { 7 this.color = color; 8 this.model = model; 9 this.year = year; 10 } 11 12 public String getColor() { 13 return color; 14 } 15 16 public String getModel() { 17 return model; 18 } 19 20 public int getYear() { 21 return year; 22 } 23 }
يجب أن يتطابق اسم ملف Java المصدر مع اسم الفئة المخزنة فيه ، لذلك يجب أن نسمي الملف Car.java. يمكن أن يحتوي كل ملف Java على فئة عامة واحدة فقط.
نفس الفئة في بيثون ستبدو هكذا:
1 class Car: 2 def __init__(self, color, model, year): 3 self.color = color 4 self.model = model 5 self.year = year
في Python ، يمكنك إعلان فصل في أي مكان وفي أي وقت. احفظ هذا الملف كـ car.py.
باستخدام هذه الفئات كأساس ، نواصل دراسة المكونات الرئيسية للفئات والكائنات.
سمات الكائن
في جميع اللغات الموجهة للكائنات ، يتم تخزين البيانات المتعلقة بالكائن في مكان ما. في كل من Python و Java ، يتم تخزين هذه البيانات في سمات ، وهي متغيرات مرتبطة بكائنات محددة.
أحد أهم الاختلافات بين Python و Java هو كيفية تحديد سمات الصف والكائن وكيفية التحكم في هذه اللغات. بعض هذه الاختلافات ناتجة عن قيود تفرضها اللغات ، بينما يرتبط بعضها الآخر بممارسة أكثر فعالية.
الإعلان والتهيئة
في Java ، نعلن عن سمات (تشير إلى نوعها) داخل الفصل ، ولكن خارج جميع الطرق. قبل استخدام سمات الفصل الدراسي ، يجب أن نحددها:
1 public class Car { 2 private String color; 3 private String model; 4 private int year;
في Python ، نعلن ونعرّف السمات داخل طريقة فئة init () ، وهي تناظرية للمنشئ في Java:
1 def __init__(self, color, model, year): 2 self.color = color 3 self.model = model 4 self.year = year
من خلال تحديد الذات للكلمة الرئيسية أمام اسم المتغير ، فإننا نعلم بيثون أن هذه سمات. كل مثيل للفصل يحصل على نسخته. جميع المتغيرات في بيثون ليست مكتوبة بشكل فضفاض ، والسمات ليست استثناء.
يمكن أيضًا إنشاء المتغيرات خارج طريقة init () ، لكن هذا لن يكون الحل الأفضل وقد يؤدي إلى أخطاء يصعب اكتشافها. على سبيل المثال ، يمكنك إضافة سمة عجلات جديدة إلى كائن Car كما يلي:
1 >>> import car 2 >>> my_car = car.Car("yellow", "beetle", 1967) 3 >>> print(f"My car is {my_car.color}") 4 My car is yellow 5 6 >>> my_car.wheels = 5 7 >>> print(f"Wheels: {my_car.wheels}") 8 Wheels: 5
ومع ذلك ، إذا نسينا تحديد التعبير my_car.wheels = 5 في السطر السادس ، فسيظهر لنا خطأ:
1 >>> import car 2 >>> my_car = car.Car("yellow", "beetle", 1967) 3 >>> print(f"My car is {my_car.color}") 4 My car is yellow 5 6 >>> print(f"Wheels: {my_car.wheels}") 7 Traceback (most recent call last): 8 File "<stdin>", line 1, in <module> 9 AttributeError: 'Car' object has no attribute 'wheels'
في Python ، إذا قمت بتعريف متغير خارج الطريقة ، فسيتم اعتباره كمتغير فئة. دعنا نغير فئة السيارات:
1 class Car: 2 3 wheels = 0 4 5 def __init__(self, color, model, year): 6 self.color = color 7 self.model = model 8 self.year = year
الآن سوف يتغير استخدام العجلات المتغيرة. بدلاً من الوصول إليه من خلال كائن ، يمكننا الوصول إليه باستخدام اسم الفئة:
1 >>> import car 2 >>> my_car = car.Car("yellow", "beetle", 1967) 3 >>> print(f"My car is {my_car.color}") 4 My car is yellow 5 6 >>> print(f"It has {car.Car.wheels} wheels") 7 It has 0 wheels 8 9 >>> print(f"It has {my_car.wheels} wheels") 10 It has 0 wheels
ملاحظة: في Python ، يتم الوصول إلى متغير الفئة باستخدام بناء الجملة التالي:
- اسم الملف الذي يحتوي على الفئة (بدون ملحق .py)
- نقطة
- اسم الصف
- نقطة
- اسم متغير
نظرًا لأننا قمنا بحفظ فئة السيارة في ملف car.py ، فإننا نشير إلى متغير فئة العجلات في السطر السادس بهذه الطريقة: car.Car.wheels.
عند التعامل مع متغير العجلات ، يجب عليك الانتباه إلى أن تغيير قيمة متغير مثيل لفئة my_car.wheels لا يؤدي إلى تغيير متغير فئة car.Car.wheels:
1 >>> from car import * 2 >>> my_car = car.Car("yellow", "Beetle", "1966") 3 >>> my_other_car = car.Car("red", "corvette", "1999") 4 5 >>> print(f"My car is {my_car.color}") 6 My car is yellow 7 >>> print(f"It has {my_car.wheels} wheels") 8 It has 0 wheels 9 10 >>> print(f"My other car is {my_other_car.color}") 11 My other car is red 12 >>> print(f"It has {my_other_car.wheels} wheels") 13 It has 0 wheels 14 15 >>>
على الخطين 2 و 3 ، قمنا بتعريف كائنين في السيارة: my_car و my_other_car.
أولاً ، خاصية عجلات كلا الكائنين هي صفر. على السطر السادس عشر ، قمنا بتعيين متغير الفئة: car.Car.wheels = 4 ، أصبح لكلا الكائنين الآن 4 عجلات. ومع ذلك ، عندما نغير خاصية الكائن my_car.wheels = 5 في الصف 24 ، تظل خاصية الكائن الثاني دون تغيير.
هذا يعني أن لدينا الآن نسختين مختلفتين من سمة العجلات:
- متغير فئة ينطبق على جميع الكائنات السيارة
- متغير مثيل فئة محدد ينطبق فقط على كائن my_car.
لهذا السبب ، يمكنك الرجوع عن طريق الخطأ إلى مثيل خاطئ وارتكاب خطأ خفي.
في Java ، يكون ما يعادل سمة class سمة ثابتة:
public class Car { private String color; private String model; private int year; private static int wheels; public Car(String color, String model, int year) { this.color = color; this.model = model; this.year = year; } public static int getWheels() { return wheels; } public static void setWheels(int count) { wheels = count; } }
عادةً ما نصل إلى المتغيرات الثابتة في Java من خلال اسم الفصل. يمكنك الوصول إليهم من خلال نسخة من الفصل ، كما هو الحال في بيثون ، ولكن هذا لن يكون الحل الأفضل.
بدأ فصل Java الخاص بنا في الإطالة. أحد الأسباب التي تجعل Java عبارة "verbose" لـ Python هي مفهوم الأساليب والسمات العامة والخاصة.
العامة والخاصة
تتحكم Java في الوصول إلى الأساليب والسمات من خلال التمييز بين البيانات العامة والخاصة .
في Java ، من المتوقع أن تكون السمات خاصة (أو محمية ، إذا احتاج أحفاد الفصل إلى الوصول إليها). وبالتالي ، فإننا نقيد الوصول إليهم من الخارج. لتوفير الوصول إلى السمات الخاصة ، نعلن عن الأساليب العامة التي تقوم بتثبيت أو استقبال هذه البيانات (المزيد حول هذا لاحقًا).
تذكر أنه في شفرة Java الخاصة بنا ، تم إعلان أن متغير اللون خاص. لذلك ، لن يتم تجميع التعليمات البرمجية أدناه:
Car myCar = new Car("blue", "Ford", 1972);
إذا لم تحدد مستوى الوصول إلى السمات ، فسيتم تعيينها افتراضيًا على أنها حزمة محمية ، مما يحد من الوصول إلى الفئات داخل الحزمة. إذا كنا نريد أن يعمل الرمز أعلاه ، فسيتعين علينا جعل السمة عامة.
ومع ذلك ، لا يتم تشجيع Java على الإعلان عن السمات العامة. يوصى بتعريفهم بالخصوصية ، ثم استخدام الطرق العامة مثل getColor () و getModel () ، كما هو مذكور في الكود أعلاه.
في المقابل ، تفتقر Python إلى فكرة البيانات العامة والخاصة. في بيثون ، كل شيء عام. ستعمل شفرة الثعبان هذه مع اثارة ضجة:
>>> my_car = car.Car("blue", "Ford", 1972) >>>
بدلاً من المتغيرات الخاصة في Python ، هناك مفهوم متغيرات مثيل فئة غير عامة ( غير عامة ). جميع المتغيرات التي تبدأ أسماؤها بشرطة سفلية واحدة تعتبر غير عامة. لا يمنعنا اصطلاح التسمية هذا من الوصول إلى المتغير مباشرةً.
أضف السطر التالي إلى فئة بيثون كار:
class Car: wheels = 0 def __init__(self, color, model, year): self.color = color self.model = model self.year = year self._cupholders = 6
يمكننا الوصول إلى المتغير _cupholder مباشرة:
>>> import car >>> my_car = car.Car("yellow", "Beetle", "1969") >>> print(f"It was built in {my_car.year}") It was built in 1969 >>> my_car.year = 1966 >>> print(f"It was built in {my_car.year}") It was built in 1966 >>> print(f"It has {my_car._cupholders} cupholders.") It has 6 cupholders.
يسمح لك Python بالوصول إلى هذا المتغير ، ومع ذلك ، فإن بعض بيئات التطوير مثل VS Code ستوجه تحذيرًا:

بالإضافة إلى ذلك ، يستخدم Python تسطير سفلي مزدوج في بداية اسم متغير لإخفاء سمة. عندما ترى Python مثل هذا المتغير ، فإنها تقوم تلقائيًا بتغيير اسمه لجعل الوصول المباشر صعبًا. ومع ذلك ، لا تزال هذه الآلية لا تمنعنا من اللجوء إليها. نوضح هذا مع المثال التالي:
class Car: wheels = 0 def __init__(self, color, model, year): self.color = color self.model = model self.year = year self.__cupholders = 6
الآن إذا نظرنا إلى متغير __cupholders ، فقد حصلنا على خطأ:
>>> import car >>> my_car = car.Car("yellow", "Beetle", "1969") >>> print(f"It was built in {my_car.year}") It was built in 1969 >>> my_car.year = 1966 >>> print(f"It was built in {my_car.year}") It was built in 1966 >>> print(f"It has {my_car.__cupholders} cupholders.") Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Car' object has no attribute '__cupholders'
فلماذا لا توجد سمة __cupholders؟
هذا هو الشيء. عندما ترى Python سمة مزدوجة أسفل السطر في البداية ، فإنها تغيرها بإضافة اسم فئة مسطر إلى البداية. للوصول إلى السمة مباشرةً ، يجب عليك أيضًا تغيير الاسم:
>>> print(f"It has {my_car._Car__cupholders} cupholders") It has 6 cupholders
الآن يطرح السؤال التالي: إذا تم إعلان أن سمة فئة Java خاصة وكانت سمة فئة بيثون مسبوقة بخط سفلي مزدوج أسفل الاسم ، فكيف يمكن الوصول إلى هذه البيانات؟
التحكم في الوصول
في Java ، نصل إلى السمات الخاصة باستخدام أدوات التثبيت والحروف . لكي يتمكن المستخدم من إعادة رسم الجهاز الخاص به ، أضف الجزء التالي من التعليمات البرمجية إلى فئة Java:
public String getColor() { return color; } public void setColor(String color) { this.color = color; }
نظرًا لأن أساليب getColor () و setColor () عامة ، يمكن لأي مستخدم الاتصال بهم والحصول على / تغيير لون الجهاز. يعد استخدام السمات الخاصة ، التي نصل إليها على المستوطنين والمستوطنين ، أحد أسباب "جدية" جافا أكبر مقارنة ببيثون.
كما هو موضح أعلاه ، في Python يمكننا الوصول إلى السمات مباشرة. نظرًا لأن كل شيء عام ، يمكننا الوصول إلى أي شيء ، في أي وقت وفي أي مكان. يمكننا الحصول على قيم السمات وتعيينها مباشرةً عن طريق الاتصال بأسمائهم. في Python ، يمكننا حتى إزالة السمات ، وهو أمر لا يمكن تصوره في Java:
>>> my_car = Car("yellow", "beetle", 1969) >>> print(f"My car was built in {my_car.year}") My car was built in 1969 >>> my_car.year = 1966 >>> print(f"It was built in {my_car.year}") It was built in 1966 >>> del my_car.year >>> print(f"It was built in {my_car.year}") Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Car' object has no attribute 'year'
ومع ذلك ، يحدث أيضًا أننا نريد التحكم في الوصول إلى السمات. في هذه الحالة ، خصائص بيثون تأتي لمساعدتنا.
في Python ، توفر الخصائص وصولاً محكمًا إلى سمات الفصل باستخدام أدوات الديكور. باستخدام الخصائص ، نعلن عن وظائف في فئات Python مثل getters و setters في Java (إزالة السمات هي مكافأة).
يمكن رؤية تشغيل الخصائص في المثال التالي لفئة السيارة:
1 class Car: 2 def __init__(self, color, model, year): 3 self.color = color 4 self.model = model 5 self.year = year 6 self._voltage = 12 7 8 @property 9 def voltage(self): 10 return self._voltage 11 12 @voltage.setter 13 def voltage(self, volts): 14 print("Warning: this can cause problems!") 15 self._voltage = volts 16 17 @voltage.deleter 18 def voltage(self): 19 print("Warning: the radio will stop working!") 20 del self._voltage
في هذا المثال ، نوسع مفهوم فئة السيارات لتشمل السيارات الكهربائية. يعلن السطر 6 عن السمة _voltage لتخزين جهد البطارية فيها.
في السطور 9 و 10 للوصول المتحكم به ، نقوم بإنشاء دالة الجهد () ونعيد قيمة المتغير الخاص. باستخدامproperty decorator ، نقوم بتحويلها إلى getter ، والتي يمكن لأي مستخدم الوصول إليها الآن.
في الأسطر 13-15 ، نحدد وظيفة تسمى أيضًا الجهد (). ومع ذلك ، نحن تزيينه بشكل مختلف: الجهد . أخيرًا ، في الأسطر 18-20 ، نقوم بتزيين وظيفة الجهد () بجهد. مقياس ، ويمكن إزالة سمة _voltage إذا لزم الأمر.
الوظائف المزخرفة لها نفس الاسم ، مما يشير إلى أنها تتحكم في الوصول إلى نفس السمة. تصبح أسماء الوظائف هذه أيضًا أسماء السمات المستخدمة للحصول على قيمها. إليك كيف تعمل:
1 >>> from car import * 2 >>> my_car = Car("yellow", "beetle", 1969) 3 4 >>> print(f"My car uses {my_car.voltage} volts") 5 My car uses 12 volts 6 7 >>> my_car.voltage = 6 8 Warning: this can cause problems! 9 10 >>> print(f"My car now uses {my_car.voltage} volts") 11 My car now uses 6 volts 12 13 >>> del my_car.voltage 14 Warning: the radio will stop working!
يرجى ملاحظة أننا نستخدم الجهد ، وليس _ الجهد. لذلك نطلب من بيثون تطبيق الخصائص التي حددتها للتو:
- عندما نقوم بطباعة القيمة my_car.voltage في السطر الرابع ، فإن Python تستدعي الدالة voltage () المزينة بـproperty.
- عندما نقوم بتعيين القيمة my_car.voltage في السطر السابع ، تقوم Python باستدعاء دالة الجهد () المزينة بجهد ضبط الجهد .
- عندما نقوم بحذف my_car.voltage في السطر 13 ، تقوم Python باستدعاء دالة الجهد () ، المزينة بجهد .deleter.
يعطينا الديكور أعلاه القدرة على التحكم في الوصول إلى السمات دون استخدام أساليب مختلفة. يمكنك حتى جعل السمة خاصية للقراءة فقط عن طريق إزالة دالات @. setter و @ .deleter.
النفس وهذا
في Java ، يشير الفصل إلى نفسه باستخدام هذه الكلمة الأساسية:
public void setColor(String color) { this.color = color; }
هذا ضمني في كود جافا. من حيث المبدأ ، ليس من الضروري كتابته ، إلا عندما تتزامن أسماء المتغيرات.
يمكن كتابة Setter مثل هذا:
public void setColor(String newColor) { color = newColor; }
نظرًا لأن فئة السيارة تحتوي على سمة تسمى اللون ولا توجد متغيرات أخرى لها نفس الاسم في النطاق ، يتم تشغيل ارتباط بهذا الاسم. استخدمنا هذه الكلمة الرئيسية في المثال الأول للتمييز بين سمة ومعلمة بنفس اسم اللون.
في Python ، تخدم الكلمة الرئيسية الذاتية غرضًا مشابهًا: الوصول إلى أعضاء السمات ، ولكن على عكس Java ، فهي مطلوبة :
class Car: def __init__(self, color, model, year): self.color = color self.model = model self.year = year self._voltage = 12 @property def voltage(self): return self._voltage
بيثون يتطلب أن يكون النفس إلزاميا. كل الذات تخلق أو تشير إلى سمة. إذا تخطينا ذلك ، فسيقوم Python ببساطة بإنشاء متغير محلي بدلاً من سمة.
يرجع الاختلاف في الطريقة التي نستخدم بها الذات وهذا في Python و Java إلى الاختلافات الرئيسية بين اللغتين وطريقة تسمية المتغيرات والسمات.
الطرق والوظائف
الفرق بين اللغات المعنية هو أن في بيثون وظائف ، لكن في جاوة ليست كذلك.
في Python ، ستعمل التعليمة البرمجية التالية دون مشاكل (ويتم استخدامها في كل مكان):
>>> def say_hi(): ... print("Hi!") ... >>> say_hi() Hi!
يمكننا استدعاء say_hi () من أي مكان في الرؤية. لا تحتوي هذه الوظيفة على إشارة إلى الذات ، مما يعني أنها وظيفة عالمية ، وليست وظيفة صنفية. لن تكون قادرة على تغيير أو حفظ أي بيانات من أي فئة ، ولكن يمكنها استخدام المتغيرات المحلية والعالمية.
في المقابل ، كل سطر نكتبه في Java ينتمي إلى فصل دراسي. لا توجد وظيفة خارج الفصل ، وبحكم التعريف ، فإن جميع وظائف Java هي طرق. في Java ، أقرب شيء إلى دالة محض هو طريقة ثابتة:
public class Utils { static void SayHi() { System.out.println("Hi!"); } }
تيلس. يتم استدعاء SayHi () من أي مكان دون إنشاء مثيل لفئة Utils أولاً. بما أننا ندعو SayHi () دون إنشاء كائن ، فهذا الرابط غير موجود. ومع ذلك ، لا تزال هذه ليست وظيفة بمعنى أن say_hi () في بيثون.
الوراثة وتعدد الأشكال
الوراثة وتعدد الأشكال هما مفهومان أساسيان في OOP. بفضل الأول ، تتلقى الكائنات (بمعنى آخر ، ترث) سمات ووظائف الكائنات الأخرى ، مما يؤدي إلى إنشاء تسلسل هرمي من كائنات عامة إلى كائنات أكثر تحديداً. على سبيل المثال ، كلا من فئة السيارات وفئة القوارب هما نوعان محددان من فئة السيارة. كلا الكائنين يرثان سلوك أحد الوالدين أو كائنات أصل متعددة. في هذه الحالة ، يطلق عليهم كائنات تابعة.
تعدد الأشكال ، بدوره ، هو القدرة على العمل مع أشياء مختلفة باستخدام نفس الوظيفة أو الطريقة.
يتم تطبيق كل من مفاهيم OOP الأساسية هذه في Java و Python بطرق مختلفة تمامًا.
ميراث
تدعم بايثون تركة متعددة ، أي إنشاء فصل من أكثر من والد.
لإثبات ذلك ، نقسم فئة السيارة إلى فئتين: واحدة للمركبات وواحدة للسيارات التي تستخدم الكهرباء:
class Vehicle: def __init__(self, color, model): self.color = color self.model = model class Device: def __init__(self): self._voltage = 12 class Car(Vehicle, Device): def __init__(self, color, model, year): Vehicle.__init__(self, color, model) Device.__init__(self) self.year = year @property def voltage(self): return self._voltage @voltage.setter def voltage(self, volts): print("Warning: this can cause problems!") self._voltage = volts @voltage.deleter def voltage(self): print("Warning: the radio will stop working!") del self._voltage
تحدد فئة المركبة سمات اللون والطراز. فئة الجهاز تحتوي على السمة _voltage. فئة السيارة مشتقة من هاتين الفئتين ، وأصبحت سمات اللون والطراز والجهد الآن جزءًا من الفئة الجديدة.
تستدعي طريقة init () الخاصة بفئة السيارة أساليب () (كلا الصفوف الرئيسية للتأكد من تهيئة جميع البيانات بشكل صحيح. بعد ذلك ، يمكننا إضافة أي وظيفة مطلوبة إلى فئة السيارة. في هذا المثال ، سنضيف سمة السنة ، وكذلك getter و setter _voltage.
تبقى وظيفة فئة السيارات الجديدة كما هي. يمكننا إنشاء واستخدام كائنات فئة ، كما فعلنا مع بعض الأمثلة السابقة:
>>> from car import * >>> my_car = Car("yellow", "beetle", 1969) >>> print(f"My car is {my_car.color}") My car is yellow >>> print(f"My car uses {my_car.voltage} volts") My car uses 12 volts >>> my_car.voltage = 6 Warning: this can cause problems! >>> print(f"My car now uses {my_car.voltage} volts") My car now uses 6 volts
لغة Java ، بدورها ، تدعم الوراثة الفردية فقط ، مما يعني أن الفئات في Java يمكنها أن ترث البيانات والسلوك من فئة أصل واحدة فقط. ولكن في Java ، يمكن التوريث من واجهات متعددة. توفر الواجهات مجموعة من الأساليب ذات الصلة التي يجب تنفيذها ، مما يسمح لفصول الأطفال بالتصرف بطريقة مماثلة.
لرؤية هذا ، قمنا بتقسيم فئة Car Java إلى صفها وواجهة الأم:
public class Vehicle { private String color; private String model; public Vehicle(String color, String model) { this.color = color; this.model = model; } public String getColor() { return color; } public String getModel() { return model; } } public interface Device { int getVoltage(); } public class Car extends Vehicle implements Device { private int voltage; private int year; public Car(String color, String model, int year) { super(color, model); this.year = year; this.voltage = 12; } @Override public int getVoltage() { return voltage; } public int getYear() { return year; } }
لا تنس أن كل فئة وكل واجهة في Java يجب أن توضع في ملفها الخاص.
كما في المثال أعلاه مع Python ، نقوم بإنشاء فئة مركبة جديدة لتخزين البيانات والوظائف الشائعة الملازمة للمركبات. ومع ذلك ، لإضافة وظيفة الجهاز ، نحتاج إلى إنشاء واجهة تحدد طريقة الحصول على جهد الجهاز.
يتم إنشاء فئة السيارة بالوراثة من فئة السيارة باستخدام الكلمة الأساسية الممتدة وتطبيق واجهة الجهاز باستخدام الكلمة الأساسية للتطبيق. في مُنشئ الفصل ، نسمي المُنشئ الأصل بـ super (). نظرًا لوجود فئة واحدة من الوالدين ، فإننا نشير إلى مُنشئ فئة السيارة. لتطبيق الواجهة ، نعيد تعريف getVoltage () باستخدام التعليق التوضيحي Override .
بدلاً من إعادة استخدام الشفرة من Device ، كما يفعل Python ، تتطلب Java أن ننفذ نفس الوظيفة في كل فئة تنفذ الواجهة. تحدد الواجهات طرقًا فقط - لا يمكنها تحديد بيانات مثيل الفصل أو تفاصيل التطبيق.
Java? .
Java . , Java- , , . , .
Java- charge(), Device. , Device, charge().
Rhino.java:
public class Rhino { }
Main.java charge() , Car Rhino.
public class Main{ public static void charge(Device device) { device.getVoltage(); } public static void main(String[] args) throws Exception { Car car = new Car("yellow", "beetle", 1969); Rhino rhino = new Rhino(); charge(car); charge(rhino); } }
, : Information:2019-02-02 15:20 - Compilation completed with 1 error and 0 warnings in 4 s 395 ms Main.java Error:(43, 11) java: incompatible types: Rhino cannot be converted to Device
Rhino Device, charge().
( — strict variable typing, , Python ) , Java, Python , : « , » ( : " , , , , " – . ). , Python .
Python:
>>> def charge(device): ... if hasattr(device, '_voltage'): ... print(f"Charging a {device._voltage} volt device") ... else: ... print(f"I can't charge a {device.__class__.__name__}") ... >>> class Phone(Device): ... pass ... >>> class Rhino: ... pass ... >>> my_car = Car("yellow", "Beetle", "1966") >>> my_phone = Phone() >>> my_rhino = Rhino() >>> charge(my_car) Charging a 12 volt device >>> charge(my_phone) Charging a 12 volt device >>> charge(my_rhino) I can't charge a Rhino
charge() _voltage. Device , - (Car Phone) , , , . , Device ( Rhino), , , , (rhino) .
Java Object, . , . Object :
class Object { boolean equals(Object obj) { ... } int hashCode() { ... } String toString() { ... } }
equals() , , hashCode() , . Java . , , , .
toString() . . , , , , System.out.println():
Car car = new Car("yellow", "Beetle", 1969); System.out.println(car);
car:
Car@61bbe9ba
, ? , toString(). Car:
public String toString() { return "Car: " + getColor() + " : " + getModel() + " : " + getYear(); }
, , :
Car: yellow : Beetle : 1969
Python (dunder — double underscore). Python- , , , .
Python : repr () str (). repr (), str () . hashcode() toString() Java.
Java, Python :
>>> my_car = Car("yellow", "Beetle", "1966") >>> print(repr(my_car)) <car.Car object at 0x7fe4ca154f98> >>> print(str(my_car)) <car.Car object at 0x7fe4ca154f98>
, str () Python- Car:
def __str__(self): return f'Car {self.color} : {self.model} : {self.year}'
:
>>> my_car = Car("yellow", "Beetle", "1966") >>> print(repr(my_car)) <car.Car object at 0x7f09e9a7b630> >>> print(str(my_car)) Car yellow : Beetle : 1966
. repr (), .
Python , , , .
Python . Python , Java .
Python- Car :
class Car: def __init__(self, color, model, year): self.color = color self.model = model self.year = year def __str__(self): return f'Car {self.color} : {self.model} : {self.year}' def __eq__(self, other): return self.year == other.year def __lt__(self, other): return self.year < other.year def __add__(self, other): return Car(self.color + other.color, self.model + other.model, int(self.year) + int(other.year))
, :
Python , , , .
Car:
>>> my_car = Car("yellow", "Beetle", "1966") >>> your_car = Car("red", "Corvette", "1967") >>> print (my_car < your_car) True >>> print (my_car > your_car) False >>> print (my_car == your_car) False >>> print (my_car + your_car) Car yellowred : BeetleCorvette : 3933
, , , Java.
– . Java, Python .
. Python type() isinstance () , :
>>> my_car = Car("yellow", "Beetle", "1966") >>> print(type(my_car)) <class 'car.Car'> >>> print(isinstance(my_car, Car)) True >>> print(isinstance(my_car, Device)) True
Java getClass() instanceof :
Car car = new Car("yellow", "beetle", 1969); System.out.println(car.getClass()); System.out.println(car instanceof Car);
:
class com.realpython.Car true
Python dir() , ( ). , getattr():
>>> print(dir(my_car)) ['_Car__cupholders', '__add__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_voltage', 'color', 'model', 'voltage', 'wheels', 'year'] >>> print(getattr(my_car, "__format__")) <built-in method __format__ of Car object at 0x7fb4c10f5438>
Java , , , .
getFields() . , Car , :
Field[] fields = car.getClass().getFields();
Java , getDeclaredMethods(). get-, , , :
1) getDeclaredMethods()
2) :
:
1 public static boolean getProperty(String name, Object object) throws Exception { 2 3 Method[] declaredMethods = object.getClass().getDeclaredMethods(); 4 for (Method method : declaredMethods) { 5 if (isGetter(method) && 6 method.getName().toUpperCase().contains(name.toUpperCase())) { 7 return true; 8 } 9 } 10 return false; 11 } 12 13
getProperty() – . . true, , false.
Java, Python .
Java- true , , . , getDeclaredMethods() Method. Method invoke(), Method. 7 true, , method.invoke(object).
Python. , Python , , :
>>> for method_name in dir(my_car): ... if callable(getattr(my_car, method_name)): ... print(method_name) ... __add__ __class__ __delattr__ __dir__ __eq__ __format__ __ge__ __getattribute__ __gt__ __init__ __init_subclass__ __le__ __lt__ __ne__ __new__ __reduce__ __reduce_ex__ __repr__ __setattr__ __sizeof__ __str__ __subclasshook__
Python , Java. str () :
>>> for method_name in dir(my_car): ... attr = getattr(my_car, method_name) ... if callable(attr): ... if method_name == '__str__': ... print(attr()) ... Car yellow : Beetle : 1966
, dir(). , getattr(), callable(), . , , str (), .