Class Definition and Object Creation

Java: java public class Car { // Fields (attributes) private String color; private String model; private int year;

TrackJava to Python Journey
Current SectionPython Basics
Progress11 of 19

1. Class Definition and Object Creation

Java:

public class Car {
    // Fields (attributes)
    private String color;
    private String model;
    private int year;
    
    // Constructor
    public Car(String color, String model, int year) {
        this.color = color;
        this.model = model;
        this.year = year;
    }
    
    // Method
    public void displayInfo() {
        System.out.println(model + " (" + year + ") - " + color);
    }
}

// Creating object
Car myCar = new Car("Red", "Tesla", 2023);
myCar.displayInfo();

Python:

class Car:
    # Constructor (initializer)
    def __init__(self, color, model, year):
        self.color = color
        self.model = model
        self.year = year
    
    # Method
    def display_info(self):
        print(f"{self.model} ({self.year}) - {self.color}")

# Creating object
my_car = Car("Red", "Tesla", 2023)
my_car.display_info()

Key Differences:

  • Java requires explicit field declarations with types; Python defines attributes dynamically in __init__.
  • Java uses new keyword; Python doesn't.
  • Java uses this; Python uses self (explicitly required as first parameter).
  • Python's __init__ is similar to Java constructors but technically different.

2. Access Modifiers and Encapsulation

Java:

public class BankAccount {
    private double balance;  // Private field
    
    public BankAccount(double initialBalance) {
        this.balance = initialBalance;
    }
    
    // Getter
    public double getBalance() {
        return balance;
    }
    
    // Setter with validation
    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }
    
    private void auditLog() {  // Private method
        System.out.println("Logging transaction");
    }
}

BankAccount account = new BankAccount(1000);
// account.balance = 5000;  // Error: balance is private
account.deposit(500);
System.out.println(account.getBalance());  // 1500

Python:

class BankAccount:
    def __init__(self, initial_balance):
        self.__balance = initial_balance  # "Private" (name mangling)
    
    # Getter
    def get_balance(self):
        return self.__balance
    
    # Setter with validation
    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
    
    def __audit_log(self):  # "Private" method
        print("Logging transaction")

account = BankAccount(1000)
account.deposit(500)
print(account.get_balance())  # 1500

# Can still access (but discouraged):
# print(account._BankAccount__balance)  # Name mangling

Python Properties (More Pythonic):

class BankAccount:
    def __init__(self, initial_balance):
        self._balance = initial_balance  # Convention: single underscore = "protected"
    
    @property
    def balance(self):  # Getter
        return self._balance
    
    @balance.setter
    def balance(self, amount):  # Setter
        if amount >= 0:
            self._balance = amount

account = BankAccount(1000)
print(account.balance)  # Uses getter
account.balance = 1500  # Uses setter

Key Differences:

  • Java has strict access modifiers (public, private, protected, package-private).
  • Python uses naming conventions: _protected, __private (name mangling).
  • Python's encapsulation is convention-based, relying on developer cooperation.

3. Inheritance

Single Inheritance

Java:

// Parent class
class Animal {
    protected String name;
    
    public Animal(String name) {
        this.name = name;
    }
    
    public void eat() {
        System.out.println(name + " is eating");
    }
}

// Child class
class Dog extends Animal {
    public Dog(String name) {
        super(name);  // Call parent constructor
    }
    
    public void bark() {
        System.out.println(name + " is barking");
    }
}

Dog dog = new Dog("Buddy");
dog.eat();   // Inherited method
dog.bark();  // Own method

Python:

# Parent class
class Animal:
    def __init__(self, name):
        self.name = name
    
    def eat(self):
        print(f"{self.name} is eating")

# Child class
class Dog(Animal):
    def __init__(self, name):
        super().__init__(name)  # Call parent constructor
    
    def bark(self):
        print(f"{self.name} is barking")

dog = Dog("Buddy")
dog.eat()   # Inherited method
dog.bark()  # Own method

Multiple Inheritance

Java (Not Supported for Classes):

// Java uses interfaces for multiple inheritance
interface Flyable {
    void fly();
}

interface Swimmable {
    void swim();
}

class Duck implements Flyable, Swimmable {
    public void fly() {
        System.out.println("Duck is flying");
    }
    
    public void swim() {
        System.out.println("Duck is swimming");
    }
}

Duck duck = new Duck();
duck.fly();
duck.swim();

Python (Fully Supported):

class Flyable:
    def fly(self):
        print("Flying")

class Swimmable:
    def swim(self):
        print("Swimming")

class Duck(Flyable, Swimmable):  # Multiple inheritance
    pass

duck = Duck()
duck.fly()
duck.swim()

Key Differences:

  • Python supports multiple inheritance directly; Java does not (uses interfaces instead).
  • Python uses Method Resolution Order (MRO) via C3 linearization to handle diamond problem.
  • Java avoids diamond problem by not allowing multiple class inheritance.

4. Method Overriding and Polymorphism

Java:

class Animal {
    public void makeSound() {
        System.out.println("Animal sound");
    }
}

class Dog extends Animal {
    @Override  // Optional annotation
    public void makeSound() {
        System.out.println("Bark");
    }
}

class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Meow");
    }
}

// Polymorphism
Animal myDog = new Dog();
Animal myCat = new Cat();
myDog.makeSound();  // Bark
myCat.makeSound();  // Meow

Python:

class Animal:
    def make_sound(self):
        print("Animal sound")

class Dog(Animal):
    def make_sound(self):  # Override
        print("Bark")

class Cat(Animal):
    def make_sound(self):  # Override
        print("Meow")

# Polymorphism (duck typing)
my_dog = Dog()
my_cat = Cat()
my_dog.make_sound()  # Bark
my_cat.make_sound()  # Meow

# Duck typing example
def animal_sound(animal):
    animal.make_sound()  # No type checking needed

animal_sound(my_dog)  # Bark
animal_sound(my_cat)  # Meow

Key Difference:

  • Python uses duck typing: "If it walks like a duck and quacks like a duck, it's a duck".
  • Java uses static typing with explicit type declarations.

5. Abstract Classes and Interfaces

Java:

// Abstract class
abstract class Shape {
    abstract double area();  // Abstract method
    
    public void display() {  // Concrete method
        System.out.println("Area: " + area());
    }
}

class Circle extends Shape {
    private double radius;
    
    public Circle(double radius) {
        this.radius = radius;
    }
    
    @Override
    double area() {
        return Math.PI * radius * radius;
    }
}

// Interface
interface Drawable {
    void draw();
}

class Rectangle extends Shape implements Drawable {
    private double width, height;
    
    public Rectangle(double w, double h) {
        width = w;
        height = h;
    }
    
    @Override
    double area() {
        return width * height;
    }
    
    @Override
    public void draw() {
        System.out.println("Drawing rectangle");
    }
}

Python:

from abc import ABC, abstractmethod

# Abstract class
class Shape(ABC):
    @abstractmethod
    def area(self):  # Abstract method
        pass
    
    def display(self):  # Concrete method
        print(f"Area: {self.area()}")

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius
    
    def area(self):
        return 3.14 * self.radius ** 2

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height
    
    def area(self):
        return self.width * self.height

circle = Circle(5)
circle.display()  # Area: 78.5

6. Static Methods and Class Methods

Java:

class MathUtils {
    // Static method
    public static int add(int a, int b) {
        return a + b;
    }
}

// Call without creating instance
int result = MathUtils.add(5, 3);  // 8

Python:

class MathUtils:
    # Static method
    @staticmethod
    def add(a, b):
        return a + b
    
    # Class method
    @classmethod
    def multiply(cls, a, b):
        return a * b

# Call without creating instance
result = MathUtils.add(5, 3)  # 8
result2 = MathUtils.multiply(4, 2)  # 8

7. Constructor Overloading

Java:

class Person {
    private String name;
    private int age;
    
    // Constructor 1
    public Person(String name) {
        this.name = name;
        this.age = 0;
    }
    
    // Constructor 2 (overloaded)
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

Person p1 = new Person("Alice");
Person p2 = new Person("Bob", 30);

Python:

class Person:
    def __init__(self, name, age=0):  # Default parameter
        self.name = name
        self.age = age

p1 = Person("Alice")
p2 = Person("Bob", 30)

Key Difference:

  • Java supports constructor overloading.
  • Python uses default arguments instead (only one __init__ allowed).

8. Practical Example: Complete Class

Java:

public class Employee {
    private String name;
    private double salary;
    private static int employeeCount = 0;
    
    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
        employeeCount++;
    }
    
    public void giveRaise(double percentage) {
        salary += salary * (percentage / 100);
    }
    
    public static int getEmployeeCount() {
        return employeeCount;
    }
    
    @Override
    public String toString() {
        return name + ": $" + salary;
    }
}

Employee emp1 = new Employee("Alice", 50000);
emp1.giveRaise(10);
System.out.println(emp1);  // Alice: $55000.0
System.out.println(Employee.getEmployeeCount());  // 1

Python:

class Employee:
    employee_count = 0  # Class variable
    
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary
        Employee.employee_count += 1
    
    def give_raise(self, percentage):
        self.salary += self.salary * (percentage / 100)
    
    @classmethod
    def get_employee_count(cls):
        return cls.employee_count
    
    def __str__(self):  # Similar to toString()
        return f"{self.name}: ${self.salary}"

emp1 = Employee("Alice", 50000)
emp1.give_raise(10)
print(emp1)  # Alice: $55000.0
print(Employee.get_employee_count())  # 1

Summary Table

  • Class Definition

    • Java: Explicit field declarations
    • Python: Dynamic attributes in __init__
  • Access Modifiers

    • Java: public, private, protected (strict)
    • Python: Convention-based (_, __)
  • Inheritance

    • Java: Single inheritance only (+ interfaces)
    • Python: Multiple inheritance supported
  • Constructor

    • Java: Method with class name, can be overloaded
    • Python: __init__ method, use default params
  • Static Methods

    • Java: static keyword
    • Python: @staticmethod decorator
  • Abstract Classes

    • Java: abstract keyword
    • Python: ABC module with @abstractmethod
  • Method Reference

    • Java: this (implicit)
    • Python: self (explicit first parameter)
  • Polymorphism

    • Java: Type-based
    • Python: Duck typing
  • Object Creation

    • Java: new keyword required
    • Python: Direct class call