AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 79473492
Accepted
Cilenco
Cilenco
Asked: 2025-02-28 01:33:52 +0800 CST2025-02-28 01:33:52 +0800 CST 2025-02-28 01:33:52 +0800 CST

Obter propriedade de TypeElement no Processador de Anotação

  • 772

Na minha empresa temos muitos Enums que implementam uma interface como esta:

public interface DomainEnum {
    String getDomainId();
}

@Getter
@AllArgsConstructor
public enum ExampleEnum implements DomainEnum {
  A("1"), B("2"), C("3");

  private final String domainId;
}

Gostaria de usar o MapStruct para mapear Strings para qualquer Enum implementado DomainEnumusando o domainId. Nos documentos, vi que você pode implementar uma estratégia de nomeação de enum personalizada que descreve exatamente o que eu quero. Meu problema atual é que não tenho certeza de como obter o domainIddo enum. Este é meu código até agora:

public class CustomEnumMappingStrategy extends DefaultEnumMappingStrategy {

  // other methods like in example from docs
 
  protected String getCustomEnumConstant(TypeElement enumTypeElement, String enumConstant) {
    var enumValue = getEnumValue(enumTypeElement, enumConstant);
    // How can I now get the domainId from the ElementKind?
  }

  protected ElementKind getEnumValue(TypeElement enumTypeElement, String enumConstant) {
    return enumTypeElement.getEnclosedElements().stream()
      .filter(it -> it.getKind().equals(ElementKind.ENUM_CONSTANT))
      .filter(it -> it.toString().equals(enumConstant))
      .findFirst()
      .orElse(null);
  }
}

Então, como eu poderia começar domainIddo ElementKindagora para finalizar minha estratégia de mapeamento?

java
  • 1 1 respostas
  • 35 Views

1 respostas

  • Voted
  1. Best Answer
    rzwitserloot
    2025-02-28T02:21:47+08:002025-02-28T02:21:47+08:00

    O Processamento de Anotações é limitado somente a assinaturas. Para definições de constantes enum, ele termina com o nome.

    • Não é possível inserir a ("1")peça A("1"), em um processador de anotações.
    • Não é viável carregar a classe enum e passar pelos valores com eg ExampleEnum.values()para então chamar .getDomainId()cada valor por vez de dentro do seu processador de anotações: Você executa durante a compilação , o ExampleEnumé apenas um arquivo de origem neste ponto, ele ainda não existe como um arquivo de classe. Mesmo que existisse, carregar classes arbitrárias durante a compilação é um problema de ovo e galinha e, portanto, uma ideia bastante ruim que tornará seu código muito frágil. Por exemplo, se um de seus enums usar um tipo não Java-core em qualquer lugar, por exemplo, um método public SomeTypeYouDefined getFoo()? A compilação explode; seu código não pode mais ser compilado.

    Assim chegamos à resposta à sua pergunta: Infelizmente, o que você quer é impossível .

    Outro problema é que sua "definição" do que você quer é fundamentalmente impraticável.

    Por exemplo, aqui está uma implementação correta do seu sistema:

    public enum MyHackyEnum implements DomainEnum {
      A, B, C;
    
      @Override public String getDomainId() {
        return "foo-" + LocalDateTime.now();
      }
    }
    

    Você não terá um erro de compilador ou um erro de tempo de execução, isso 'simplesmente funcionará', mas obviamente qualquer tentativa em tempo de compilação de criar um mapeamento de todos os IDs de domínio falhará completamente. Esse é um lugar ruim para se estar.

    Eu sugiro fortemente que você pare com esse plano. Ele simplesmente não "se encaixa". Fazer coisas em tempo de compilação onde uma das entradas depende de implementações de um método decretado por uma interface nunca é uma boa ideia por esse exato motivo.

    É isso que você realmente quer.

    Anote as próprias constantes enum!

    > cat Test.java
    
    import java.lang.annotation.*;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    @interface DomainId {
      String value();
    }
    
    interface DomainEnum {
      public default String getDomainId() {
         try {
           Enum<?> self = (Enum<?>) this;
           return self.getDeclaringClass().getField(self.name()).getAnnotation(DomainId.class).value();
         } catch (ClassCastException e) {
           throw new IllegalStateException("Only enums must implement DomainEnum. Class " + this.getClass() + " violates this rule");
         } catch (NoSuchFieldException e) {
           throw new RuntimeException("Unexpected - an enum value isnt a field in its own def?", e);
         }
      }
    }
    
    enum MyDomainEnum implements DomainEnum {
      @DomainId("1") A,
      @DomainId("2") B,
      @DomainId("3") C,
    }
    
    public class Test {
      public static void main(String[] args) {
        System.out.println(MyDomainEnum.A.getDomainId());
      }
    }
    
    > javac Test.java
    > java Test
    1
    

    Este trecho é independente, basta inseri-lo Test.javae seguir em frente.

    O snippet mostra como você pode acessar os IDs de domínio em tempo de execução. Acessá-los em tempo de anotação seria um exemplo autônomo significativamente mais complexo, mas é lógica básica de processamento de anotação. Acione na anotação e inspecione o 'elemento' que a anotação está anotando. Os valores Enum são tratados como campos para esse propósito, e você pode obter o nome deles em seu processador de anotação muito bem.

    NB: O exemplo acima precisa de algum desenvolvimento; detectar que a DomainIdanotação está faltando e produzir uma exceção apropriada, por exemplo. Seu processador de anotação deve detectar quaisquer enums que implementam, DomainEnummas não têm uma @DomainIdanotação em cada constante que definem e emitir um erro. Isso é coisa básica de AP e não requer truques especiais: você pode 'obter' todos os arquivos de origem sendo compilados como AP facilmente (inscreva-se *como uma anotação para obtê-los), você pode perguntar por todos os tipos de nível superior neles, então filtrá-los para enums, então filtrar todos os enums que não implementam DomainEnume partir daí.

    Como o 'ID de domínio' é transmitido por meio de um parâmetro de anotação, o próprio compilador forçará os autores de quaisquer enumerações de domínio a tornar esses valores constantes, eliminando assim completamente o exemplo complicado.

    NB: O Lombok hackeia diretamente todo o AST, mas o lombok não é um processador de anotações e tem vários caminhos de código para as várias implementações AST comumente usadas por aí. Você provavelmente não quer seguir esse caminho, mas se realmente quiser, pode bifurcar o lombok e adicionar um processador lombok (você não precisa de uma anotação; os plugins do lombok veem todo o código e, portanto, podem decidir apenas escanear os tipos de enumeração que implementam DomainEnume procurar por eles). Mas você ainda teria que ditar, conforme a documentação, que todas as enumerações de domínio DEVEM ter seu ID de domínio como primeiro parâmetro de cada valor constante de enumeração. Eu seguiria o plano acima, muito mais simples e melhor.

    • 6

relate perguntas

  • Lock Condition.notify está lançando java.lang.IllegalMonitorStateException

  • Resposta de microsserviço Muitos para Um não aparece no carteiro

  • Validação personalizada do SpringBoot Bean

  • Os soquetes Java são FIFO?

  • Por que não é possível / desencorajado definir um lado do servidor de tempo limite de solicitação?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Reformatar números, inserindo separadores em posições fixas

    • 6 respostas
  • Marko Smith

    Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não?

    • 2 respostas
  • Marko Smith

    Problema com extensão desinstalada automaticamente do VScode (tema Material)

    • 2 respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Martin Hope
    Fantastic Mr Fox Somente o tipo copiável não é aceito na implementação std::vector do MSVC 2025-04-23 06:40:49 +0800 CST
  • Martin Hope
    Howard Hinnant Encontre o próximo dia da semana usando o cronógrafo 2025-04-21 08:30:25 +0800 CST
  • Martin Hope
    Fedor O inicializador de membro do construtor pode incluir a inicialização de outro membro? 2025-04-15 01:01:44 +0800 CST
  • Martin Hope
    Petr Filipský Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não? 2025-03-23 21:39:40 +0800 CST
  • Martin Hope
    Catskul O C++20 mudou para permitir a conversão de `type(&)[N]` de matriz de limites conhecidos para `type(&)[]` de matriz de limites desconhecidos? 2025-03-04 06:57:53 +0800 CST
  • Martin Hope
    Stefan Pochmann Como/por que {2,3,10} e {x,3,10} com x=2 são ordenados de forma diferente? 2025-01-13 23:24:07 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve