OOP syntax in different programming languages
Reading time10 minAs we saw in the introduction to OOP concepts course, a class is a kind of template for creating objects. It’s in the class that we’ll define our methods and attributes.
OOP is particularly useful when you need to represent data that’s a little more complex than a simple number or string. Of course, there are classes that every programming language (including Python) defines for us: numbers, strings and lists are among them. But we’d be limited if we couldn’t create our own classes.
Class definition
To introduce the syntax, let’s create a first, very simple class representing a person (class Person
). For us here, a person is characterized by the surname, first name, age and place of residence (city). The class will therefore have 4 attributes and a constructor.
The creation of this class corresponds to the code below.
class Person:
""" Represents a person with a name, firstname, age, and city.
"""
def __init__(self, name: str, firstname: str, age: int, city: str): # The constructor
""" Constructs a new Person object with the specified name, firstname, age, and city.
"""
self.name = name
self.firstname = firstname
self.age = age
self.city = city
/**
* Represents a person with a name, firstname, age, and city.
*/
public class Person {
private String name;
private String firstname;
private int age;
private String city;
/**
* Constructs a new Person object with the specified name, firstname, age, and city.
*
* @param name the person's last name
* @param firstname the person's first name
* @param age the person's age
* @param city the person's city of residence
*/
public Person(String name, String firstname, int age, String city) { // The constructor
this.name = name;
this.firstname = firstname;
this.age = age;
this.city = city;
}
}
Let’s look at the syntax in detail:
Create a person.py
file (or a Person.java
file for the Java code) and copy the above code. If you try to execute it, nothing happens, the Java code raises even an exception… You’ve just defined the class, we need to instantiate it before we can use it.
Class instantiation: creating objects
We want to create two people.
if __name__ == '__main__':
# Create a person named Alice Weber from London and 33 years old
one_person = Person("Weber", "Alice", 33, "London")
print(one_person.to_string())
# Create a person named Bob Smith from Brighton and 25 years old
a_second_person = Person("Smith", "Bob", 25, "Brighton")
print(a_second_person)
public static void main(String[] args) {
// Create a person named Alice Weber from London and 33 years old
Person personOne = new Person("Weber", "Alice", 33, "London");
System.out.println(personOne);
// Create a person named Alice Weber from London and 33 years old
Person personTwo = new Person("Smith", "Bob", 25, "Brighton");
System.out.println(personTwo);
}
Copy/paste the code above at the end of the person.py
file (or the Person.java
file for the Java code). Execute it. Now, you can see something not very comprehensible… Here, the most important thing is to see the class from which the object comes. So we can check that both objects do indeed come from our Person
class.
We’re now going to add a method that returns a string representing a person, and we’ll call it after instantiating the class.
class Person:
""" Represents a person with a name, firstname, age, and city.
"""
def __init__(self, name: str, firstname: str, age: int, city: str): # The constructor
""" Constructs a new Person object with the specified name, firstname, age, and city.
"""
self.name = name
self.firstname = firstname
self.age = age
self.city = city
def to_string(self) -> str:
""" Returns a string representation of the person.
"""
return f"{self.firstname} {self.name} ({self.age} old, from {self.city})"
if __name__ == '__main__':
one_person = Person("Weber", "Alice", 33, "London")
print(one_person.to_string())
a_second_person = Person("Smith", "Bob", 25, "Brighton")
print(a_second_person.to_string())
/**
* Represents a person with a name, firstname, age, and city.
*/
public class Person {
private String name;
private String firstname;
private int age;
private String city;
/**
* Constructs a new Person object with the specified name, firstname, age, and city.
*
* @param name the person's last name
* @param firstname the person's first name
* @param age the person's age
* @param city the person's city of residence
*/
public Person(String name, String firstname, int age, String city) { // The constructor
this.name = name;
this.firstname = firstname;
this.age = age;
this.city = city;
}
public String toString() {
return firstname + " " + name + " (" + age + " old) from " + city;
}
public static void main(String[] args) {
Person personOne = new Person("Weber", "Alice", 33, "London");
System.out.println(personOne.toString());
Person personTwo = new Person("Smith", "Bob", 25, "Brighton");
System.out.println(personTwo.toString());
}
}
Now we can talk about the self
(this
in Java) keyword… When you create an object, in this case a person, the values of its attributes are unique to it (the value of the name
attribute of one_person
(personOne
) is not the same as that of a_second_person
(personTwo
)). This is logical, as each person has his/her own name, etc. So when you call a method from an object, it’s important to know on which object it should be executed and this is the role of the self
(this
) keyword.
Encapsulation
Finally, let’s look at the concept of encapsulation. As a reminder, this concept consists in protecting or hiding certain attributes of an object.
Inheritance
Inheritance is a way of structuring code that defines hierarchical relationships between classes. To illustrate how to create classes that inherit from another, let’s suppose we want to create a program that manipulates dogs and cats. Both have a nickname and we want to track the number of meals they have eaten since born. However, dogs bark and cats meow and we want to track the weight of a dog.
As dogs and cats have common characteristics (nickname and number of meals), we decide to create an Animal
class with two attributes (one to store the name of the animal and the other one to count the number of times it has eaten) and one method, executed each time the animal eats something.
Bellow you’ll find the code of the Animal
class.
# Here, we create a class "Animal"
"""
Represents an animal with a nickname and the number of meals it has eaten.
"""
class Animal ():
# Classes have a special method "__init__" called a constructor.
# This is the code that will be executed when you instantiate an animal later.
# All methods of a class should the keyword "self" as first argument.
# This keyword references the object itself when the class is instantiated.
def __init__ (self, name: str):
"""
Constructs a new Animal object with the specified nickname.
name: the animal's nickname
"""
self.nickname = name # Create an attribute to store the name
self.__nb_meals = 0 # Create an attribute to count how many meals the animal had
# Now, let's define methods that do custom stuff
def get_nickname (self) -> str:
"""
This method returns the nickname of the animal.
"""
return self.nickname
def eat_stuff (self, stuff_name: str):
"""
This method is called when the animal eats something.
stuff_name: the name of the stuff the animal is eating
"""
print("Yum, tasty", stuff_name)
self.__nb_meals += 1
def get_nb_meals(self) -> int:
"""
This method returns the number of meals the animal has eaten.
"""
return self.__nb_meals
// Here we create the class Animal.
/**
* Represents an animal with a nickname and the number of meals it has eaten.
* Thus, it has two attributes:
* - nickname: the name of the animal
* - nbMeals: the number of times it has eaten
**/
public class Animal {
// Attributes
private String nickname;
private int nbMeals;
/**
* Constructs a new Animal object with the specified nickname.
* When created an animal has no eaten meal.
* @param name the animal's nickname
*/
public Animal(String name) {
this.nickname = name;
this.nbMeals = 0;
}
/**
* Returns the nickname of the animal.
* @return the nickname of the animal
*/
public String getNickname() {
return this.nickname;
}
// Now, let's define methods that do custom stuff.
/**
* This method is called when the animal eats something.
* @param stuffName the name of the stuff the animal is eating
*/
public void eatStuff(String stuffName) {
System.out.println("Yum, tasty", stuffName)
this.nbMeals += 1
}
/**
* Returns the number of meals the animal has eaten.
* @return the number of meals the animal has eaten
*/
public int getNbMeals() {
return this.nbMeals
}
}
Now, how dogs and cats are reified in our program? On one hand, as an animal, a dog should have a nickname and the number of times it has eaten but a dog has also a weight and can bark. On the other hand, a cat is also an animal but it also meows. Therefore, we’re going to implement the Dog
and Cat
classes as childs of the Animal
class so that they have access to its attributes and methods automatically. The Dog
class will have a new attribute (weight
) and a new method (bark
) and the Cat
class will provide a method meow
. The code for these classes is presented bellow.
# Here, we create a class "Dog"
"""
Represents a dog with a nickname, the number of meals it has eaten, and its weight.
A Dog inherits from an Animal.
"""
class Dog (Animal):
# It is good practice to also call the parent's constructor
# Then you can complement with additional codes if needed
# It is also good practice not to repeat arguments of the parent class
# Arguments *args and **kwargs are here for this purpose
def __init__ (self, size, *args, **kwargs):
"""
Constructs a new Dog object with the specified nickname and weight.
size: the dog's weight
"""
super().__init__(*args, **kwargs) # Call parent's constructor
self.weight = weight # Create an attribute to store the weight
# The weight of a dog can be accessed
def get_weight (self) -> float:
"""
This method returns the weight of the dog.
"""
return self.weight
# Now, let's define a new method
# A dog can bark
def bark (self):
"""
This method is called when the dog barks.
"""
print("Woof")
# Here, we create a class "Cat"
"""
Represents a cat with a nickname and the number of meals it has eaten.
A Cat inherits from an Animal.
"""
class Cat (Animal):
# It is good practice to also call the parent's constructor
# Then you can complement with additional codes if needed
# It is also good practice not to repeat arguments of the parent class
# Arguments *args and **kwargs are here for this purpose
def __init__ (self, *args, **kwargs):
"""
Constructs a new Cat object with the specified nickname.
"""
super().__init__(*args, **kwargs) # Call parent's constructor
# Now, let's define a new method
# A cat can meow
def meow (self):
"""
This method is called when the cat meows.
"""
print("Meow")
// Here we create the class Dog.
/**
* Represents a dog with a nickname, the number of meals it has eaten, and its weight.
* A Dog inherits from an Animal.
**/
public class Dog extends Animal {
// The specific attribute of a Dog
private float weight;
// Because Dog inherits from Animal, the parent's constructor must be called
// Then you can complement with additional codes if needed
/**
* Constructs a new Dog object with the specified nickname and weight.
*
* @param name the dog's nickname
* @param weight the dog's weight when created
*/
public Dog(String name, float weight) {
super(name); // Call parent's constructor to initialize nickname and nbMeals
this.weight = weight; // Initialize the weight attribute
}
// The weight of a dog can be accessed
/**
* This method returns the weight of the dog.
* @return the weight of the dog
*/
public float getWeight() {
return this.weight;
}
// Now, let's define a new method
// A Dog can bark
/**
* This method is called when the dog barks.
*/
public void bark() {
System.out.println("Woof");
}
}
// Here we create the class Cat.
/**
* Represents a cat with a nickname and the number of meals it has eaten.
* A Cat inherits from an Animal.
**/
public class Cat extends Animal {
/**
* Constructs a new Cat object with the specified nickname.
*
* @param name the cat's nickname
*/
public Cat(String name) {
super(name); // Call parent's constructor to initialize nickname and nbMeals
}
// Now, let's define a new method
// A Cat can meow
/**
* This method is called when the cat meows.
*/
public void meow() {
System.out.println("Meow");
}
}
Let’s try this code:
# Instantiate an object of class Dog
a_dog = newDog(42, "Snoopy")
# Show its attributes "Snoopy", 42, 0
print(a_dog.get_nickname(), a_dog.get_weight(), a_dog.get_nb_meals())
# Call its methods
a_dog.eat_stuff("cookie") # Shows "Yum, tasty cookie"
a_dog.bark() # Shows "Woof"
# Show its attributes again
print(a_dog.get_nickname(), a_dog.get_weight(), a_dog.get_nb_meals()) # Shows "Snoopy", 42, 1
# Instantiate an object of class Cat
a_cat = Cat("Garfield")
# Show its attributes "Garfield", 0
print(a_cat.get_nickname(), a_cat.get_nb_meals())
# Call its methods
a_cat.eat_stuff("kittens") # Shows "Yum, tasty kittens"
a_cat.meow() # Shows "Meow"
a_cat.bark() # Raises an error
// Instantiate an object of class Dog
Dog aDog = new Dog("Snoopy", 42)
// Show its attributes "Snoopy", 42, 0
System.out.println(a.getNickname() + "," + a.getWeight() + "," + a.getNbMeals())
// Call its methods
aDog.eatStuff("cookie") // Shows "Yum, tasty cookie"
aDog.bark() // Shows "Woof"
// Show its attributes again
System.out.println(a.getNickname() + "," + a.getWeight() + "," + a.getNbMeals()) // Shows "Snoopy", 42, 1
// Instantiate an object of class Cat
Cat aCat = new Cat("Garfield")
// Show its attributes "Garfield", 0
System.out.println(a.nickname + "," + a.getNbMeals())
// Call its methods
aCat.eatStuff("kittens") // Shows "Yum, tasty kittens"
aCat.meow() // Shows "Meow"
aCat.bark() // Raises an error