[Java] Method를 호출할 때 this가 항상 존재하는 이유

zxcev·2023년 4월 10일
0

Java

목록 보기
3/3
post-thumbnail

자바에서는 모든 것이 클래스다.
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를 인자로 전달해주었기 때문에 selfuser` instance가 들어간다는 사실을 코드상에서 좀 더 명확히 보여준다.

그러나 방법1처럼 user.print();를 호출하더라도 self라는 변수 안에 user instance가 암묵적으로 전달된다.

자바도 이와 마찬가지다.

selfthis라고 생각하면, 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);
둘 중 하나는 주석 처리를 해야 정상 작동한다.

안 그러면 소유권 시스템 때문에 오류가 발생할 것이다.

0개의 댓글