Java: Поддерживает ли псевдоним типа инструмент обработки аннотаций (APT)?

1

Я никогда не работал с псевдонимами типа, но эта концепция, по-видимому, очень полезная функция, добавляющая семантику над теми же типизированными объектами и защитой от общих опечаток.

Скажем, существует void foo(float volume, float weight). Это нормально, если он вызывается следующим образом: foo(v, m), но foo(m, v) не является очевидной опечаткой. void process(Iterable<File> javaPath, Iterable<File> classPath) может быть другим вариантом использования. К сожалению, в Java нет псевдонимов типов, и некоторые обходные пути являются истинным излишеством:

  • объединение одного поля в класс (боксирование примитива в объект, объект в объекте, имеющем дополнительную ссылку, более сложные правила сериализации/десериализации)
  • расширяя один класс от другого (например, как создать некоторый псевдоним типа переменной в Java - невозможно для примитивов, классы могут быть окончательными или иметь недоступный конструктор).

Таким образом, у обоих из них есть недостатки и затраты времени исполнения/производительности.

Насколько я могу судить, в Java время на основе "псевдонимов типа" может быть заменено проверками времени компиляции, например, обрабатываются @NotNull и @Nullable. Существуют ли какие-либо статические проверки типа/псевдонимы/инструменты APT, поддерживающие такие конструкции, как void foo(@Volume float volume, @Weight float weight), чтобы контролер мог проверять такую "безопасность типа" во время компиляции и требовать прохождения переменных/констант и литералы, которые должны быть аннотированы (на сайтах с объявлениями и вызовами)?


Этот вопрос также поднят, потому что я в настоящее время работаю над инструментом обработки кода Java-исходного кода, который может использовать несколько back-end (в настоящее время только JDT). Пока я хочу, чтобы это был JDT-агностик, я хотел бы иметь бизнес-объекты для типов, методов и полей. Несмотря на это, я теряю много информации из JDT AST, лучший текущий подход для моего инструмента - использование строк (это позволяет выполнить некоторый анализ, достаточный для сферы применения инструмента). Но такие методы, как void process(String type, String field, String method), путают, поэтому я создал Type, Field и Method которые представляют объекты домена и являются JDT-агностиками. Они в порядке, но я не ожидаю, что они будут расширены в будущем (все эти три типа имеют одно private final String name; поле + мне пришлось переопределить hashCode/equals и toString). Это снова привело меня к идее проверки типа APT. До тех пор, пока нормально обрабатывать необработанные данные, достаточно будет иметь что-то вроде void process(@Type String type, @Field String field, @Method String method) (я все еще могу использовать один объект).

  • 0
    Я не понимаю Если я звоню foo(a,b) , для которых одновременно и Ь int , как инструменты предполагается проверить , если мой является a Volume ? Если вам нужно, чтобы Volume имел смысловое значение, сделайте его классом.
  • 0
    @AdrianShum Мне просто нужно больше семантики над поплавком. Включение одного примитива в класс приводит к созданию другого экземпляра класса (больше кучи), требует больше адаптеров в сериализаторах / десериализаторах (например, GSON не работает для нового класса - требуется больше адаптеров). Идея такая же, как идея NotNull и Nullable - вы не создаете классы просто для добавления семантики, вы аннотируете, верно? .NET поддерживает типы значений, которые могут легко содержать примитивы без снижения производительности (у него нет примитивов как таковых, все является истинным объектом). Но в Java нет типов значений.
Показать ещё 7 комментариев
Теги:
type-alias
annotation-processing
type-safety

1 ответ

0

Если ваша проблема выглядит неоднозначно, когда вы вызываете foo(v,m) что люди могут ошибочно писать foo(m,v) без каких-либо предупреждений или видимых подсказок об ошибке, вы можете использовать шаблон с быстрым построением:

(Код не проверен, просто написал, чтобы продемонстрировать идею)

public class MyClass {
    public class FooStub {
        int volume;
        int weight;
        public FooStub withVolume(int v) {
            this.volume = v;
            return this;
        }
        public FooStub withWeight(int w) {
            this.weight = w;
            return this;
        }
        public int run() {   // or call it getResult() or whatever you want
            Assert.notNull(volume);
            Assert.notNull(weight);
            return foo(volume,weight);  // calling method of outer class
        }
    }

    public int foo(int volume, int weight) {   // you even hide this if you want
        return volume * weight;
    }
    public FooStub foo() {
        return new FooStub();
    }

}

Таким образом, вызывающий может сделать

MyClass myObj = ....;
int result = myObj.foo().withVolume(a).withWeight(b).run();
// which is the same as result = myObject.foo(a,b);
  • 0
    Теперь я вижу вашу точку зрения лучше, спасибо. Я предполагаю, что здесь возникает другая вещь: myObj.foo().withVolume(b).withWeight(a).run() - здесь a и b меняются местами случайно.
  • 0
    с надлежащим соглашением об именах, вы можете заметить это на глаз. С простым старым методом, если вы делаете myObj.foo(w,v); , он по-прежнему «выглядит» хорошо, в то время как myObj.foo().withVolume(w).withWeight(v).run() не
Показать ещё 7 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню