알고리즘 Cheat Sheet 시리즈는 코딩 테스트를 풀다가 "이거 자바에선 뭐였지?", "파이썬은 어떻게 했더라?" 싶을 때 바로 펼쳐 보려고 만든 개인 참고용 정리입니다.
Java와 Python을 나란히 놓고, 문법 차이 때문에 실수하기 쉬운 부분만 짧게 정리합니다.

이번 주제는 오버로딩과 오버라이딩입니다.
이름이 비슷해서 헷갈리지만 전혀 다른 개념이고, 면접 단골 질문이기도 합니다.
코테에서는 직접 만든 클래스를 정렬하거나 HashMap 키로 쓸 때 오버라이딩이 꼭 필요합니다.
| 구분 | 오버로딩 (Overloading) | 오버라이딩 (Overriding) |
|---|---|---|
| 한 줄 정의 | 같은 이름, 다른 매개변수로 메서드를 여러 개 생성 | 부모의 메서드를 자식이 다시 정의 |
| 관계 | 같은 클래스 안 | 상속 관계 (부모 ↔ 자식) |
| 메서드 이름 | 같다 | 같다 |
| 매개변수 | 달라야 한다 (개수 · 타입 · 순서) | 같아야 한다 |
| 반환 타입 | 상관없다 (반환 타입만 다르면 안 됨) | 같거나 하위 타입 |
| 결정 시점 | 컴파일할 때 | 실행할 때 (실제 객체 기준) |
| 자바 | ⭕ | ⭕ |
| 파이썬 | ❌ 지원하지 않는다 | ⭕ |
오버로딩은 "새로 추가(load)", 오버라이딩은 "덮어쓰기(ride over)" 로 외우자!
class Calc {
int add(int a, int b) { return a + b; }
int add(int a, int b, int c) { return a + b + c; } // 개수가 다름
double add(double a, double b) { return a + b; } // 타입이 다름
}
Calc c = new Calc();
c.add(1, 2); // 3 → 첫 번째
c.add(1, 2, 3); // 6 → 두 번째
c.add(1.5, 2.5); // 4.0 → 세 번째
우리가 늘 쓰는 Math.max(int, int), Math.max(long, long), Math.max(double, double)과 System.out.println()도 오버로딩이다.
int add(int a, int b) { ... }
long add(int a, int b) { ... } // ❌ 반환 타입만 다른 것은 오버로딩이 아니다 (컴파일 에러)
def add(a, b):
return a + b
def add(a, b, c): # 앞의 add를 덮어쓴다
return a + b + c
add(1, 2) # ❌ TypeError: add() missing 1 required positional argument: 'c'
파이썬은 대신 기본값 인자와 가변 인자로 같은 효과를 낸다.
def add(a, b, c=0): # 기본값 인자
return a + b + c
add(1, 2) # 3
add(1, 2, 3) # 6
def add_all(*nums): # 가변 인자 (몇 개든 받는다)
return sum(nums)
add_all(1, 2, 3, 4) # 10
| 상황 | 자바 | 파이썬 |
|---|---|---|
| 인자 개수에 따라 다르게 | 생성자를 여러 개 정의 | __init__에 기본값 인자 |
| 다른 생성자 호출 | this(...) | - |
class Point {
int x, y;
Point() { this(0, 0); } // 다른 생성자를 호출
Point(int x, int y) { this.x = x; this.y = y; }
}
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
| 항목 | 자바 | 파이썬 |
|---|---|---|
| 상속 | class Dog extends Animal | class Dog(Animal): |
| 부모 생성자 호출 | super(name); | super().__init__(name) |
| 부모 메서드 호출 | super.speak(); | super().speak() |
| 오버라이딩 표시 | @Override | 없음 (같은 이름으로 정의하면 끝) |
| 다중 상속 | ❌ (인터페이스는 여러 개 가능) | ⭕ |
class Animal {
String name;
Animal(String name) { this.name = name; }
void speak() { System.out.println("..."); }
}
class Dog extends Animal {
Dog(String name) { super(name); }
@Override
void speak() { // 부모의 speak를 다시 정의
super.speak(); // 부모 것도 부르고 싶을 때
System.out.println("멍멍");
}
}
Animal a = new Dog("바둑이");
a.speak(); // "..." 다음에 "멍멍" ← 변수 타입(Animal)이 아니라 실제 객체(Dog) 기준
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
print("...")
class Dog(Animal):
def __init__(self, name):
super().__init__(name)
def speak(self): # 같은 이름으로 정의하면 오버라이딩
super().speak()
print("멍멍")
Dog("바둑이").speak() # "..." 다음에 "멍멍"
public → private 불가).static, final, private 메서드는 오버라이딩할 수 없다.직접 만든 클래스를 출력, 비교, 해시, 정렬에 쓰려면 아래 메서드를 다시 정의해야 한다.
| 용도 | 자바 | 파이썬 | 안 하면 생기는 일 |
|---|---|---|---|
| 출력 | toString() | __repr__ / __str__ | Point@1b6d3586 같은 주소가 출력된다 |
| 값이 같은지 | equals(Object o) | __eq__(self, other) | 값이 같아도 다른 객체로 본다 |
해시 (HashMap · HashSet) | hashCode() | __hash__(self) | 값이 같은 키를 못 찾는다 |
| 정렬 · 우선순위 큐 | compareTo() (Comparable) | __lt__(self, other) | 정렬할 때 에러가 난다 |
class Point implements Comparable<Point> {
int x, y;
Point(int x, int y) { this.x = x; this.y = y; }
@Override
public String toString() {
return "(" + x + ", " + y + ")";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Point)) return false;
Point p = (Point) o;
return x == p.x && y == p.y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
@Override
public int compareTo(Point o) { // x 오름차순, 같으면 y 오름차순
if (x != o.x) return Integer.compare(x, o.x);
return Integer.compare(y, o.y);
}
}
Set<Point> set = new HashSet<>();
set.add(new Point(1, 2));
set.contains(new Point(1, 2)); // true (equals + hashCode 덕분)
PriorityQueue<Point> pq = new PriorityQueue<>(); // compareTo 기준으로 꺼낸다
class Point:
def __init__(self, x, y):
self.x, self.y = x, y
def __repr__(self):
return f"({self.x}, {self.y})"
def __eq__(self, other):
return (self.x, self.y) == (other.x, other.y)
def __hash__(self):
return hash((self.x, self.y))
def __lt__(self, other): # x 오름차순, 같으면 y 오름차순
return (self.x, self.y) < (other.x, other.y)
s = {Point(1, 2)}
Point(1, 2) in s # True
sorted([Point(2, 1), Point(1, 5)]) # [(1, 5), (2, 1)]
| 방법 | 코드 | 자동으로 만들어 주는 것 |
|---|---|---|
자바 record (16+) | record Point(int x, int y) {} | equals, hashCode, toString |
파이썬 dataclass | @dataclass(frozen=True, order=True) | __eq__, __hash__, __lt__, __repr__ |
| 파이썬 튜플 | (x, y) | 전부 (코테에서는 이게 제일 간단) |
from dataclasses import dataclass
@dataclass(frozen=True, order=True)
class Point:
x: int
y: int
equals(Point p)는 오버라이딩이 아니라 오버로딩이다public boolean equals(Point p) { // ❌ 매개변수 타입이 Object가 아니다
return x == p.x && y == p.y;
}
부모(Object)의 equals(Object o)와 매개변수 타입이 달라서 오버라이딩이 아니라 오버로딩이 된다.
HashSet은 equals(Object)를 부르기 때문에 이 메서드는 쓰이지 않고, 값이 같아도 못 찾는다.
@Override를 항상 붙이자! 위 코드에@Override를 붙이면 컴파일 에러로 바로 알려 준다.
equals를 재정의하면 hashCode도 반드시 함께... equals만 하면 HashSet / HashMap에서 값이 같은 객체를 못 찾는다.
파이썬에서 __eq__만 정의하면 해시가 불가능해진다. TypeError: unhashable type이 나므로 __hash__도 같이 정의한다.
파이썬 리스트를 출력하면 __str__이 아니라 __repr__을 쓴다. 하나만 정의할 거면 __repr__을 정의하자.
자바 static 메서드는 오버라이딩되지 않는다. 자식에 같은 이름으로 만들면 가려질(hiding) 뿐, 변수 타입 기준으로 호출된다.
파이썬 기본값 인자에 리스트를 쓰지 말자. 기본값은 함수 정의 시 한 번만 만들어져서 호출끼리 공유된다.
def add(x, lst=[]): # ❌
lst.append(x)
return lst
add(1) # [1]
add(2) # [1, 2] ← 이전 호출의 리스트가 남아 있다
def add(x, lst=None): # ⭕
if lst is None:
lst = []
lst.append(x)
return lst
*args로 대신한다.super.메서드()super().메서드()HashSet / HashMap에 넣으려면 equals + hashCode (파이썬은 __eq__ + __hash__)compareTo (파이썬은 __lt__)@Override를 항상 붙이자! equals(Point p) 같은 실수를 컴파일러가 잡아 준다.