Hallo Habr! Ich präsentiere Ihnen die Übersetzung des Artikels „Objektorientierte Programmierung in Python vs Java“ von John Fincher.
Die Implementierung der objektorientierten Programmierung (OOP) in Java und Python ist unterschiedlich. Das Prinzip der Arbeit mit Objekten, Variablentypen und anderen Sprachfunktionen kann zu Schwierigkeiten beim Wechsel von einer Sprache in eine andere führen. Dieser Artikel, der sowohl für Java-Programmierer, die Python lernen möchten, als auch für Python-Programmierer, die Java besser lernen möchten, nützlich sein kann, enthält die wichtigsten Ähnlichkeiten und Unterschiede zwischen diesen Sprachen in Bezug auf OOP.
Weitere Details - unter dem Schnitt.
Klassenbeispiele in Python und Java
Lassen Sie uns zunächst die einfachste Klasse in Python und Java implementieren, um einige der Unterschiede in diesen Sprachen zu veranschaulichen, und wir werden diese Klasse schrittweise ändern.
Stellen Sie sich vor, wir haben die folgende Definition der Car-Klasse in 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 }
Der Name der Java-Quelldatei muss mit dem Namen der darin gespeicherten Klasse übereinstimmen, daher müssen wir die Datei Car.java benennen. Jede Java-Datei kann nur eine öffentliche Klasse enthalten.
Dieselbe Klasse in Python würde folgendermaßen aussehen:
1 class Car: 2 def __init__(self, color, model, year): 3 self.color = color 4 self.model = model 5 self.year = year
In Python können Sie eine Klasse überall und jederzeit deklarieren. Speichern Sie diese Datei als car.py.
Auf dieser Grundlage setzen wir das Studium der Hauptkomponenten von Klassen und Objekten fort.
Objektattribute
In allen objektorientierten Sprachen werden Daten zu einem Objekt irgendwo gespeichert. Sowohl in Python als auch in Java werden diese Daten in Attributen gespeichert, bei denen es sich um Variablen handelt, die bestimmten Objekten zugeordnet sind.
Einer der wichtigsten Unterschiede zwischen Python und Java besteht darin, wie sie Klassen- und Objektattribute definieren und wie diese Sprachen sie steuern. Einige dieser Unterschiede werden durch sprachliche Einschränkungen verursacht, während andere mit einer effektiveren Praxis verbunden sind.
Erklärung und Initialisierung
In Java deklarieren wir Attribute (die ihren Typ angeben) innerhalb der Klasse, jedoch außerhalb aller Methoden. Bevor wir Klassenattribute verwenden, müssen wir sie definieren:
1 public class Car { 2 private String color; 3 private String model; 4 private int year;
In Python deklarieren und definieren wir Attribute innerhalb der Methode der init () -Klasse, die ein Analogon zum Konstruktor in Java ist:
1 def __init__(self, color, model, year): 2 self.color = color 3 self.model = model 4 self.year = year
Indem Sie das Schlüsselwort self vor dem Variablennamen angeben, teilen wir Python mit, dass dies Attribute sind. Jede Instanz der Klasse erhält ihre Kopie. Alle Variablen in Python sind nicht lose typisiert, und Attribute sind keine Ausnahme.
Variablen können auch außerhalb der init () -Methode erstellt werden. Dies ist jedoch nicht die beste Lösung und kann zu schwer zu erkennenden Fehlern führen. Beispielsweise können Sie dem Autoobjekt wie folgt ein neues Radattribut hinzufügen:
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
Wenn wir jedoch vergessen, den Ausdruck my_car.wheels = 5 in der 6. Zeile anzugeben, erhalten wir eine Fehlermeldung:
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'
Wenn Sie in Python eine Variable außerhalb der Methode deklarieren, wird sie als Klassenvariable betrachtet. Lassen Sie uns die Autoklasse ändern:
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
Jetzt ändert sich die Verwendung der variablen Räder. Anstatt über ein Objekt darauf zuzugreifen, greifen wir über den Klassennamen darauf zu:
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
Hinweis: In Python wird auf eine Klassenvariable mit der folgenden Syntax zugegriffen:
- Der Name der Datei, die die Klasse enthält (ohne die Erweiterung .py)
- Punkt
- Klassenname
- Punkt
- Variablenname
Da wir die Car-Klasse in der Datei car.py gespeichert haben, verweisen wir auf die Variable der Wheel-Klasse in der 6. Zeile folgendermaßen: car.Car.wheels.
Wenn Sie mit der Radvariablen arbeiten, müssen Sie darauf achten, dass das Ändern des Werts einer Instanzvariablen der Klasse my_car.wheels nicht zum Ändern der Variablen der Klasse car.Car.wheels führt:
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 >>>
In den Zeilen 2 und 3 haben wir zwei Autoobjekte definiert: my_car und my_other_car.
Erstens ist die Räderigenschaft beider Objekte Null. In der 16. Zeile setzen wir die Klassenvariable: car.Car.wheels = 4, beide Objekte haben jetzt 4 Räder. Wenn wir jedoch in der 24. Zeile die Eigenschaft des Objekts my_car.wheels = 5 ändern, bleibt die Eigenschaft des zweiten Objekts unberührt.
Dies bedeutet, dass wir jetzt zwei verschiedene Kopien des Attributs "Räder" haben:
- Klassenvariable, die für alle Autoobjekte gilt
- Eine bestimmte Klasseninstanzvariable, die nur für das my_car-Objekt gilt.
Aus diesem Grund können Sie versehentlich auf die falsche Instanz verweisen und einen subtilen Fehler machen.
In Java ist das Äquivalent eines Klassenattributs ein statisches Attribut:
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; } }
Normalerweise greifen wir in Java über den Klassennamen auf statische Variablen zu. Sie können wie in Python über eine Instanz der Klasse darauf zugreifen, dies ist jedoch nicht die beste Lösung.
Unsere Java-Klasse beginnt sich zu verlängern. Einer der Gründe, warum Java für Python „ausführlich“ ist, ist der Begriff der öffentlichen und privaten Methoden und Attribute.
Öffentlich und privat
Java steuert den Zugriff auf Methoden und Attribute, indem zwischen öffentlichen und privaten Daten unterschieden wird.
In Java wird erwartet, dass Attribute als privat deklariert werden (oder geschützt werden, wenn die Nachkommen der Klasse Zugriff darauf benötigen). Daher beschränken wir den Zugang von außen zu ihnen. Um den Zugriff auf private Attribute zu ermöglichen, deklarieren wir öffentliche Methoden, die diese Daten installieren oder empfangen (dazu später mehr).
Denken Sie daran, dass in unserem Java-Code die Farbvariable als privat deklariert wurde. Daher wird der folgende Code nicht kompiliert:
Car myCar = new Car("blue", "Ford", 1972);
Wenn Sie die Zugriffsebene auf Attribute nicht angeben, wird diese standardmäßig als paketgeschützt festgelegt , wodurch der Zugriff auf Klassen innerhalb des Pakets eingeschränkt wird. Wenn der obige Code funktionieren soll, müssen wir das Attribut öffentlich machen.
Java wird jedoch nicht dazu ermutigt, Attribute als öffentlich zu deklarieren. Es wird empfohlen, dass Sie sie als privat deklarieren und dann öffentliche Methoden wie getColor () und getModel () verwenden, wie im obigen Code erwähnt.
Im Gegensatz dazu fehlt Python der Begriff der öffentlichen und privaten Daten. In Python ist alles öffentlich. Dieser Python-Code funktioniert mit einem Knall:
>>> my_car = car.Car("blue", "Ford", 1972) >>>
Anstelle von privaten Variablen in Python gibt es das Konzept nicht öffentlicher ( nicht öffentlicher ) Klasseninstanzvariablen. Alle Variablen, deren Namen mit einem einzelnen Unterstrich beginnen, gelten als nicht öffentlich. Diese Namenskonvention hindert uns nicht daran, direkt auf die Variable zuzugreifen.
Fügen Sie unserer Python-Klasse Car die folgende Zeile hinzu:
class Car: wheels = 0 def __init__(self, color, model, year): self.color = color self.model = model self.year = year self._cupholders = 6
Wir können direkt auf die Variable _cupholders zugreifen:
>>> 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.
Mit Python können Sie auf eine solche Variable zugreifen. Einige Entwicklungsumgebungen wie VS Code geben jedoch eine Warnung aus:

Darüber hinaus verwendet Python einen doppelten Unterstrich am Anfang eines Variablennamens, um ein Attribut auszublenden. Wenn Python eine solche Variable sieht, ändert es automatisch seinen Namen, um den direkten Zugriff zu erschweren. Dieser Mechanismus hindert uns jedoch immer noch nicht daran, uns ihm zuzuwenden. Wir demonstrieren dies anhand des folgenden Beispiels:
class Car: wheels = 0 def __init__(self, color, model, year): self.color = color self.model = model self.year = year self.__cupholders = 6
Wenn wir uns nun die Variable __cupholders ansehen, erhalten wir eine Fehlermeldung:
>>> 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'
Warum existiert das Attribut __cupholders nicht?
Hier ist das Ding. Wenn Python ganz am Anfang ein doppeltes Unterstrichattribut sieht, ändert es es, indem es am Anfang einen unterstrichenen Klassennamen hinzufügt. Um direkt auf das Attribut zugreifen zu können, müssen Sie auch den Namen ändern:
>>> print(f"It has {my_car._Car__cupholders} cupholders") It has 6 cupholders
Nun stellt sich die Frage: Wenn das Attribut der Java-Klasse als privat deklariert ist und dem Attribut der Python-Klasse ein doppelter Unterstrich im Namen vorangestellt ist, wie kommt man dann zu diesen Daten?
Zugangskontrolle
In Java greifen wir mit Setzern und Gettern auf private Attribute zu. Fügen Sie der Java-Klasse den folgenden Code hinzu, damit der Benutzer seinen Computer neu streichen kann:
public String getColor() { return color; } public void setColor(String color) { this.color = color; }
Da die Methoden getColor () und setColor () öffentlich sind, kann jeder Benutzer sie aufrufen und die Farbe des Computers abrufen / ändern. Die Verwendung privater Attribute, auf die wir auf öffentliche Getter und Setter zugreifen, ist einer der Gründe für die größere „Ausführlichkeit“ von Java im Vergleich zu Python.
Wie oben gezeigt, können wir in Python direkt auf Attribute zugreifen. Da alles öffentlich ist, können wir jederzeit und überall auf alles zugreifen. Wir können Attributwerte direkt abrufen und festlegen, indem wir sie nach ihrem Namen aufrufen. In Python können wir sogar Attribute entfernen, was in Java undenkbar ist:
>>> 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'
Es kommt jedoch auch vor, dass wir den Zugriff auf Attribute steuern möchten. In diesem Fall helfen uns Python-Eigenschaften.
In Python bieten Eigenschaften mithilfe von Dekoratoren einen kontrollierten Zugriff auf Klassenattribute. Mithilfe von Eigenschaften deklarieren wir Funktionen in Python-Klassen wie Getter und Setter in Java (das Entfernen von Attributen ist ein Bonus).
Die Funktionsweise der Eigenschaften ist im folgenden Beispiel der Fahrzeugklasse dargestellt:
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
In diesem Beispiel erweitern wir das Konzept der Fahrzeugklasse um Elektroautos. Zeile 6 deklariert das Attribut _voltage, um die Batteriespannung darin zu speichern.
In den Zeilen 9 und 10 für den kontrollierten Zugriff erstellen wir die Funktiontage () und geben den Wert der privaten Variablen zurück. Mit dem Dekorator @property verwandeln wir ihn in einen Getter, auf den jeder Benutzer jetzt zugreifen kann.
In den Zeilen 13-15 definieren wir eine Funktion, die auch als Spannung () bezeichnet wird. Wir dekorieren es jedoch anders: Spannungseinsteller . Schließlich verzieren wir in den Zeilen 18-20 die Funktiontage () mit Spannungsdeleter und können bei Bedarf das Attribut _voltage entfernen.
Dekorierte Funktionen haben denselben Namen, was darauf hinweist, dass sie den Zugriff auf dasselbe Attribut steuern. Diese Funktionsnamen werden auch zu den Namen der Attribute, mit denen ihre Werte abgerufen werden. So funktioniert es:
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!
Bitte beachten Sie, dass wir Spannung verwenden, nicht _ Spannung. Deshalb weisen wir Python an, die soeben definierten Eigenschaften anzuwenden:
- Wenn wir den Wert my_car.voltage in die 4. Zeile drucken, ruft Python die mit @property dekorierte Funktiontage () auf.
- Wenn wir in der 7. Zeile den Wert my_car.voltage zuweisen, ruft Python die Funktiontage () auf, die mit dem Spannungssetter dekoriert ist.
- Wenn wir my_car.voltage in Zeile 13 löschen, ruft Python die Funktiontage () auf, die mit Voltage .deleter dekoriert ist.
Die oben genannten Dekorateure geben uns die Möglichkeit, den Zugriff auf Attribute ohne Verwendung verschiedener Methoden zu steuern. Sie können das Attribut sogar zu einer schreibgeschützten Eigenschaft machen, indem Sie die dekorierten Funktionen @ .setter und @ .deleter entfernen.
Selbst und das
In Java verweist eine Klasse mit dem Schlüsselwort this auf sich selbst:
public void setColor(String color) { this.color = color; }
Dies ist im Java-Code impliziert. Im Prinzip ist es nicht einmal notwendig, es zu schreiben, außer wenn die Namen der Variablen übereinstimmen.
Setter kann so geschrieben werden:
public void setColor(String newColor) { color = newColor; }
Da die Car-Klasse ein Attribut namens color hat und keine Variablen mit demselben Namen mehr im Gültigkeitsbereich sind, wird eine Verknüpfung zu diesem Namen ausgelöst. Wir haben das Schlüsselwort this im ersten Beispiel verwendet, um zwischen einem Attribut und einem Parameter mit demselben Farbnamen zu unterscheiden.
In Python dient das Schlüsselwort self einem ähnlichen Zweck: dem Zugriff auf Attributmitglieder, im Gegensatz zu Java ist jedoch Folgendes erforderlich :
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 erfordert, dass self obligatorisch ist. Jedes Selbst erstellt oder bezieht sich auf ein Attribut. Wenn wir es überspringen, erstellt Python einfach eine lokale Variable anstelle eines Attributs.
Der Unterschied in der Art und Weise, wie wir uns selbst verwenden, und dies in Python und Java ist auf die Hauptunterschiede zwischen den beiden Sprachen und die Art und Weise zurückzuführen, wie sie Variablen und Attribute benennen.
Methoden und Funktionen
Der Unterschied zwischen den fraglichen Sprachen besteht darin, dass es in Python Funktionen gibt, in Java jedoch nicht.
In Python funktioniert der folgende Code problemlos (und wird überall verwendet):
>>> def say_hi(): ... print("Hi!") ... >>> say_hi() Hi!
Wir können say_hi () von überall in der Sichtbarkeit aufrufen. Diese Funktion enthält keinen Verweis auf self, was bedeutet, dass es sich um eine globale Funktion handelt, nicht um eine Klassenfunktion. Sie kann keine Daten einer Klasse ändern oder speichern, kann jedoch lokale und globale Variablen verwenden.
Im Gegensatz dazu gehört jede Zeile, die wir in Java schreiben, zu einer Klasse. Eine Funktion existiert nicht außerhalb der Klasse, und per Definition sind alle Java-Funktionen Methoden. In Java ist eine statische Methode einer reinen Funktion am nächsten:
public class Utils { static void SayHi() { System.out.println("Hi!"); } }
Utils. SayHi () wird von überall aufgerufen, ohne zuvor eine Instanz der Utils-Klasse zu erstellen. Da wir SayHi () aufrufen, ohne ein Objekt zu erstellen, existiert dieser Link nicht. Dies ist jedoch immer noch keine Funktion in dem Sinne, dass say_hi () in Python ist.
Vererbung und Polymorphismus
Vererbung und Polymorphismus sind zwei grundlegende Konzepte in OOP. Dank des ersten Objekts erhalten (mit anderen Worten, erben) die Attribute und Funktionen anderer Objekte, wodurch eine Hierarchie von allgemeineren Objekten zu spezifischeren Objekten erstellt wird. Beispielsweise sind sowohl die Fahrzeugklasse als auch die Bootsklasse bestimmte Typen der Fahrzeugklasse. Beide Objekte erben das Verhalten eines übergeordneten oder mehrerer übergeordneter Objekte. In diesem Fall werden sie als untergeordnete Objekte bezeichnet.
Polymorphismus ist wiederum die Fähigkeit, mit verschiedenen Objekten mit derselben Funktion oder Methode zu arbeiten.
Beide grundlegenden OOP-Konzepte werden in Java und Python auf völlig unterschiedliche Weise implementiert.
Vererbung
Python unterstützt die Mehrfachvererbung, d. H. Das Erstellen einer Klasse aus mehr als einem übergeordneten Element.
Um dies zu demonstrieren, teilen wir die Fahrzeugklasse in zwei Kategorien ein: eine für Fahrzeuge und eine für Autos, die Strom verbrauchen:
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
Die Fahrzeugklasse definiert die Farb- und Modellattribute. Die Geräteklasse hat das Attribut _voltage. Die Car-Klasse leitet sich von diesen beiden Klassen ab, und die Attribute color, model und _voltage sind jetzt Teil der neuen Klasse.
Die init () -Methode der Car-Klasse ruft die init () -Methoden beider übergeordneter Klassen auf, um sicherzustellen, dass alle Daten ordnungsgemäß initialisiert werden. Danach können wir der Fahrzeugklasse jede gewünschte Funktionalität hinzufügen. In diesem Beispiel fügen wir das Jahresattribut sowie den Getter und Setter für _voltage hinzu.
Die Funktionalität der neuen Fahrzeugklasse bleibt gleich. Wir können Klassenobjekte erstellen und verwenden, wie wir einige Beispiele zuvor getan haben:
>>> 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
Die Java-Sprache unterstützt wiederum nur eine einzelne Vererbung, was bedeutet, dass Klassen in Java Daten und Verhalten nur von einer übergeordneten Klasse erben können. In Java ist jedoch die Vererbung von mehreren Schnittstellen möglich. Schnittstellen bieten eine Gruppe verwandter Methoden, die implementiert werden müssen, damit sich untergeordnete Klassen ähnlich verhalten können.
Um dies zu sehen, haben wir die Car Java-Klasse in ihre übergeordnete Klasse und Schnittstelle aufgeteilt:
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; } }
Vergessen Sie nicht, dass jede Klasse und jede Schnittstelle in Java in einer eigenen Datei abgelegt werden muss.
Wie im obigen Beispiel mit Python erstellen wir eine neue Fahrzeugklasse, um allgemeine Daten und Funktionen zu speichern, die Fahrzeugen eigen sind. Um jedoch die Funktionalität des Geräts hinzuzufügen, müssen wir eine Schnittstelle erstellen, die die Methode zum Abrufen der Spannung des Geräts definiert.
Die Car-Klasse wird erstellt, indem mit dem Schlüsselwort extensives von der Fahrzeugklasse geerbt und die Geräteschnittstelle mit dem Schlüsselwort implement implementiert wird. Im Klassenkonstruktor rufen wir den übergeordneten Konstruktor mit super () auf. Da es nur eine übergeordnete Klasse gibt, verweisen wir auf den Konstruktor der Fahrzeugklasse. Um die Schnittstelle zu implementieren, definieren wir getVoltage () mithilfe der Annotation Override neu .
Anstatt den Code von Device wiederzuverwenden, wie dies in Python der Fall ist, erfordert Java, dass wir in jeder Klasse, die die Schnittstelle implementiert, dieselbe Funktionalität implementieren. Schnittstellen definieren nur Methoden - sie können keine Klasseninstanzdaten oder Implementierungsdetails definieren.
Warum passiert das mit 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 (), .