[Kotlin] 함수는 뭐고 메서드는 뭐고 프로퍼티는 뭐고 필드는 무엇인가

Jihoon Oh·2023년 3월 1일
1
post-thumbnail
post-custom-banner

코틀린을 사용한지도 어느덧 3개월이 다 되어 갑니다. 자바에서 코틀린으로 처음 넘어와서 보통 궁금해 하는것이 무엇일까요? 여러가지가 있겠지만 같은 JVM 계열 언어임에도 비슷한 요소의 용어가 다른 점도 그 중 하나일 것 같습니다.

함수 vs 메서드

보통 언어들은 어떠한 동작을 하는 요소, 무언가를 매개변수로 받아서 무언가를 반환하는 것을 함수(function)이라고 부릅니다. 좀 더 수준있게 정의해보자면 하나의 특별한 목적의 작업을 수행하기 위해 독립적으로 설계된 프로그램 코드의 집합 이라고 할 수 있겠네요. 그런데 우리는 자바로 프로그래밍을 하면서는 함수라고 부르지 않습니다. 메서드라고 부르죠.

그런데 코틀린으로 넘어오니 다시 함수라고 부릅니다. 애초에 선언하는 키워드 자체도 fun(이거 설마 재미있다는 뜻의 fun하고 노린건가요?) 입니다.

void foo() {
    System.out.println("나는 자바");
}
fun foo() {
    println("나는 코틀린")
}

그렇다보니 메서드라는 명칭이 입에 익어 코틀린으로 넘어왔을 때에도 메서드라고 부르는 경우가 많습니다. 실제로 저희 회사 분들도 다들 메서드라고 부르시는 경우가 많더라고요.

그렇다면 함수와 메서드는 무슨 차이가 있을까요? 그냥 각 언어마다 부르는 명칭의 차이만 있는 걸까요?

이걸 알아보기 위해서는 코틀린과 자바의 함수 구조에 대해 알아봐야 합니다.

다들 잘 아시겠지만, 자바에서 메서드를 정의하기 위해서는 클래스 안쪽이어야 합니다. 클래스 바깥, 즉 최상위 레벨에는 메서드를 정의할 수 없습니다. 또한 그러한 메서드들을 호출하기 위해서는 인스턴스가 필요합니다. 심지어 정적 메서드 조차도 선언할 때는 클래스 안에 선언해야 합니다. 때문에 메서드는 다음과 같이 생각할 수 있습니다.

객체와 연관된 함수
_Method is a function associated to an object.

다만 정적 메서드는 객체 인스턴스를 생성하지 않고도 호출할 수 있고, 메모리에 저장되는 것도 힙이 아닌 스태틱 영역에 할당되며 단지 클래스 아래에 선언되기만 할 뿐이라 이것을 과연 메서드라 볼 수 있을지, 그저 함수일 뿐이 아닌지 다른 시선으로 볼 수도 있긴 합니다.

결국 생각해보면 메서드도 곧 함수입니다. 단지 객체와 연관된 함수일 뿐인 것이죠. 그래서 사실 자바의 메서드를 함수라 불러도 전혀 틀린 이야기가 아닌 것 같습니다. 그러면 반대로 코틀린은 자바에서 파생된 언어이고 함수들과 객체가 연관되어 있으니 메서드라고 부르면 되지 않을까요?

하지만 코틀린은 꼭 클래스 안이 아니더라도 함수를 정의할 수 있다는 점이 다릅니다. 자바의 메서드에 해당하는 클래스의 멤버 함수를 제외하고도 최상위 레벨에도 함수를 정의할 수 있고, 심지어 함수 안에 지역 함수를 정의할 수 있기도 합니다. 그래서 메서드라고 통칭할 수 없으니 함수라고 부르는 것으로 보입니다. (어쨌든 메서드 포함 모두 함수니까요.)

프로퍼티 vs 필드

함수 vs 메서드 외에도 또 하나의 용어 차이가 있습니다. 바로 프로퍼티와 필드입니다. 클래스의 멤버 변수를 자바에서는 필드(field)라고 부릅니다. 하지만, 코틀린에서는 필드 대신 프로퍼티(property)라고 합니다.

저는 처음에 이름만 다르게 부른다고 생각했었는데, 사실은 함수 vs 메서드 처럼 조금 차이가 있다고 합니다. 함수가 메서드를 포함하고 있듯, 프로퍼티도 필드를 포함하고 있습니다. 다만 논리적 포함관계가 아니라 실제로 포함하고 있는 것입니다.

프로퍼티는 다음과 같이 구성됩니다.

  • 필드
  • getter
  • setter

다음 예제를 봅시다.

class Person(
    val name: String
)

val ohzzi = Person("오찌")
println(ohzzi.name) // getter 호출

저는 getter를 따로 정의하지 않았습니다. 그냥 프로퍼티 name만 정의했을 뿐입니다. 하지만 인스턴스 변수명.프로퍼티명으로 getter를 호출할 수 있습니다. 이것으로 보아, 프로퍼티는 getter를 포함하고 있습니다.

setter도 마찬가지입니다.

class Person(
    val name: String
    var age: Int
)

val ohzzi = Person("오찌", 26)
ohzzi.age = 27
println(ohzzi.age) // setter 호출

인스턴스 변수명.프로퍼티명 = 변경할 값으로 setter를 호출할 수 있습니다.

저는 처음에는 그냥 필드를 public으로 연 것이 아닌가 라고 생각을 했는데, 자바로 디컴파일 해보면 다음과 같은 형태의 코드가 됩니다.

public class Person {
    private final String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public String getName() {
        return name;
    }
    
    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        this.age = age;
    }
}

필드 자체는 private으로 유지되고, val이냐 var냐에 따라 getter와 setter가 작성되는 것을 볼 수 있습니다.

그리고 코틀린 프로퍼티는 backing field라는 녀석도 가지고 있습니다. 아까 프로퍼티가 필드 + getter + setter라고 말씀드렸죠? backing field는 프로퍼티의 접근자, 즉 getter와 setter에서 필드 값을 참조하기 위해 field 키워드를 통해 접근하는 녀석입니다. 그냥 자바로 디컴파일 했을 때의 필드라고 생각하면 이해하기 쉽습니다.

class Counter {
    var count = 0
        set(value) {
            if (value >= 0) field = value
        }
}

위 예제는 count 프로퍼티의 setter를 재정의 하는 로직입니다. 이 때 setter 안에서 count라는 프로퍼티의 필드를 호출하기 위한(할당을 해주려면 당연하겠죠?) 방법이 필요한데, field 키워드를 사용합니다. 이 키워드를 통해 count 프로퍼티 내부의 필드에 접근할 수 있는 것입니다. 이런 방식은 접근자, 즉 getter와 setter에서만 사용할 수 있습니다.


이렇게 함수, 메서드, 프로퍼티, 필드에 대해 간단히 알아보았습니다. 제가 원래 개념 정리 글을 잘 안쓰려고 하는데, 자꾸 용어를 혼용해서 쓰던 차에 정리를 한 번 하는게 낫겠다 라는 생각에 적어보게 되었네요. 우리 올바른 용어를 사용하도록 합시다 :)

참고 자료

Kotlin programmer dictionary: Function vs Method vs Procedure
Kotlin Programmer Dictionary: Field vs Property

profile
Backend Developeer
post-custom-banner

1개의 댓글

오찌 멋있어요!!!

답글 달기