Обязательный именованный параметр с проверкой при компиляции
От: vsb Казахстан  
Дата: 19.06.23 16:27
Оценка:
У меня есть метод, которому я хочу передать несколько параметров. Типы у параметров могут совпадать. Я хочу это сделать таким образом, чтобы:

1. При изменении сигнатуры метода компилятор должен проверить все места вызова и убедиться, что они исправлены. К примеру добавление параметра.

2. При вызове нельзя незаметно перепутать два параметра с одинаковым типом.

По-человечески это делается, если есть фича именованные параметры. К примеру

function print({x, y}: {x: number, y: number}) {
    console.log(x, y);
}

const x1 = 1, y1 = 2;
print({x: x1, y: y1});


В Java такой фичи нет.

Если мы делаем просто вызов метода, то нарушается пункт 2. К примеру я могу написать test(y1, x1) и из этого будет совершенно не очевидно, что я написал ерунду.

Можно сделать объект с полями (parameter object). Но тут нарушается пункт 1. Если мы добавляем новое поле, то компилятор не проверит, что мы его инициализировали.

В голову приходит два варианта:

Вариант 1: избавиться от условия того, что типы у параметров могут совпадать. Т.е. завести типы-обёртки для каждого аргумента.

    static class X {
        final int x;

        X(int x) {
            this.x = x;
        }

        static X x(int x) {
            return new X(x);
        }
    }

    static class Y {
        final int y;

        Y(int y) {
            this.y = y;
        }

        static Y y(int y) {
            return new Y(y);
        }
    }
    
    static void print(X x, Y y) {
        System.out.println(x.x + " " + y.y);
    }

    public static void main(String[] args) {
        int x1 = 1, y1 = 2;
        print(x(x1), y(y1));
    }


Вариант 2: сделать хитрый билдер.

    static class PrintArgs0 {
        static PrintArgs0 printArgs() {
            return new PrintArgs0();
        }
        
        PrintArgs1 x(int x) {
            return new PrintArgs1(x);
        }
    }
    
    static class PrintArgs1 {
        private final int x;

        PrintArgs1(int x) {
            this.x = x;
        }
        
        PrintArgs y(int y){
            return new PrintArgs(x, y);
        }
    }
    
    static class PrintArgs {

        private final int x;
        private final int y;

        public PrintArgs(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }
    
    static void print(PrintArgs args) {
        System.out.println(args.x + " " + args.y);
    }

    public static void main(String[] args) {
        int x1 = 1, y1 = 2;
        print(printArgs().x(x1).y(y1));
    }


Оба варианта слегка безумны. Даже с кодогенератором плодить класс на каждый параметр это чересчур.

Может я не вижу чего-то очевидного?
Отредактировано 19.06.2023 16:28 vsb . Предыдущая версия . Еще …
Отредактировано 19.06.2023 16:27 vsb . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.