Introdução ao Java Lombok

1. O que é Lombok?

1. Endereço do site oficial

Clique para ir diretamente

2. O site oficial descreve Lombok da seguinte forma:

O Projeto Lombok é uma biblioteca java que se conecta automaticamente ao seu editor e cria ferramentas, apimentando seu java.
Nunca mais escreva outro método getter ou equals, com uma anotação sua classe tem um construtor completo, automatiza suas variáveis ​​de registro e muito mais.

Após um entendimento simples, vamos falar sobre o significado:

O projeto Lombok é na verdade uma biblioteca Java que pode ser inserida automaticamente em editores e construir ferramentas para melhorar o desempenho Java. No futuro, você só precisará de algumas anotações simples e não precisará mais escrever repetidamente get, equals e outros métodos na classe.

3. Vantagens


Lombok pode simplificar o código Java na forma de anotações simples e melhorar a eficiência do desenvolvimento dos desenvolvedores.

Por exemplo: Javabeans que muitas vezes precisam ser escritos durante o desenvolvimento requerem tempo para adicionar getters/setters correspondentes, e também podem precisar escrever construtores, iguais e outros métodos, e requerem manutenção. Quando há muitos atributos, um grande número de getter/ aparecerão métodos setter. Eles parecem muito extensos e não possuem muito conteúdo técnico. Uma vez modificados os atributos, é fácil cometer erros de esquecer de modificar os métodos correspondentes.

Lombok pode gerar automaticamente construtores, getters/setters, equals, hashcode, toString e outros métodos para propriedades em tempo de compilação por meio de anotações.

O maravilhoso é que não existem métodos getter e setter no código-fonte, mas existem métodos getter e setter no arquivo de bytecode compilado. Isso evita o trabalho de reconstruir manualmente esses códigos e faz com que o código pareça cada vez mais limpo.

2. Instalação e uso do Lombok

1. Instale o plug-in Lombok

O plug-in Lombok suporta IDEs como Eclipse, IntelliJ IDEA e MyEclispe. Aqui usamos o IntelliJ IDEA para demonstrar a instalação:

1) Abra Arquivo > Configurações > Plugins > Marketplace, pesquise Lombok, conforme mostrado na figura, clique em instalar, Aceitar aparecerá e reinicie o IDEA após a instalação.

2) Defina Ativar processamento de anotação

Abra Arquivo > Configurações > Compilação, Execução, Implantação > Compilador > Processadores de anotação, marque Habilitar processamento de anotação, em seguida, Aplicar e OK.

2. Crie um novo projeto Java e apresente o pacote jar

1. Existem dois métodos comumente usados ​​para introduzir pacotes jar, como segue:

Método 1: Para projetos comuns, você pode baixar diretamente o pacote jar do site oficial, basta importar o projeto como se estivesse usando o pacote jar comum.

endereço de download jar: clique para baixar

Método 2: O projeto Maven pode configurar coordenadas de dependência em pom.xml. As coordenadas de dependência são as seguintes:

<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.12</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

Perceber:

1. <scope>provided</scope>Isso significa que o pacote é usado apenas durante a compilação e teste. O pacote Lombok não será incluído quando o projeto for realmente empacotado.

2. Lombok também suporta outros métodos de construção, como Ant, Gradle e Kobalt. Se necessário, você pode consultar as Ferramentas de Construção no menu Instalar no site oficial. Para outros métodos de uso, você também pode consultar o menu Instalar .

2. Para obter instruções de uso, você pode consultar as informações oficiais ou verificar minha demonstração de código de anotação comum aqui.

Foco:

@ NonNull、@ Getter、@ Setter、@ AllArgsConstructor、@ NoArgsConstructor、@ ToString、@ Dados

1. Anotação oficial

1) Clique para visualizar estável

2) Clique para visualizar experimental

2. Documentação oficial da API

Clique para ver

1)@NonNull

Esta anotação é usada em propriedades ou construtores.Lombok irá gerar uma declaração não nula,que pode ser usada para verificar parâmetros e ajudar a evitar ponteiros nulos.

Código de amostra:

//成员方法参数加上@NonNull注解,构造方法也一样,在此不做演示
public String getName(@NonNull Person p){
    return p.getName();
}

O efeito real é equivalente a:

public String getName(Person p){
    if(p==null){
        throw new NullPointerException("person");
    }
    return p.getName();
}

2) @Gettere@Setter

Quando usada na classe JavaBean ou JavaBean, usar esta anotação é equivalente a gerar métodos get e set correspondentes para variáveis ​​de membro. O modificador padrão do método é público. Você também pode usar AccessLevel para especificar o modificador de acesso para o método gerado.

Essas duas anotações também podem ser usadas diretamente nas classes para gerar métodos get e set correspondentes para todas as variáveis ​​de membro não estáticas nesta classe.

Código de amostra:

public class Student{
    @Getter
    @Setter
    private String name;
 
    @Setter(AccessLevel.PROTECTED)
    private int age;
 
    @Getter(AccessLevel.PUBLIC)
    private String language;
}

O efeito real é equivalente a:

public class Student{
    private String name;
    private int age;
    private String language;
 
    public void setName(String name){
        this.name = name;
    }
 
    public String getName(){
        return name;
    }
 
    protected void setAge(int age){
        this.age = age;
    }
 
    public String getLanguage(){
        return language;
    }
}

3)@Cleanup

Esta anotação é usada na frente de uma variável para garantir que o recurso representado por esta variável será fechado automaticamente . O padrão é chamar o método close() do recurso. Se o recurso tiver outros métodos de fechamento, você pode usar @Cleanup ("methodName") para especificar o método a ser chamado.

Código de amostra:

public static void main(String[] args) throws IOException {
     @Cleanup 
     InputStream in = new FileInputStream(args[0]);
     @Cleanup 
     OutputStream out = new FileOutputStream(args[1]);
     byte[] b = new byte[1024];
     while (true) {
       int r = in.read(b);
       if (r == -1) break;
       out.write(b, 0, r);
     }
 }

O efeito real é equivalente a:

public static void main(String[] args) throws IOException {
     InputStream in = new FileInputStream(args[0]);
     try {
       OutputStream out = new FileOutputStream(args[1]);
       try {
         byte[] b = new byte[10000];
         while (true) {
           int r = in.read(b);
           if (r == -1) break;
           out.write(b, 0, r);
         }
       } finally {
         if (out != null) {
           out.close();
         }
       }
     } finally {
       if (in != null) {
         in.close();
       }
    }
}

4)@ToString

Usado em JavaBean ou semelhante a JavaBean , o uso desta anotação reescreverá automaticamente o método toStirng correspondente . Por padrão, o nome da classe e todos os atributos serão gerados (na ordem de definição do atributo), separados por vírgulas e especificados pelo parâmetro callSuper Referindo-se à classe pai e definindo o parâmetro includeFieldNames como true, o atributo toString() pode ser exibido claramente.

<code>@ToString(exclude="column")</code>
Significado: Exclui o elemento correspondente à coluna coluna, ou seja, o parâmetro coluna não é incluído na geração do método toString;
<code>@ToString(exclude= {"column1", "column2"})</code>
Significado: Excluir elementos correspondentes a múltiplas colunas de coluna, separados por vírgulas em inglês, ou seja, múltiplos parâmetros de coluna não são incluídos na geração do método toString; <code>
@ToString (of="column")</code>
Significado: Gere apenas o método toString que contém os parâmetros do elemento correspondente à coluna coluna, ou seja, apenas o parâmetro coluna é incluído na geração do método toString;;
<code> @ToString(of={" column1″,”column2”})</code>
Significado: Gere apenas o método toString que contém os parâmetros dos elementos correspondentes a múltiplas colunas. O meio é separado por vírgulas no estado inglês, ou seja, ao gerar o método toString, ele contém apenas parâmetros de múltiplas colunas;

Código de amostra:

@ToString(exclude="id")
public class ToStringExample {
  private static final int STATIC_VAR = 10;
  private String name;
  private Shape shape = new Square(5, 10);
  private String[] tags;
  private int id;
  
  public String getName() {
    return this.getName();
  }
  
  @ToString(callSuper=true, includeFieldNames=true)
  public static class Square extends Shape {
    private final int width, height;
    
    public Square(int width, int height) {
      this.width = width;
      this.height = height;
    }
  }
}

O efeito real é equivalente a:

public class ToStringExample {
  private static final int STATIC_VAR = 10;
  private String name;
  private Shape shape = new Square(5, 10);
  private String[] tags;
  private int id;
  
  public String getName() {
    return this.getName();
  }
  
  public static class Square extends Shape {
    private final int width, height;
    
    public Square(int width, int height) {
      this.width = width;
      this.height = height;
    }
    
    @Override 
    public String toString() {
      return "Square(super=" + super.toString() + ", width=" + this.width + ", height=" + this.height + ")";
    }
  }
  
  @Override 
   public String toString() {
    return "ToStringExample(" + this.getName() + ", " + this.shape + ", " + Arrays.deepToString(this.tags) + ")";
  }
}

5)@EqualsAndHashCode

Por padrão,todas as propriedades não estáticas e não transitórias são usadas para gerar equals e hasCode.Algumas propriedades também podem ser excluídas através da anotação de exclusão.

Código de amostra:

@EqualsAndHashCode(exclude={"id", "shape"})
public class EqualsAndHashCodeExample {
  private transient int transientVar = 10;
  private String name;
  private double score;
  private Shape shape = new Square(5, 10);
  private String[] tags;
  private int id;
  
  public String getName() {
    return this.name;
  }
  
  @EqualsAndHashCode(callSuper=true)
  public static class Square extends Shape {
    private final int width, height;
    
    public Square(int width, int height) {
      this.width = width;
      this.height = height;
    }
  }
}

6 ) e@NoArgsConstructor@RequiredArgsConstructor@AllArgsConstructor

Essas três anotações são usadas nas classes:

O primeiro e o terceiro são fáceis de entender, que consistem em gerar um construtor sem parâmetros e um construtor contendo todos os parâmetros da classe;

A segunda anotação usa todas as variáveis ​​de membro da classe com a anotação @NonNull  ou modificação final para gerar o construtor correspondente. É claro que as variáveis ​​de membro não são estáticas. Além disso, se a classe contiver variáveis ​​de membro modificadas finais, não poderá ser anotada com @NoArgsConstructor .

Todas as três anotações podem especificar as permissões de acesso do construtor gerado. Ao mesmo tempo, a segunda anotação também pode usar a forma @RequiredArgsConstructor ( staticName = " methodName ") para gerar um método estático com um nome especificado e retornar um método estático gerado chamando o construtor correspondente.Object.

Código de amostra:

@RequiredArgsConstructor(staticName = "myShape")
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor
public class Shape {
    private int x;
    @NonNull
    private double y;
    @NonNull
    private String name;
}

O efeito real é equivalente a:

public class Shape {
    private int x;
    private double y;
    private String name;
 
    public Shape(){
    }
 
    protected Shape(int x,double y,String name){
        this.x = x;
        this.y = y;
        this.name = name;
    }
 
    public Shape(double y,String name){
        this.y = y;
        this.name = name;
    }
 
    public static Shape myShape(double y,String name){
        return new Shape(y,name);
    }
}

7)@Data

A anotação da classe gerará automaticamente os métodos setter/getter, equals, canEqual, hashCode e toString para todas as propriedades da classe. Se for uma propriedade final, nenhum método setter será gerado para a propriedade.

@ValueAs anotações são semelhantes, exceto que definirão todas as variáveis ​​de membro como modificações finais privadas por padrão e não gerarão um método definido.@Data

Os exemplos oficiais são os seguintes:

@Data public class DataExample {
  private final String name;
  @Setter(AccessLevel.PACKAGE) private int age;
  private double score;
  private String[] tags;
  
  @ToString(includeFieldNames=true)
  @Data(staticConstructor="of")
  public static class Exercise<T> {
    private final String name;
    private final T value;
  }
}

O efeito real é equivalente a:

public class DataExample {
  private final String name;
  private int age;
  private double score;
  private String[] tags;
  
  public DataExample(String name) {
    this.name = name;
  }
  
  public String getName() {
    return this.name;
  }
  
  void setAge(int age) {
    this.age = age;
  }
  
  public int getAge() {
    return this.age;
  }
  
  public void setScore(double score) {
    this.score = score;
  }
  
  public double getScore() {
    return this.score;
  }
  
  public String[] getTags() {
    return this.tags;
  }
  
  public void setTags(String[] tags) {
    this.tags = tags;
  }
  
  @Override public String toString() {
    return "DataExample(" + this.getName() + ", " + this.getAge() + ", " + this.getScore() + ", " + Arrays.deepToString(this.getTags()) + ")";
  }
  
  protected boolean canEqual(Object other) {
    return other instanceof DataExample;
  }
  
  @Override public boolean equals(Object o) {
    if (o == this) return true;
    if (!(o instanceof DataExample)) return false;
    DataExample other = (DataExample) o;
    if (!other.canEqual((Object)this)) return false;
    if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
    if (this.getAge() != other.getAge()) return false;
    if (Double.compare(this.getScore(), other.getScore()) != 0) return false;
    if (!Arrays.deepEquals(this.getTags(), other.getTags())) return false;
    return true;
  }
  
  @Override public int hashCode() {
    final int PRIME = 59;
    int result = 1;
    final long temp1 = Double.doubleToLongBits(this.getScore());
    result = (result*PRIME) + (this.getName() == null ? 43 : this.getName().hashCode());
    result = (result*PRIME) + this.getAge();
    result = (result*PRIME) + (int)(temp1 ^ (temp1 >>> 32));
    result = (result*PRIME) + Arrays.deepHashCode(this.getTags());
    return result;
  }
  
  public static class Exercise<T> {
    private final String name;
    private final T value;
    
    private Exercise(String name, T value) {
      this.name = name;
      this.value = value;
    }
    
    public static <T> Exercise<T> of(String name, T value) {
      return new Exercise<T>(name, value);
    }
    
    public String getName() {
      return this.name;
    }
    
    public T getValue() {
      return this.value;
    }
    
    @Override public String toString() {
      return "Exercise(name=" + this.getName() + ", value=" + this.getValue() + ")";
    }
    
    protected boolean canEqual(Object other) {
      return other instanceof Exercise;
    }
    
    @Override public boolean equals(Object o) {
      if (o == this) return true;
      if (!(o instanceof Exercise)) return false;
      Exercise<?> other = (Exercise<?>) o;
      if (!other.canEqual((Object)this)) return false;
      if (this.getName() == null ? other.getValue() != null : !this.getName().equals(other.getName())) return false;
      if (this.getValue() == null ? other.getValue() != null : !this.getValue().equals(other.getValue())) return false;
      return true;
    }
    
    @Override public int hashCode() {
      final int PRIME = 59;
      int result = 1;
      result = (result*PRIME) + (this.getName() == null ? 43 : this.getName().hashCode());
      result = (result*PRIME) + (this.getValue() == null ? 43 : this.getValue().hashCode());
      return result;
    }
  }
}

8)@SneakyThrows

Esta anotação é usada em métodos. Você pode agrupar o código no método com uma instrução try-catch, capturar a exceção e usar Lombok.sneakyThrow(e) para lançar a exceção no catch. Você pode usar a forma @SneakyThrows( Exception.class )

public class SneakyThrows implements Runnable {
    @SneakyThrows(UnsupportedEncodingException.class)
    public String utf8ToString(byte[] bytes) {
        return new String(bytes, "UTF-8");
    }
 
    @SneakyThrows
    public void run() {
        throw new Throwable();
    }
}

O efeito real é equivalente a:

public class SneakyThrows implements Runnable {
    public String utf8ToString(byte[] bytes) {
        try{
            return new String(bytes, "UTF-8");
        }catch(UnsupportedEncodingException uee){
            throw Lombok.sneakyThrow(uee);
        }
    }
 
    public void run() {
        try{
            throw new Throwable();
        }catch(Throwable t){
            throw Lombok.sneakyThrow(t);
        }
    }
}

9)@Synchronized

Esta anotação é usada em métodos de classe ou métodos de instância. O efeito é o mesmo da palavra-chave sincronizada. A diferença é que os objetos de bloqueio são diferentes. Para métodos de classe e métodos de instância, os objetos de bloqueio da palavra-chave sincronizada são o objeto de classe e este objeto da classe respectivamente, enquanto @Synchronized Os objetos de bloqueio são o objeto final estático privado LOCK e o objeto final privado lock. Claro, você também pode especificar o objeto de bloqueio. O exemplo também é muito simples:

public class Synchronized {
    private final Object readLock = new Object();
 
    @Synchronized
    public static void hello() {
        System.out.println("world");
    }
 
    @Synchronized
    public int answerToLife() {
        return 42;
    }
 
    @Synchronized("readLock")
    public void foo() {
        System.out.println("bar");
    }
}

O efeito real é equivalente a:

public class Synchronized {
   private static final Object $LOCK = new Object[0];
   private final Object $lock = new Object[0];
   private final Object readLock = new Object();
 
   public static void hello() {
     synchronized($LOCK) {
       System.out.println("world");
     }
   }
 
   public int answerToLife() {
     synchronized($lock) {
       return 42;
     }
   }
 
   public void foo() {
     synchronized(readLock) {
       System.out.println("bar");
     }
   }
 }

10)@Log

Esta anotação é usada em classes, que podem salvar a etapa de geração de objetos de log da fábrica de logs e realizar o registro diretamente. As anotações específicas variam de acordo com as diferentes ferramentas de registro. Ao mesmo tempo, você pode usar o tópico nas anotações para especificar o classe ao gerar objetos de log.nome.

As diferentes anotações de log estão resumidas abaixo:

O acima é a anotação e o seguinte é o efeito real:

@CommonsLog
private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);
@JBossLog
private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(LogExample.class);
@Log
private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());
@Log4j
private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class);
@Log4j2
private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
@Slf4j
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
@XSlf4j
private static final org.slf4j.ext.XLogger log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);

3. Por que o Lombok não é recomendado?

As vantagens do Lombok são óbvias. Ele pode nos ajudar a economizar muito código redundante. Na verdade, do meu ponto de vista pessoal, o Lombok não é recomendado em projetos de desenvolvimento Java, mas o Professor Pan ainda introduziu seu uso, porque em alguns existem tais cenários de uso na empresa. Vamos ver por que o Sr. Pan não recomenda o uso do Lombok. Quais são suas deficiências?
 

1) Altamente intrusivo, forçando companheiros de equipe

O uso do plug-in Lombok exige que os desenvolvedores instalem o plug-in correspondente no IDE. Não apenas você precisa instalá-lo, mas qualquer pessoa que desenvolva com você também precisa instalá-lo. Se alguém não instalou o plug-in, usar um IDE para abrir um projeto baseado no Lombok causará erros como método não encontrado, fazendo com que o projeto não seja compilado. Mais importante ainda, se o Lombok for usado em um pacote jar que definimos, todos os aplicativos que dependem desse pacote jar deverão instalar plug-ins, o que é muito intrusivo.

2) Depuração de código reduzida

O Lombok pode realmente ajudar a reduzir muito código, porque o Lombok ajudará a gerar muito código automaticamente. No entanto, esses códigos não são gerados até o estágio de compilação; portanto, durante o processo de desenvolvimento, muitos códigos estão faltando. Isso traz alguns problemas para a depuração de código: se quisermos saber a quais classes o método getter de um determinado atributo em uma determinada classe é referenciado, não é tão simples.

3) Afeta a atualização da versão

Lombok é muito intrusivo ao código, o que pode causar um grande problema, ou seja, afetará nossa atualização do JDK. De acordo com a atual frequência de atualização do JDK, uma nova versão será lançada a cada seis meses, mas como o Lombok é uma ferramenta de terceiros e é mantido por uma equipe de código aberto, sua velocidade de iteração não pode ser garantida. Portanto, se precisarmos atualizar para uma nova versão do JDK, isso será afetado se os recursos não forem suportados no Lombok. Outro possível problema é que as próprias atualizações do Lombok também serão restritas. Como um aplicativo pode depender de vários pacotes jar, e cada pacote jar pode depender de uma versão diferente do Lombok, isso leva à necessidade de arbitragem de versão no aplicativo, e sabemos que a arbitragem de versão do pacote jar não é tão fácil. a probabilidade de ocorrência de problemas também é muito alta.

4) Observe que há riscos em usá-lo

No processo de uso do Lombok, se você não compreender os princípios subjacentes de várias anotações, é fácil produzir resultados inesperados. Para dar um exemplo simples: sabemos que quando usamos @Data para definir uma classe, o método equals() será gerado automaticamente para nós. Mas se você usar apenas @Data em vez de @EqualsAndHashCode(callSuper=true), o padrão será @EqualsAndHashCode(callSuper=false).Neste momento, o método equals() gerado apenas comparará as propriedades da subclasse e não Para atributos herdados de uma classe pai, independentemente de os direitos de acesso ao atributo da classe pai estarem abertos ou não, podem ocorrer resultados inesperados.

5) Pode danificar o encapsulamento

Se você não tomar cuidado durante o uso, o encapsulamento do código será destruído até certo ponto. Como exemplo simples, definimos uma classe de carrinho de compras e usamos a anotação @Data:

@Data
public class ShoppingCart { 
    //商品数目
    private int itemsCount; 
    //总价格
    private double totalPrice; 
    //商品明细
    private List items = new ArrayList<>();
}

Sabemos que o número de produtos no carrinho de compras, os detalhes do produto e o preço total já estavam relacionados antes. Se precisarem ser modificados, deverão ser modificados juntos. No entanto, usamos a anotação @Data do Lombok. Para as duas propriedades itemsCount e totalPrice, embora as definamos como tipos privados, fornecemos métodos getter e setter públicos.

Externamente, os valores desses dois atributos podem ser modificados à vontade através do método setter. Podemos chamar o método setter à vontade para redefinir os valores dos atributos itemsCount e totalPrice, o que também os tornará inconsistentes com os valores do atributo items.

A definição de encapsulamento orientado a objetos é ocultar dados internos por meio de controle de acesso, e o mundo externo só pode acessar e modificar dados internos por meio da interface limitada fornecida pela classe. Portanto, expor métodos setter que não deveriam ser expostos viola claramente o recurso de encapsulamento orientado a objetos.

Uma boa abordagem seria não fornecer getters/setters, mas apenas fornecer um método addItem público e modificar as propriedades itemsCount, totalPrice e items ao mesmo tempo.

Portanto, neste caso, não é adequado usar Lombok, ou apenas usar @Getter em vez de @Setter, em vez de usar @Data diretamente. Você precisa ter cuidado durante o uso.

4. Resumo

Embora o Lombok seja bom, ele também tem muitas deficiências. Se você for forçado a usá-lo no desenvolvimento da equipe da empresa, você só poderá usá-lo. Se você estiver desenvolvendo um novo projeto, tente não usá-lo se puder, caso contrário, haverá haver muitas armadilhas.

Acho que você gosta

Origin blog.csdn.net/u013302168/article/details/132840836
Recomendado
Clasificación