Menu
CoddyTech

OOPs in Java vs Python vs C++

How the same ideas look in Java, Python and C++, with the Python traps as runnable code.

12 questions
  1. How is OOP different in Java, Python and C++?

    They differ mostly in how strict they are.

    JavaPythonC++
    Multiple inheritance of classesNo; many interfacesYes, by the MROYes
    Access controlEnforcedConventions onlyEnforced, plus friend
    Methods dynamic by defaultYesYesNo, only virtual ones
    Method overloadingYesNo (last def wins)Yes
    CleanupGarbage collectorReference counting plus a cycle collector (CPython)Destructors (RAII)
  2. What is self in Python?

    self is the first parameter of an instance method: the object the method was called on. Python passes it for you, so c.increment() is shorthand for Counter.increment(c).

    Python
    class Counter:
        def __init__(self):
            self.value = 0
    
        def increment(self):
            self.value += 1
    
    c = Counter()
    c.increment()
    Counter.increment(c)
    print(c.value)

    It prints 2. self is a convention, not a keyword. See Python classes.

  3. Can you read a double-underscore attribute from outside a Python class?

    Python
    class Account:
        def __init__(self):
            self.__pin = 1234
            self._owner = "asha"
    
    a = Account()
    print(hasattr(a, "__pin"), a._owner, a._Account__pin)

    A name with two leading underscores inside a class is mangled to _ClassName__name, so self.__pin is stored as _Account__pin. hasattr(a, "__pin") is False, while a._Account__pin still works. A single underscore is only a convention, and nothing stops access. Mangling avoids name clashes in subclasses; it is not security.

  4. Is a list declared in a Python class body per object or per class?

    Python
    class Dog:
        tricks = []
    
        def __init__(self, name):
            self.name = name
    
        def add_trick(self, trick):
            self.tricks.append(trick)
    
    rex = Dog("Rex")
    fido = Dog("Fido")
    rex.add_trick("sit")
    print(fido.tricks)

    tricks = [] is a class attribute: one list on the class, shared by every instance. self.tricks.append(...) finds that list and mutates it, so Fido sees Rex's trick. Create per-object state in __init__ (self.tricks = []); class attributes are safe for constants and values you only read.

  5. Which parent's method does Python call on a diamond declared as D(B, C)?

    Python
    class A:
        def hi(self):
            return "A"
    
    class B(A):
        def hi(self):
            return "B"
    
    class C(A):
        def hi(self):
            return "C"
    
    class D(B, C):
        pass
    
    print(D().hi(), [k.__name__ for k in D.__mro__])

    Python resolves methods along the MRO (method resolution order), computed by C3 linearization: the class, then its parents left to right, with a shared base after every class that inherits from it. For D(B, C) that is D, B, C, A, object, so hi() comes from B. More in Python inheritance.

  6. Does Python support method overloading?

    Not the way Java does. A second def with the same name replaces the first, so only the last exists. Use default arguments, *args, or functools.singledispatch to choose by the type of the first argument (singledispatchmethod, Python 3.8+, does it for methods).

    Python
    from functools import singledispatch
    
    class Calc:
        def add(self, a, b):
            return a + b
    
        def add(self, a, b, c=0):  # replaces the method above
            return a + b + c
    
    print(Calc().add(1, 2), Calc().add(1, 2, 3))
    
    @singledispatch
    def describe(x):
        return "something"
    
    @describe.register
    def _(x: int):
        return f"int {x}"
    
    @describe.register
    def _(x: str):
        return f"str {x!r}"
    
    print(describe(5), describe("hi"), describe(2.5))

    It prints 3 6, then int 5 str 'hi' something.

  7. What is @property in Python, and why use it instead of getters and setters?

    @property (a decorator) makes a method read like an attribute, and @name.setter validates assignments. You can add a property later without changing any caller, so Python code does not write get_x() and set_x() up front.

    Python
    class Temperature:
        def __init__(self, celsius):
            self.celsius = celsius  # goes through the setter
    
        @property
        def celsius(self):
            return self._celsius
    
        @celsius.setter
        def celsius(self, value):
            if value < -273.15:
                raise ValueError("below absolute zero")
            self._celsius = value
    
        @property
        def fahrenheit(self):
            return self._celsius * 9 / 5 + 32
    
    t = Temperature(25)
    print(t.fahrenheit)
    try:
        t.celsius = -300
    except ValueError as e:
        print("Refused:", e)

    It prints 77.0, then Refused: below absolute zero. fahrenheit has no setter, so it is read-only.

  8. What is duck typing, and how is it different from using interfaces?

    Duck typing accepts an object for what it can do, not the type it declares: if it has speak(), you can call speak(). Python checks at runtime; Java requires implements Speaker and checks at compile time.

    Python
    class Duck:
        def speak(self):
            return "Quack"
    
    class Robot:
        def speak(self):
            return "Beep"
    
    def chorus(things):
        return " ".join(t.speak() for t in things)
    
    print(chorus([Duck(), Robot()]))

    It prints Quack Beep although Duck and Robot share no parent. A missing method fails only when that line runs.

  9. What is a virtual function in C++? What happens without virtual?

    A virtual function is dispatched at runtime from the object's actual type when called through a base pointer or reference. Without virtual, C++ binds the call at compile time from the pointer's type, the opposite of Java's default.

    #include <iostream>
    
    struct Animal {
        void name() { std::cout << "Animal\n"; }           // not virtual
        virtual void sound() { std::cout << "...\n"; }
        virtual ~Animal() = default;
    };
    
    struct Dog : Animal {
        void name() { std::cout << "Dog\n"; }
        void sound() override { std::cout << "Woof\n"; }
    };
    
    int main() {
        Dog d;
        Animal& a = d;
        a.name();   // prints Animal: bound at compile time
        a.sound();  // prints Woof: dispatched at runtime
    }
    

    A pure virtual function (= 0) makes the class abstract. See C++ virtual functions.

  10. Why does a C++ base class need a virtual destructor?

    Deleting a derived object through a base class pointer whose destructor is not virtual is undefined behavior; usually the derived destructor does not run, so its resources leak.

    struct Base {
        ~Base() {}                 // should be: virtual ~Base() = default;
    };
    
    struct Buffer : Base {
        int* data = new int[1000];
        ~Buffer() { delete[] data; }
    };
    
    int main() {
        Base* p = new Buffer();
        delete p;                  // undefined behavior: ~Buffer() is not called
    }
    

    A base class destructor should be either public and virtual, or protected and non-virtual. See the cppreference delete expression page.

  11. What is the difference between a struct and a class in C++?

    The only difference is default access: members and base classes of a struct are public by default, those of a class are private. Both can have constructors, methods, inheritance and virtual functions.

    struct Point { int x, y; };        // x and y are public
    class Account { int balance; };    // balance is private
    

    By convention struct holds plain data and class holds types with invariants.

  12. What is a friend function in C++? Does it break encapsulation?

    A friend function (or class) is outside code that a class explicitly allows to read its private and protected members. It is not a member and has no this.

    #include <iostream>
    
    class Money {
        long paise;
    public:
        explicit Money(long p) : paise(p) {}
        friend std::ostream& operator<<(std::ostream& out, const Money& m);
    };
    
    std::ostream& operator<<(std::ostream& out, const Money& m) {
        return out << m.paise / 100 << "." << m.paise % 100 / 10 << m.paise % 10;
    }
    

    The classic use is operator<<, whose left operand is the stream, so it cannot be a member. Friendship is granted by the class and is neither transitive nor inherited.

Coddy programming languages illustration

Learn to code with Coddy

Get started