자바에서는 모든 것이 클래스다.
main function조차도 클래스 내에 위치해야 하는 광기 어린 집착을 보여줄 정도다.
그래서 static 함수나 람다가 아니라면 자바의 함수는 다 메소드일텐데,
이 메소드를 호출할 때마다 항상 만들지도 않았는데 사용할 수 있는 변수가 존재한다.
바로 this
다.
public class User {
public long id;
public String name;
public void print() {
// this를 사용할 수 있다.
System.out.println(this);
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
위 코드를 보면 print
메소드 내에서 인자로 받은 것도 아니고, 지역 변수로 선언한 것도 아닌데
this
를 사용하고 있는 것을 볼 수 있다.
이것이 어떻게 가능한 것일까.
답은 매우 간단한데,
아래 rust 코드를 살펴보며 알아보자.
#[derive(Debug)]
struct User {
id: i64,
name: &'static str,
}
impl User {
pub fn print(self) {
println!("User: {:?}", self);
}
}
fn main() {
let user = User {
id: 1,
name: "John Doe",
};
// 사용법1. 자바 메소드와 같은 형태.
user.print();
// 사용법2. User의 print 함수 포인터를 호출하며 직접 User instance를 인자로 전달
User::print(user);
}
rust에서도 자바와 마찬가지로 클래스(rust에서는 struct
)에 포함된
함수(impl
블록에 정의)를 메소드 형태로 사용할 수 있다.
그런데 rust는 메소드를 만들기 위해 명시적으로 self
라는 매개변수를 정의해야 한다.
(자바의 this
와 유사하며, self
없는 함수는 associcated function
이 되어 버림)
타입은 항상 impl
블록에 명시된 타입(여기서는 User
)의 instance여야 하며,
예약어이기 때문에 변수명을 마음대로 지을 수 없고 오직 self
를 사용해야 한다.
이렇게 정의된 rust의 메소드들은 2가지 방법으로 호출이 가능하다.
user.print();
혹은 User::print(user);
와 같은 식이다.
첫 번째는 자바 코드와 유사하고, 두 번째는 살짝 낯선 형태이며 user
instance를 인자로 직접 전달하고 있다.
그러나 두 호출 방식은 기능적으로 완전히 동일하다.
방법2는 User::print
함수 포인터를 직접 호출하면서 user
instance를 인자로 전달해주었기 때문에
self에
user` instance가 들어간다는 사실을 코드상에서 좀 더 명확히 보여준다.
그러나 방법1처럼 user.print();
를 호출하더라도 self
라는 변수 안에 user
instance가 암묵적으로 전달된다.
자바도 이와 마찬가지다.
self
를 this
라고 생각하면, this
도 항상 암묵적으로 메소드의 인자로 전달되는 것이다.
// ...
public void print(User this) {
System.out.println(this);
}
// ...
그래서 위 코드에서 아무 인자도 받지 않던 print
메소드에 User
타입의 변수 this
를 첫 번째 인자로 전달하더라도 아무런 오류 없이 실행된다.
암묵적으로 전달하던 것을 명시한 것일 뿐이기 때문이다.
// The receiver should be the first parameter
public void print(int a, User this) {
System.out.println(this);
}
그러나 this
를 첫 번째 인자가 아니라 뒤 쪽에 놓는다면 위와 같은 오류가 발생한다.
receiver
는 반드시 첫 번째 인자가 되어야 한다는.
this
를 Receiver라고 부르는데, Go의 그것과 용어가 같다.
자바는 this
, rust는 self
, Go는 항상 다르지만 어쨌든 이걸 Receiver라고도 부르는가보다.
참고로 위 rust 코드에서 user.print();
, User::print(user);
둘 중 하나는 주석 처리를 해야 정상 작동한다.
안 그러면 소유권 시스템 때문에 오류가 발생할 것이다.