Generación de código de patrón de constructor en IntelliJ


82

¿Hay alguna forma de automatizar la escritura de patrones de Builder en IntelliJ?

Por ejemplo, dada esta clase simple:

class Film {
   private String title;
   private int length;

   public void setTitle(String title) {
       this.title = title;
   }

   public String getTitle() {
       return this.title;
   }

   public void setLength(int length) {
       this.length = length;
   }

   public int getLength() {
       return this.length;
   }
}

¿Hay alguna manera de que pueda hacer que el IDE genere esto o algo similar?

public class FilmBuilder {

    Film film;

    public FilmBuilder() {
        film = new Film();
    }

    public FilmBuilder withTitle(String title) {
        film.setTitle(title);
        return this;
    }

    public FilmBuilder withLength(int length) {
        film.setLength(length);
        return this;
    }

    public Film build() {
        return film;
    }
}

Respuestas:


106

Utilice la refactorización Reemplazar constructor con constructor .

Para usar esta función, haga clic en la firma del constructor en su código, luego haga clic derecho y seleccione el menú "Refactor", luego haga clic en "Reemplazar constructor con constructor ..." para que aparezca el cuadro de diálogo para generar el código.


12
¿Hay alguna manera de cambiar "setX ()" a "withX ()", Serge?
ae6rt

5
¿O puede cambiar setX () a solo x ()?
Kurru

¡Agradable! Nunca es nuevo lo que existió. Gracias
vikingsteve

16
Puede cambiar el nombre de los métodos de establecedor haciendo clic en el engranaje en la esquina superior derecha de la ventana de creación de constructor y seleccionando "Cambiar el nombre del prefijo de establecedor". Luego, puede cambiarlo a "con" o simplemente eliminar el prefijo por completo.
Chris B

3
¿Es posible hacer que IntelliJ cree la clase Builder como una clase interna automáticamente?
kit

23

Encontré que la generación de patrones de constructor incorporada en IntelliJ es un poco torpe por algunas razones:

  1. Necesita usar un constructor existente como referencia.
  2. No se puede acceder rápidamente a través del menú "Generar" ( command+Nen OS X).
  3. Solo genera clases de constructor externas. Como han mencionado otros, es muy común usar clases internas estáticas al implementar el patrón de construcción.

El complemento InnerBuilder soluciona todas estas deficiencias y no requiere instalación ni configuración. Aquí hay un constructor de muestra generado por el complemento:

public class Person {
    private String firstName;
    private String lastName;
    private int age;

    private Person(Builder builder) {
        firstName = builder.firstName;
        lastName = builder.lastName;
        age = builder.age;
    }

    public static final class Builder {
        private String firstName;
        private String lastName;
        private int age;

        public Builder() {
        }

        public Builder firstName(String firstName) {
            this.firstName = firstName;
            return this;
        }

        public Builder lastName(String lastName) {
            this.lastName = lastName;
            return this;
        }

        public Builder age(int age) {
            this.age = age;
            return this;
        }

        public Person build() {
            return new Person(this);
        }
    }
}

3
Por favor, vea mi respuesta para obtener una solución integrada a todos los problemas mencionados.
jFrenetic

1
Hace algunos buenos puntos @jFrenetic, pero aún así encuentro que el enfoque incorporado es demasiado engorroso, incluso con esas soluciones. Con el plug-in, que es, literalmente, sólo alt+shift+B, entery BAM, que tiene su constructor. : D
Mansoor Siddiqui

1
Dios mío, esto es un sueño hecho realidad. El constructor incorporado no lo hizo por mí; prefiero tener al constructor como una clase interna de PoJo. ¡Gracias, un consejo encantador!
Somaiah Kumbera

16

A continuación, se explica cómo superar las deficiencias mencionadas por Mansoor Siddiqui :

1) Necesita usar un constructor existente como referencia.

Que es muy fácil de generar. Simplemente presione Alt+ Insen Windows para invocar el menú Generar y elija Constructor.

2) No se puede acceder rápidamente a través del menú "Generar" (comando + N en OS X)

Simplemente vaya a Settings -> Keymap, búsquelo Buildery asígnele un acceso directo de su elección (si usa esta funcionalidad con mucha frecuencia, lo que rara vez es el caso). Puede asignar Alt+ Bpor ejemplo.

Otra alternativa es Ctrl+ Shift+ A(Buscar acción). Comience a escribir Buildere inmediatamente obtendrá acceso al comando:

Cuadro de diálogo Buscar acción

Puede usar este atajo para acceder rápidamente a cualquier función de IntelliJ IDEA (esto ayuda mucho cuando no recuerda exactamente cómo se llama el comando y dónde encontrarlo).

3) Solo genera clases externas de constructores. Como han mencionado otros, es muy común usar clases internas estáticas al implementar el patrón de construcción.

También prefiero a mis constructores como clases internas estáticas. Desafortunadamente, no hay una forma sencilla de hacerlo, pero aún es factible. Solo tiene que definir la clase interna anidada usted mismo (déjela vacía), y cuando invoque el Replace Constructor with Builderdiálogo, elija la Use existingopción y seleccione su clase interna. ¡Funciona de maravilla! Sin embargo, hubiera sido más fácil configurar esta opción.


11

Si se pregunta si esto se puede usar para crear una clase con una clase de constructor interno como lo describe Joshua Block , solo tiene que definir una clase interna vacía primero, luego marque "Usar existente" y busque su nueva (clase interna ) y presione "Refactor".

¡PD! El cursor debe residir dentro del constructor (escrito previamente) para poder usar la función de refactorización "Reemplazar constructor con constructor".


Es interesante que estos complementos olviden una parte importante del patrón del constructor: aún debe definir los parámetros necesarios en el constructor. De lo contrario, ha pasado de detectar estáticamente los errores de parámetros faltantes a detectar solo en tiempo de ejecución.
EJ Campbell

@EJCampbell Eso frustraría uno de los propósitos de usar un Builder, que es hacer que el código sea más legible al proporcionar una forma de adjuntar nombres a los parámetros, incluso si son necesarios. Esto ayuda cuando un constructor toma una gran cantidad de parámetros. No sería necesario si Java nos permitiera nombrar parámetros reales en la llamada, como hacen C # y Swift.
ajb

5

La forma de IntelliJ a esto es, en mi humilde opinión, es complicado. Hay dos complementos (prefiero este: https://plugins.jetbrains.com/plugin/7354 ) que sirven mucho mejor para el propósito.

Por ejemplo, prefiero tener la clase Builder como una clase interna de PoJo. Para lograr eso con IntelliJ, necesita algunos golpes adicionales.

Otra ventaja del complemento es la ubicación de la funcionalidad (en el Generate...menú contextual).


4

¡Lombok es la forma más fácil de hacerlo! (Tiene un complemento Intellij para soporte de sintaxis https://plugins.jetbrains.com/plugin/6317-lombok-plugin )

Solo agrega un @Builder anotación, y detrás de las paredes agregará una implementación de constructor dentro del objeto.

Con Lombok:

import lombok.Builder;
import lombok.Singular;
import java.util.Set;

@Builder
public class BuilderExample {
  @Builder.Default private long created = System.currentTimeMillis();
  private String name;
  private int age;
  @Singular private Set<String> occupations;
}

Sin Lombok:

import java.util.Set;

public class BuilderExample {
  private long created;
  private String name;
  private int age;
  private Set<String> occupations;
  
  BuilderExample(String name, int age, Set<String> occupations) {
    this.name = name;
    this.age = age;
    this.occupations = occupations;
  }
  
  private static long $default$created() {
    return System.currentTimeMillis();
  }
  
  public static BuilderExampleBuilder builder() {
    return new BuilderExampleBuilder();
  }
  
  public static class BuilderExampleBuilder {
    private long created;
    private boolean created$set;
    private String name;
    private int age;
    private java.util.ArrayList<String> occupations;
    
    BuilderExampleBuilder() {
    }
    
    public BuilderExampleBuilder created(long created) {
      this.created = created;
      this.created$set = true;
      return this;
    }
    
    public BuilderExampleBuilder name(String name) {
      this.name = name;
      return this;
    }
    
    public BuilderExampleBuilder age(int age) {
      this.age = age;
      return this;
    }
    
    public BuilderExampleBuilder occupation(String occupation) {
      if (this.occupations == null) {
        this.occupations = new java.util.ArrayList<String>();
      }
      
      this.occupations.add(occupation);
      return this;
    }
    
    public BuilderExampleBuilder occupations(Collection<? extends String> occupations) {
      if (this.occupations == null) {
        this.occupations = new java.util.ArrayList<String>();
      }

      this.occupations.addAll(occupations);
      return this;
    }
    
    public BuilderExampleBuilder clearOccupations() {
      if (this.occupations != null) {
        this.occupations.clear();
      }
      
      return this;
    }

    public BuilderExample build() {
      // complicated switch statement to produce a compact properly sized immutable set omitted.
      Set<String> occupations = ...;
      return new BuilderExample(created$set ? created : BuilderExample.$default$created(), name, age, occupations);
    }
    
    @java.lang.Override
    public String toString() {
      return "BuilderExample.BuilderExampleBuilder(created = " + this.created + ", name = " + this.name + ", age = " + this.age + ", occupations = " + this.occupations + ")";
    }
  }
}

Fuente: https://projectlombok.org/features/Builder


lombok es la generación de código con pasos adicionales. debido a esto, se confunden varias herramientas de análisis estático porque el código final que leen no se parece a Java.
ThisCompSciGuy


0

Como complemento a la respuesta de @ CrazyCoder, creo que es muy útil saber que en la parte superior derecha del cuadro de diálogo Reemplazar constructor con constructor hay un botón de configuración que puede usar para cambiar el nombre del prefijo de los establecedores.


0

Para aquellos que quieran reemplazar "demasiados parámetros" con un patrón de constructor, "extraer objeto de parámetro" y luego convertir el constructor en constructor mencionado en otras respuestas aquí.


0

Genere el constructor de la clase haciendo clic derecho en la clase Generar> Constructor. Luego, en Windows, presione Ctrl + Shift + A para buscar la acción y escriba "constructor", seleccione "Reemplazar constructor con constructor ..."


Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.