openlang

Java에서 옮긴 헬퍼가 Groovy에서 다른 오버로드를 부른다

Java 헬퍼 클래스를 빌드 스크립트 쪽 Groovy로 옮겼더니 같은 호출이 다른 메서드로 갔습니다. 순서대로 정리하면 이렇습니다.

class Printer {
    void show(Object o) { println 'object' }
    void show(String s) { println 'string' }

    void run(Object x) { show(x) }
}

new Printer().run('hi')

Java에서는 x의 선언 타입이 Object이므로 컴파일 시점에 show(Object)가 정해지고 object가 출력됩니다. Groovy는 실행 시점에 실제 인자 타입으로 메서드를 고르므로 같은 코드가 string을 출력합니다. 문서에도 multi-methods라는 이름으로 나와 있는 동작이라 버그는 아닙니다.

헷갈리는 건 세 번째 경우입니다. 클래스에 @CompileStatic을 붙이면 다시 object가 나옵니다. 즉 같은 소스가 어노테이션 하나로 다른 메서드를 부릅니다. 오버로드를 타입별로 나눠 둔 헬퍼가 몇 개 더 있어서, @CompileStatic을 파일 단위로 붙이는지 클래스 단위로 붙이는지, 아니면 오버로드 자체를 없애는 게 맞는지 궁금합니다.

이어지는 대화 3

서로의 근거를 따라
  1. 고유 링크

    결정하는 건 show를 선언한 클래스가 아니라 호출하는 코드의 컴파일 방식입니다. 예제에서는 둘이 같은 클래스라서 드러나지 않습니다. 두 오버로드를 @CompileStatic 유틸리티 클래스로 옮기고 동적 스크립트에서 부르면, 스크립트 쪽 호출은 여전히 실행 시점에 디스패치되어 string이 출력됩니다. 어노테이션은 호출하는 코드를 덮어야 합니다.

    그래서 클래스 단위는 호출하는 쪽이 그 클래스 안에 있을 때만 도움이 됩니다. 빌드 스크립트에서 부르는 헬퍼라면 저는 어노테이션으로 피해 가기보다 모호함 자체를 없애겠습니다. 하위 타입 하나로만 갈리는 두 오버로드가 정확히 정적 선택과 동적 선택이 갈라지는 지점입니다. 이름을 둘로 나누거나, 메서드 하나가 타입으로 직접 분기하면 두 방식 모두에서 같은 답이 나오고, 비용은 호출하는 자리의 이름이 조금 길어지는 정도입니다.

  2. 고유 링크

    저도 이름을 나누는 쪽이에요. 다만 방법이 하나 더 있어요. Groovy의 switch는 원래 클래스로 case를 걸 수 있어서, 디스패치에 기대지 않고도 실행 시점 타입으로 분기할 수 있어요.

    void show(Object o) {
        switch (o) {
            case String:
                println 'string'
                break
            default:
                println 'object'
        }
    }

    메서드가 하나뿐이라 고를 오버로드가 없으니 @CompileStatic이 있든 없든 string이 나와요. 나중에 Java로 다시 옮길 일이 생겨도 거의 한 줄씩 instanceof 패턴으로 바뀌고요. 메서드가 둘에서 하나로 줄어드는 것도 저한텐 그 자체로 장점이에요.

같은 질문의 다른 갈래

  1. 측정 단위를 값싼 ID로 쓰기

    정수 ID에 단일 케이스 유니온 대신 측정 단위를 써 보고 있는데, 더 퍼지기 전에 다른 의견을 듣고 싶어요. CustomerId of int는 [<Struct>]를 붙이지 않으면 할당이 생기고, 쓸 때마다 생성자나 매치를…

    답글 1fsharp
  2. 빈 셀은 0과도 같고 빈 문자열과도 같다

    물려받은 매크로가 수량이 0인 주문 행을 표시하는데, 빈 행까지 전부 표시하더라고요. 검사 코드는 If ws.Cells(r, 4).Value = 0 Then이었어요. 빈 셀은 Empty가 든 Variant를 돌려주고, Emp…

    답글 7vba
  3. 판별 유니온 전에 JSON 검증하기

    타입 단언은 컴파일러에게 이렇게 믿으라고 말할 뿐이에요. 외부 응답은 바이트로 시작하고, 누군가 확인하기 전까지는 그게 어떤 변형인지 알려 주는 게 하나도 없어요. 텍스트 디코딩과 JSON 파싱이 먼저 오고, 둘 다 각자 실…