Универсальный тип Java, связанный с классами с определенной аннотацией - PullRequest
0 голосов
/ 02 ноября 2019

В Java можно привязать универсальный параметр к классам, реализующим конкретный интерфейс, поэтому возможно следующее

interface MyInterface {}

class MyClassA implements MyInterface {}

class MyBoundedClassA<T extends MyInterface>

Что теперь, если вместо интерфейса я бы хотел привязать параметр к классу, аннотированному с помощью конкретногоаннотация, например:

interface @MyAnnotation {}

@MyAnnotation
class MyClassB {}

class MyBoundedClassB<T extends MyAnnotation> // NOT possible

Можно ли добиться такого поведения в Java?

---- EDIT

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

Существует хорошо известная библиотека Джексона для сериализации объектов. Эта библиотека не поддерживает сериализацию ключей карты, кроме Strings, поэтому следующее невозможно из коробки

class TimeRange {
  LocalDateTime startDate;
  LocalDateTime endDate;

}

class SportsmenActivities {
  private Map<String, <TimeRange, List<Activity>>  sportActivities;
}

В этом примере ключом внешней карты является "sportsmanCode" liek "andy", "mike"," Джон ". Внутренняя карта содержит действия, выполненные данным спортсменом в течение указанного периода.

Итак, скажем, Энди, бегал трусцой в течение одного дня, тогда запись была бы такой:

new SportsmanActivities().get("andy").put(TimeRange.of('2012-12-01,'2012-12-02'), List.with(new JoggingActivity)) // did some pseudo code here for readablity

СейчасКак сказал Джексон, он не будет сериализовать это из коробки, поэтому я написал универсальный модуль, который позволяет сериализовать такую ​​сложную карту.

Чтобы использовать это, вам нужно аннотировать ваш «ключевой» класс следующим образом:

@KeySerializable
class TimeRange {
  @MyMapKey
  LocalDateTime startDate;
  @MyMapKey
  LocalDateTime endDate;

}

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

Теперь у меня есть реализация класса Джексона, который динамически сериализует все, что передается как текстовый ключ карты"аннотировано @KeySerializable. Сигнатура выглядит следующим образом:

    class MyMapKeySerializer<T> extends JsonSerializer<T> {
      serialize (T keyToSerialize) { 
      // do magic 
      }

   }

Это работает, но я бы хотел ограничить использование T только классами, помеченными @KeySerializable, поскольку только для таких классов этот метод имеет смысл. В идеале это будет что-то вроде:

   class MyMapKeySerializer<T annotatedWith @KeySerializable> extends JsonSerializer<T> {
      serialize (T keyToSerialize) { 
      // do magic 
      }

   } 

Ответы [ 2 ]

3 голосов
/ 02 ноября 2019

Если ваша цель состоит в том, чтобы утверждать, что принимаются только аннотированные классы, у вас есть несколько вариантов обхода:

  1. Напишите процессор обработки аннотаций, который выполняет утверждение во время компиляции (посмотрите, как работает @NonNull и т. Д.),Это интересная работа, но нетривиальная, так как система компиляции / типа является совершенно новой для многих опытных разработчиков Java.
  2. Используйте некоторую форму AOP (AspectJ, Spring AOP и т. Д.), Чтобы "посоветовать" все аннотированные методы сДекоратор, ответственный за утверждение параметра, имеет такую ​​же аннотацию.
  3. Явная проверка во время выполнения с использованием parameter.getClass().isAnnotationPresent(MyAnnotation.class)
1 голос
/ 06 ноября 2019

Инструмент, подобный Checker Framework , подключается к компилятору для ограничения общего создания экземпляров способом, аналогичным тому, который вы запрашивали. Он реализован как процессор аннотаций и дает гарантию правильного использования во время компиляции.

Например, , вы можете написать class MyList<T extends @NonNull Object> {...}.

The CheckerFramework позволяет вам создать свой собственный контролер , который применяет любые правила, которые вам нравятся в @KeySerializable. В вашем случае правила могут быть настолько простыми, что вы можете просто определить пару определителей типов и использовать Проверка подтипов - по крайней мере, на первый взгляд.

Обратите внимание, что для Checker Frameworkдля работы с аннотацией @KeySerializable эта аннотация должна быть аннотацией типа, а не аннотацией объявления.

...