Π ΡƒΠ±Ρ€ΠΈΠΊΠΈ
Π‘Π΅Π· Ρ€ΡƒΠ±Ρ€ΠΈΠΊΠΈ

🎩 ΠΠ΅ΠΈΠ·ΠΌΠ΅Π½ΡΠ΅ΠΌΠΎΡΡ‚ΡŒ Π² Java πŸ”₯ Π£ΠΏΡ€ΠΎΡ‰Π΅Π½Π°

Π’ этой ΡΡ‚Π°Ρ‚ΡŒΠ΅ ΠΌΡ‹ опишСм, ΠΊΠ°ΠΊ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Immutables.org Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ° для создания Π½Π°ΡˆΠΈΡ… нСизмСняСмых классов вмСсто выполнСния Ρ€ΡƒΡ‡Π½Ρ‹Ρ… Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΉ, ΠΏΠΎΠ΄Π²Π΅Ρ€ΠΆΠ΅Π½Π½Ρ‹Ρ… ошибкам. ΠŸΠΎΠΌΠ΅Ρ‡Π΅Π½Π½Ρ‹ΠΉ java, Π»ΡƒΡ‡ΡˆΠΈΠΌΠΈ ΠΏΡ€Π°ΠΊΡ‚ΠΈΠΊΠ°ΠΌΠΈ, Π½Π΅ΠΈΠ·ΠΌΠ΅Π½ΡΠ΅ΠΌΠΎΡΡ‚ΡŒΡŽ.

Π‘Ρ‚Π°Ρ‚ΡŒΡ Π±Ρ‹Π»Π° ΠΏΠ΅Ρ€Π²ΠΎΠ½Π°Ρ‡Π°Π»ΡŒΠ½ΠΎ ΠΎΠΏΡƒΠ±Π»ΠΈΠΊΠΎΠ²Π°Π½Π° ΠΏΠΎ адрСсу carlos chac.in

Π‘ΠΌΠΎΡ‚Ρ€ΠΈΡ‚Π΅ Ρ‚Π°ΠΊΠΆΠ΅:

Горячий Π½Π°ΠΏΠΈΡ‚ΠΎΠΊ НСизмСняСмый/АвтоматичСскоС Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅/Π›ΠΎΠΌΠ±ΠΎΠΊ πŸ”₯ ΠΊΠ°ΠΊΠΎΠΉ?

ΠšΠ°Ρ€Π»ΠΎΡ Π§Π°Ρ‡ΠΈΠ½ β˜• πŸ‘½ – 13 апрСля – 5 ΠΌΠΈΠ½ΡƒΡ‚ чтСния

Π’ Π­Ρ„Ρ„Π΅ΠΊΡ‚ΠΈΠ²Π½ΠΎΠΉ Java , Π”ΠΆΠΎΡˆΡƒΠ° Π‘Π»ΠΎΡ… Π΄Π°Π΅Ρ‚ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΡƒΡŽ Ρ€Π΅ΠΊΠΎΠΌΠ΅Π½Π΄Π°Ρ†ΠΈΡŽ:

ΠšΠ»Π°ΡΡΡ‹ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π±Ρ‹Ρ‚ΡŒ нСизмСняСмыми, Ссли Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π½Π΅Ρ‚ ΠΎΡ‡Π΅Π½ΡŒ вСской ΠΏΡ€ΠΈΡ‡ΠΈΠ½Ρ‹ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ ΠΈΡ… измСняСмыми… Если класс нСльзя ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ нСизмСняСмым, максимально ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡ΡŒΡ‚Π΅ Π΅Π³ΠΎ ΠΈΠ·ΠΌΠ΅Π½Ρ‡ΠΈΠ²ΠΎΡΡ‚ΡŒ.

πŸ”© НСизмСняСмыС ΠžΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹

ΠžΠ±ΡŠΠ΅ΠΊΡ‚ считаСтся нСизмСняСмым, Ссли Π΅Π³ΠΎ состояниС Π½Π΅ ΠΌΠΎΠΆΠ΅Ρ‚ ΠΈΠ·ΠΌΠ΅Π½ΠΈΡ‚ΡŒΡΡ послС Π΅Π³ΠΎ создания. Максимальная Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡ‚ΡŒ ΠΎΡ‚ нСизмСняСмых ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² ΡˆΠΈΡ€ΠΎΠΊΠΎ ΠΏΡ€ΠΈΠ·Π½Π°Π½Π° Π² качСствС Ρ€Π°Π·ΡƒΠΌΠ½ΠΎΠΉ стратСгии создания простого ΠΈ Π½Π°Π΄Π΅ΠΆΠ½ΠΎΠ³ΠΎ ΠΊΠΎΠ΄Π°. ссылка

  • НСизмСняСмыС ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹ ΡΠΎΠ·Π΄Π°ΡŽΡ‚ΡΡ ΠΎΠ΄ΠΈΠ½ Ρ€Π°Π·, Π² согласованном состоянии, ΠΈ ΠΌΠΎΠ³ΡƒΡ‚ Π±Ρ‹Ρ‚ΡŒ бСзопасно Ρ€Π°Π·Π΄Π΅Π»Π΅Π½Ρ‹
    • ΠŸΡ€ΠΎΠΈΠ·ΠΎΠΉΠ΄Π΅Ρ‚ сбой, Ссли ΠΎΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ Π°Ρ‚Ρ€ΠΈΠ±ΡƒΡ‚Ρ‹ ΠΎΡ‚ΡΡƒΡ‚ΡΡ‚Π²ΡƒΡŽΡ‚
    • НС ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ Π½Π΅Π·Π°ΠΌΠ΅Ρ‚Π½ΠΎ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ ΠΏΡ€ΠΈ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‡Π΅ Π² Π΄Ρ€ΡƒΠ³ΠΎΠΉ ΠΊΠΎΠ΄
  • НСизмСняСмыС ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹, СстСствСнно, потокобСзопасны ΠΈ поэтому ΠΌΠΎΠ³ΡƒΡ‚ Π±Ρ‹Ρ‚ΡŒ бСзопасно Ρ€Π°Π·Π΄Π΅Π»Π΅Π½Ρ‹ ΠΌΠ΅ΠΆΠ΄Ρƒ ΠΏΠΎΡ‚ΠΎΠΊΠ°ΠΌΠΈ
    • Никакого Ρ‡Ρ€Π΅Π·ΠΌΠ΅Ρ€Π½ΠΎΠ³ΠΎ копирования
    • ΠžΡ‚ΡΡƒΡ‚ΡΡ‚Π²ΠΈΠ΅ Ρ‡Ρ€Π΅Π·ΠΌΠ΅Ρ€Π½ΠΎΠΉ синхронизации
  • ΠžΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½ΠΈΡ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² приятно ΠΏΠΈΡΠ°Ρ‚ΡŒ ΠΈ Ρ‡ΠΈΡ‚Π°Ρ‚ΡŒ
    • Никаких ΡˆΠ°Π±Π»ΠΎΠ½Π½Ρ‹Ρ… установщиков ΠΈ Π΄ΠΎΠ±Ρ‹Ρ‚Ρ‡ΠΈΠΊΠΎΠ²
    • Никаких ΡƒΡ€ΠΎΠ΄Π»ΠΈΠ²Ρ‹Ρ… IDE-сгСнСрированных Ρ…ΡΡˆ-ΠΊΠΎΠ΄Π° , Ρ€Π°Π²Π½ΠΎ ΠΈ toString ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠ², ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π² ΠΊΠΎΠ½Π΅Ρ‡Π½ΠΎΠΌ ΠΈΡ‚ΠΎΠ³Π΅ ΡΠΎΡ…Ρ€Π°Π½ΡΡŽΡ‚ΡΡ Π² систСмС управлСния вСрсиями. ссылка

πŸ”§ Π”Π°Π²Π°ΠΉΡ‚Π΅ ΠΏΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΡƒΠ΅ΠΌ измСняСмый ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ Π² нСизмСняСмый (Π²Ρ€ΡƒΡ‡Π½ΡƒΡŽ βœ‹ ):

Π‘Π»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΉ класс – это Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎ ΠΌΡ‹ ΠΎΠ±Ρ‹Ρ‡Π½ΠΎ Π½Π°Π·Ρ‹Π²Π°Π΅ΠΌ POJO ΠΈΠ»ΠΈ Java-ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ :

import java.util.Date;
import java.util.List;
import java.util.Objects;

public class OldModel {

    private String fieldA;
    private Date fieldB;
    private Long fieldC;
    private List fieldD;

    public OldModel() {
    }

    public String getFieldA() {
        return fieldA;
    }

    public void setFieldA(String fieldA) {
        this.fieldA = fieldA;
    }

    public Date getFieldB() {
        return fieldB;
    }

    public void setFieldB(Date fieldB) {
        this.fieldB = fieldB;
    }

    public Long getFieldC() {
        return fieldC;
    }

    public void setFieldC(Long fieldC) {
        this.fieldC = fieldC;
    }

    public List getFieldD() {
        return fieldD;
    }

    public void setFieldD(List fieldD) {
        this.fieldD = fieldD;
    }

    @Override
    public String toString() {
        return "OldModel{" +
                "fieldA='" + fieldA + '\'' +
                ", fieldB=" + fieldB +
                ", fieldC=" + fieldC +
                ", fieldD=" + fieldD +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        OldModel oldModel = (OldModel) o;
        return Objects.equals(getFieldA(), oldModel.getFieldA()) &&
                Objects.equals(getFieldB(), oldModel.getFieldB()) &&
                Objects.equals(getFieldC(), oldModel.getFieldC()) &&
                Objects.equals(getFieldD(), oldModel.getFieldD());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getFieldA(), getFieldB(), getFieldC(), getFieldD());
    }
}

Π§Ρ‚ΠΎΠ±Ρ‹ ΠΏΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΠΎΠ²Π°Ρ‚ΡŒ это Π² нСизмСняСмый ΠΎΠ±ΡŠΠ΅ΠΊΡ‚, ΠΌΡ‹ Π΄ΠΎΠ»ΠΆΠ½Ρ‹:

πŸ”΄ Класс Π½Π΅ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ ΠΏΠ΅Ρ€Π΅ΠΎΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½:

  • πŸ” Π‘Π΄Π΅Π»Π°ΠΉΡ‚Π΅ ΡƒΡ€ΠΎΠΊ ΠΎΠΊΠΎΠ½Ρ‡Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹ΠΌ.
  • πŸ” Или сдСлайтС конструктор ΠΎΠΊΠΎΠ½Ρ‡Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹ΠΌ ΠΈ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉΡ‚Π΅ статичСскиС Ρ„Π°Π±Ρ€ΠΈΡ‡Π½Ρ‹Π΅ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹.
-public class OldModel {
+public final class OldModel {

πŸ” Π‘Π΄Π΅Π»Π°ΠΉΡ‚Π΅ всС поля Π·Π°ΠΊΡ€Ρ‹Ρ‚Ρ‹ΠΌΠΈ ΠΈ ΠΎΠΊΠΎΠ½Ρ‡Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹ΠΌΠΈ

-    private String fieldA;
-    private Date fieldB;
-    private Long fieldC;
-    private List fieldD;
+    private final String fieldA;
+    private final Date fieldB;
+    private final Long fieldC;
+    private final List fieldD;

🚧 ΠžΠ±ΡŠΠ΅ΠΊΡ‚ Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ построСн Π·Π° 1️ ΠΎΠ΄ΠΈΠ½ шаг.

-    public OldModel() {
+    public OldModel(
+            final String fieldA,
+            final Date fieldB,
+            final Long fieldC,
+            final List fieldD) {
+        this.fieldA = fieldA;
+        this.fieldB = fieldB;
+        this.fieldC = fieldC;
+        this.fieldD = fieldD;
     }

πŸ”΄ НС прСдоставляйтС ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΌΠΎΠ³ΡƒΡ‚ ΠΈΠ·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ состояниС ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°.

-    public void setFieldA(String fieldA) {
-        this.fieldA = fieldA;
-    }
.
. // Remove all the setters
.

⏩ Если ΠΊΠ°ΠΊΠΎΠ΅-Π»ΠΈΠ±ΠΎ ΠΈΠ· ΠΏΠΎΠ»Π΅ΠΉ являСтся измСняСмым ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠΌ, вмСсто этого ΠΏΡ€Π΅Π΄ΠΎΡΡ‚Π°Π²ΡŒΡ‚Π΅ Π·Π°Ρ‰ΠΈΡ‚Π½ΡƒΡŽ копию этого ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°.

+   public Date getFieldB() {
+       return new Date(fieldB.getTime()); // Easy to forget about this :(
+   }
+
+   public List getFieldD() {
+       return Collections.unmodifiableList(fieldD); // This is not great :(
+   }

О Ρ‚ΠΎΠΌ , ΠΊΠ°ΠΊ Π²Ρ€ΡƒΡ‡Π½ΡƒΡŽ βœ‹ Π΄Π΅Π»Π°Ρ‚ΡŒ это 😀

Π§Ρ‚ΠΎ ΠΆ, это большая Ρ€Π°Π±ΠΎΡ‚Π°, ΠΈ ΠΎΠ½Π° Ρ‚Π°ΠΊΠΆΠ΅ ΠΏΠΎΠ΄Π²Π΅Ρ€ΠΆΠ΅Π½Π° ошибкам, ΠΈ Π΄Π°ΠΆΠ΅ ΠΊΠΎΠ³Π΄Π° ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Π·Π°ΡΡ‚Π°Π²ΠΈΡ‚ΡŒ IDE ΠΈΠ·Π²Π΅Ρ€Π³Π½ΡƒΡ‚ΡŒ вСсь этот ΠΊΠΎΠ΄ для нас, Π½Π°ΠΌ всС Ρ€Π°Π²Π½ΠΎ Π½ΡƒΠΆΠ½ΠΎ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ ΠΈ ΠΈΠ·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π²Π΅Ρ‰ΠΈ.

πŸ™… И Π²ΠΎΡ‚ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚:

import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Objects;

public final class OldModel {

    private final String fieldA;
    private final Date fieldB;
    private final Long fieldC;
    private final List fieldD;

    public OldModel(
            final String fieldA,
            final Date fieldB,
            final Long fieldC,
            final List fieldD) {
        this.fieldA = fieldA;
        this.fieldB = fieldB;
        this.fieldC = fieldC;
        this.fieldD = fieldD;
    }

    public String getFieldA() {
        return this.fieldA;
    }

    public Date getFieldB() {
        return new Date(this.fieldB.getTime()); // Easy to forget about this :(
    }

    public Long getFieldC() {
        return this.fieldC;
    }

    public List getFieldD() {
        return Collections.unmodifiableList(this.fieldD); // This is not great :(
    }

    // toSting, equals and hashCode omitted
}

πŸ† Π”Π°Π²Π°ΠΉΡ‚Π΅ сдСлаСм это сСйчас , ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ простой способ

НСизмСняСмая Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ°

ΠŸΡ€ΠΎΡ†Π΅ΡΡΠΎΡ€Ρ‹ Π°Π½Π½ΠΎΡ‚Π°Ρ†ΠΈΠΉ Java для создания простых, бСзопасных ΠΈ Π½Π΅ΠΏΡ€ΠΎΡ‚ΠΈΠ²ΠΎΡ€Π΅Ρ‡ΠΈΠ²Ρ‹Ρ… ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² Π·Π½Π°Ρ‡Π΅Π½ΠΈΠΉ. НС ΠΏΠΎΠ²Ρ‚ΠΎΡ€ΡΠΉΡ‚Π΅ΡΡŒ, ΠΏΠΎΠΏΡ€ΠΎΠ±ΡƒΠΉΡ‚Π΅ НСизмСняСмый, самый ΠΏΠΎΠ»Π½Ρ‹ΠΉ инструмСнт Π² этой области!

Π’ΠΊΠ»ΡŽΡ‡Π°Ρ‚ΡŒ immutables.org Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡ‚ΡŒ ΠΎΡ‚ вашСго ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°:

Π—Π°Π²ΠΈΡΠΈΠΌΠΎΡΡ‚ΡŒ ΠΎΡ‚ Maven:


    org.immutables
    value
    2.8.3
    provided

πŸ‘· Π‘ΠΎΠ·Π΄Π°ΠΉΡ‚Π΅ нСизмСняСмый ΠΎΠ±ΡŠΠ΅ΠΊΡ‚

import org.immutables.value.Value;

import java.util.Date;
import java.util.List;

@Value.Immutable
public interface NewModel {

    String fieldA();

    Date fieldB();

    Long fieldC();

    List fieldD();
}

πŸ”© ΠšΠΎΠΌΠΏΠΈΠ»ΠΈΡ€ΡƒΠΉΡ‚Π΅ ΠΈ Π½Π°ΡΠ»Π°ΠΆΠ΄Π°ΠΉΡ‚Π΅ΡΡŒ

ПослС компиляции ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ Π°Π½Π½ΠΎΡ‚Π°Ρ†ΠΈΠΉ сгСнСрируСт для вас ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΉ ΠΊΠΎΠ΄:

Π‘ΠΎΠ·Π΄Π°Π½Π½Ρ‹ΠΉ ΠΎΠΊΠΎΠ½Ρ‡Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ класс, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Ρ€Π°ΡΡˆΠΈΡ€ΡΠ΅Ρ‚ написанный Π²Ρ€ΡƒΡ‡Π½ΡƒΡŽ Ρ‚ΠΈΠΏ значСния интСрфСйса ΠΈ Ρ€Π΅Π°Π»ΠΈΠ·ΡƒΠ΅Ρ‚ всС ΠΎΠ±ΡŠΡΠ²Π»Π΅Π½Π½Ρ‹Π΅ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ доступа, Π° Ρ‚Π°ΠΊΠΆΠ΅ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅Ρ‚ поля, ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹, конструкторы ΠΈ класс-конструктор .

НСизмСняСмый класс Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ Ρ€Π΅Π°Π»ΠΈΠ·ΡƒΠ΅Ρ‚ абстрактныС срСдства доступа ΠΊ Π°Ρ‚Ρ€ΠΈΠ±ΡƒΡ‚Π°ΠΌ для скалярных ΠΏΡ€ΠΈΠΌΠΈΡ‚ΠΈΠ²Π½Ρ‹Ρ… ΠΈ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π½Ρ‹Ρ… ссылочных Ρ‚ΠΈΠΏΠΎΠ², ΠΏΡ€ΠΈ этом для Π°Ρ‚Ρ€ΠΈΠ±ΡƒΡ‚ΠΎΠ² ΠΊΠΎΠ»Π»Π΅ΠΊΡ†ΠΈΠΈ ΠΈ Π΄Ρ€ΡƒΠ³ΠΈΡ… Ρ‚ΠΈΠΏΠΎΠ² прСдусмотрСна ΡΠΏΠ΅Ρ†ΠΈΠ°Π»ΡŒΠ½Π°Ρ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠ°. java.lang. ΠœΠ΅Ρ‚ΠΎΠ΄Ρ‹ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° Ρ€Π°Π²Π½ΠΎ , Π₯эш-ΠΊΠΎΠ΄ ΠΈ toString ΠΏΠ΅Ρ€Π΅ΠΎΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½Ρ‹ ΠΈ ΠΏΠΎΠ»Π½ΠΎΡΡ‚ΡŒΡŽ зависят ΠΎΡ‚ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠΉ Π°Ρ‚Ρ€ΠΈΠ±ΡƒΡ‚ΠΎΠ², Π° Π½Π΅ ΠΎΡ‚ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€Π° ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°.

НСизмСняСмыС классы Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ ΡΠ²Π»ΡΡŽΡ‚ΡΡ основными (Π½ΠΎ Π½Π΅ СдинствСнными) Π°Ρ€Ρ‚Π΅Ρ„Π°ΠΊΡ‚Π°ΠΌΠΈ исходного ΠΊΠΎΠ΄Π°, создаваСмыми ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΎΠΌ нСизмСняСмых Π°Π½Π½ΠΎΡ‚Π°Ρ†ΠΈΠΉ.

✏️ Как ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ созданный нСизмСняСмый класс

public class App {
    public static void main(String[] args) {
        final ImmutableNewModel model = ImmutableNewModel.builder()
                .fieldA("A")
                .fieldB(new Date())
                .fieldC(1L)
                .addFieldD("a", "b", "d")
                .addFieldD("e")
                .build();
        System.out.println(model);
    }
}

Π’Ρ‹Ρ…ΠΎΠ΄:

NewModel{fieldA=A, fieldB=Sat Apr 11 15:53:26 PDT 2020, fieldC=1, fieldD=[a, b, d, e]}

πŸ“ НСсколько сравнСний

16 69 Π‘Ρ‚Ρ€ΠΎΠΊΠΈ ΠΊΠΎΠ΄Π° для поддСрТания
377 0 Π‘Ρ‚Ρ€ΠΎΠΊΠΈ ΠΊΠΎΠ΄Π° для создания
βœ… ⁉ ΠΊ Защитная копия ΠΏΠΎΠ»Π΅ΠΉ
βœ… ❌ Π‘Π²ΠΎΠ±ΠΎΠ΄Π½Ρ‹ΠΉ API для копирования
βœ… ❌ Π‘Ρ‚Ρ€ΠΎΠΈΡ‚Π΅Π»ΡŒ
βœ… ❌ ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° Π½Π° ΠΎΠ±Π½ΡƒΠ»ΡΠ΅ΠΌΠΎΡΡ‚ΡŒ

πŸŽ‰ Π‘Π³Π΅Π½Π΅Ρ€ΠΈΡ€ΠΎΠ²Π°Π½Π½Ρ‹ΠΉ ΠΊΠΎΠ΄

По ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ Π² ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°Ρ… maven компилятор Π±ΡƒΠ΄Π΅Ρ‚ Π³Π΅Π½Π΅Ρ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΈ автоматичСски ΠΈΠΌΠΏΠΎΡ€Ρ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ сгСнСрированный ΠΊΠΎΠ΄ Π² ΠΏΠ°ΠΏΠΊΡƒ target/generated_sources ΠΈ ΠΈΠ· Π½Π΅Π΅, ΠΎΠ±Ρ€Π°Ρ‚ΠΈΡ‚Π΅ Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅, Ρ‡Ρ‚ΠΎ Π² Π±ΠΎΠ»ΡŒΡˆΠΈΠ½ΡΡ‚Π²Π΅ случаСв ΠΌΡ‹ ΠΈΠ³Π½ΠΎΡ€ΠΈΡ€ΡƒΠ΅ΠΌ ΠΏΠ°ΠΏΠΊΡƒ target/ Π² систСмах контроля вСрсий (VCS), Ρ‚Π°ΠΊΠΈΡ… ΠΊΠ°ΠΊ git ΠΈ mercurial. ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ Ρ„Π°ΠΉΠ» .gitignore Π² ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π΅. Π­Ρ‚ΠΎΡ‚ ΠΊΠΎΠ΄ Π½Π΅ Π½ΡƒΠΆΠ½ΠΎ ΠΏΠ΅Ρ€Π΅Π΄Π°Π²Π°Ρ‚ΡŒ Π² Ρ†Π΅Π½Ρ‚Ρ€Π°Π»ΠΈΠ·ΠΎΠ²Π°Π½Π½Ρ‹ΠΉ VCS.

!! ️ ΠΏΡ€Π΅Π΄ΡƒΠΏΡ€Π΅ΠΆΠ΄Π΅Π½ΠΈΠ΅: Π­Ρ‚ΠΎ ΠΎΡ‡Π΅Π½ΡŒ ΠΌΠ½ΠΎΠ³ΠΎ ΠΊΠΎΠ΄Π° ❗️ ❗️

package com.groupon.api.talks;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import org.immutables.value.Generated;

/**
 * Immutable implementation of {@link NewModel}.
 * 

* Use the builder to create immutable instances: * {@code ImmutableNewModel.builder()}. */ @Generated(from = "NewModel", generator = "Immutables") @SuppressWarnings({"all"}) @javax.annotation.Generated("org.immutables.processor.ProxyProcessor") public final class ImmutableNewModel implements NewModel { private final String fieldA; private final Date fieldB; private final Long fieldC; private final List fieldD; private ImmutableNewModel( String fieldA, Date fieldB, Long fieldC, List fieldD) { this.fieldA = fieldA; this.fieldB = fieldB; this.fieldC = fieldC; this.fieldD = fieldD; } /** * @return The value of the {@code fieldA} attribute */ @Override public String fieldA() { return fieldA; } /** * @return The value of the {@code fieldB} attribute */ @Override public Date fieldB() { return fieldB; } /** * @return The value of the {@code fieldC} attribute */ @Override public Long fieldC() { return fieldC; } /** * @return The value of the {@code fieldD} attribute */ @Override public List fieldD() { return fieldD; } /** * Copy the current immutable object by setting a value for the {@link NewModel#fieldA() fieldA} attribute. * An equals check used to prevent copying of the same value by returning {@code this}. * @param value A new value for fieldA * @return A modified copy of the {@code this} object */ public final ImmutableNewModel withFieldA(String value) { String newValue = Objects.requireNonNull(value, "fieldA"); if (this.fieldA.equals(newValue)) return this; return new ImmutableNewModel(newValue, this.fieldB, this.fieldC, this.fieldD); } /** * Copy the current immutable object by setting a value for the {@link NewModel#fieldB() fieldB} attribute. * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}. * @param value A new value for fieldB * @return A modified copy of the {@code this} object */ public final ImmutableNewModel withFieldB(Date value) { if (this.fieldB == value) return this; Date newValue = Objects.requireNonNull(value, "fieldB"); return new ImmutableNewModel(this.fieldA, newValue, this.fieldC, this.fieldD); } /** * Copy the current immutable object by setting a value for the {@link NewModel#fieldC() fieldC} attribute. * An equals check used to prevent copying of the same value by returning {@code this}. * @param value A new value for fieldC * @return A modified copy of the {@code this} object */ public final ImmutableNewModel withFieldC(Long value) { Long newValue = Objects.requireNonNull(value, "fieldC"); if (this.fieldC.equals(newValue)) return this; return new ImmutableNewModel(this.fieldA, this.fieldB, newValue, this.fieldD); } /** * Copy the current immutable object with elements that replace the content of {@link NewModel#fieldD() fieldD}. * @param elements The elements to set * @return A modified copy of {@code this} object */ public final ImmutableNewModel withFieldD(String... elements) { List newValue = createUnmodifiableList(false, createSafeList(Arrays.asList(elements), true, false)); return new ImmutableNewModel(this.fieldA, this.fieldB, this.fieldC, newValue); } /** * Copy the current immutable object with elements that replace the content of {@link NewModel#fieldD() fieldD}. * A shallow reference equality check is used to prevent copying of the same value by returning {@code this}. * @param elements An iterable of fieldD elements to set * @return A modified copy of {@code this} object */ public final ImmutableNewModel withFieldD(Iterable elements) { if (this.fieldD == elements) return this; List newValue = createUnmodifiableList(false, createSafeList(elements, true, false)); return new ImmutableNewModel(this.fieldA, this.fieldB, this.fieldC, newValue); } /** * This instance is equal to all instances of {@code ImmutableNewModel} that have equal attribute values. * @return {@code true} if {@code this} is equal to {@code another} instance */ @Override public boolean equals(Object another) { if (this == another) return true; return another instanceof ImmutableNewModel && equalTo((ImmutableNewModel) another); } private boolean equalTo(ImmutableNewModel another) { return fieldA.equals(another.fieldA) && fieldB.equals(another.fieldB) && fieldC.equals(another.fieldC) && fieldD.equals(another.fieldD); } /** * Computes a hash code from attributes: {@code fieldA}, {@code fieldB}, {@code fieldC}, {@code fieldD}. * @return hashCode value */ @Override public int hashCode() { int h = 5381; h += (h << 5) + fieldA.hashCode(); h += (h << 5) + fieldB.hashCode(); h += (h << 5) + fieldC.hashCode(); h += (h << 5) + fieldD.hashCode(); return h; } /** * Prints the immutable value {@code NewModel} with attribute values. * @return A string representation of the value */ @Override public String toString() { return "NewModel{" + + fieldA + ", fieldB=" + fieldB + ", fieldC=" + fieldC + ", fieldD=" + fieldD + "}"; } /** * Creates an immutable copy of a {@link NewModel} value. * Uses accessors to get values to initialize the new immutable instance. * If an instance is already immutable, it is returned as is. * @param instance The instance to copy * @return A copied immutable NewModel instance */ public static ImmutableNewModel copyOf(NewModel instance) { if (instance instanceof ImmutableNewModel) { return (ImmutableNewModel) instance; } return ImmutableNewModel.builder() .from(instance) .build(); } /** * Creates a builder for {@link ImmutableNewModel ImmutableNewModel}. *

   * ImmutableNewModel.builder()
   *    .fieldA(String) // required {@link NewModel#fieldA() fieldA}
   *    .fieldB(Date) // required {@link NewModel#fieldB() fieldB}
   *    .fieldC(Long) // required {@link NewModel#fieldC() fieldC}
   *    .addFieldD|addAllFieldD(String) // {@link NewModel#fieldD() fieldD} elements
   *    .build();
   * 
* @return A new ImmutableNewModel builder */ public static ImmutableNewModel.Builder builder() { return new ImmutableNewModel.Builder(); } /** * Builds instances of type {@link ImmutableNewModel ImmutableNewModel}. * Initialize attributes and then invoke the {@link #build()} method to create an * immutable instance. *

{@code Builder} is not thread-safe and generally should not be stored in a field or collection, * but instead used immediately to create instances. */ @Generated(from = "NewModel", generator = "Immutables") public static final class Builder { private static final long INIT_BIT_FIELD_A = 0x1L; private static final long INIT_BIT_FIELD_B = 0x2L; private static final long INIT_BIT_FIELD_C = 0x4L; private long initBits = 0x7L; private String fieldA; private Date fieldB; private Long fieldC; private List fieldD = new ArrayList(); private Builder() { } /** * Fill a builder with attribute values from the provided {@code NewModel} instance. * Regular attribute values will be replaced with those from the given instance. * Absent optional values will not replace present values. * Collection elements and entries will be added, not replaced. * @param instance The instance from which to copy values * @return {@code this} builder for use in a chained invocation */ public final Builder from(NewModel instance) { Objects.requireNonNull(instance, "instance"); fieldA(instance.fieldA()); fieldB(instance.fieldB()); fieldC(instance.fieldC()); addAllFieldD(instance.fieldD()); return this; } /** * Initializes the value for the {@link NewModel#fieldA() fieldA} attribute. * @param fieldA The value for fieldA * @return {@code this} builder for use in a chained invocation */ public final Builder fieldA(String fieldA) { this.fieldA = Objects.requireNonNull(fieldA, "fieldA"); initBits &= ~INIT_BIT_FIELD_A; return this; } /** * Initializes the value for the {@link NewModel#fieldB() fieldB} attribute. * @param fieldB The value for fieldB * @return {@code this} builder for use in a chained invocation */ public final Builder fieldB(Date fieldB) { this.fieldB = Objects.requireNonNull(fieldB, "fieldB"); initBits &= ~INIT_BIT_FIELD_B; return this; } /** * Initializes the value for the {@link NewModel#fieldC() fieldC} attribute. * @param fieldC The value for fieldC * @return {@code this} builder for use in a chained invocation */ public final Builder fieldC(Long fieldC) { this.fieldC = Objects.requireNonNull(fieldC, "fieldC"); initBits &= ~INIT_BIT_FIELD_C; return this; } /** * Adds one element to {@link NewModel#fieldD() fieldD} list. * @param element A fieldD element * @return {@code this} builder for use in a chained invocation */ public final Builder addFieldD(String element) { this.fieldD.add(Objects.requireNonNull(element, "fieldD element")); return this; } /** * Adds elements to {@link NewModel#fieldD() fieldD} list. * @param elements An array of fieldD elements * @return {@code this} builder for use in a chained invocation */ public final Builder addFieldD(String... elements) { for (String element : elements) { this.fieldD.add(Objects.requireNonNull(element, "fieldD element")); } return this; } /** * Sets or replaces all elements for {@link NewModel#fieldD() fieldD} list. * @param elements An iterable of fieldD elements * @return {@code this} builder for use in a chained invocation */ public final Builder fieldD(Iterable elements) { this.fieldD.clear(); return addAllFieldD(elements); } /** * Adds elements to {@link NewModel#fieldD() fieldD} list. * @param elements An iterable of fieldD elements * @return {@code this} builder for use in a chained invocation */ public final Builder addAllFieldD(Iterable elements) { for (String element : elements) { this.fieldD.add(Objects.requireNonNull(element, "fieldD element")); } return this; } /** * Builds a new {@link ImmutableNewModel ImmutableNewModel}. * @return An immutable instance of NewModel * @throws java.lang.IllegalStateException if any required attributes are missing */ public ImmutableNewModel build() { if (initBits != 0) { throw new IllegalStateException(formatRequiredAttributesMessage()); } return new ImmutableNewModel(fieldA, fieldB, fieldC, createUnmodifiableList(true, fieldD)); } private String formatRequiredAttributesMessage() { List attributes = new ArrayList<>(); if ((initBits & INIT_BIT_FIELD_A) != 0) attributes.add("fieldA"); if ((initBits & INIT_BIT_FIELD_B) != 0) attributes.add("fieldB"); if ((initBits & INIT_BIT_FIELD_C) != 0) attributes.add("fieldC"); return "Cannot build NewModel, some of required attributes are not set " + attributes; } } private static List createSafeList(Iterable iterable, boolean checkNulls, boolean skipNulls) { ArrayList list; if (iterable instanceof Collection) { int size = ((Collection) iterable).size(); if (size == 0) return Collections.emptyList(); list = new ArrayList<>(); } else { list = new ArrayList<>(); } for (T element : iterable) { if (skipNulls && element == null) continue; if (checkNulls) Objects.requireNonNull(element, "element"); list.add(element); } return list; } private static List createUnmodifiableList(boolean clone, List list) { switch(list.size()) { case 0: return Collections.emptyList(); case 1: return Collections.singletonList(list.get(0)); default: if (clone) { return Collections.unmodifiableList(new ArrayList<>(list)); } else { if (list instanceof ArrayList) { ((ArrayList) list).trimToSize(); } return Collections.unmodifiableList(list); } } } }

Π’Ρ‹Π²ΠΎΠ΄

Π―Π·Ρ‹ΠΊ Java ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ Π΅Ρ‰Π΅ Π±ΠΎΠ»Π΅Π΅ ΠΏΠΎΠ΄Ρ€ΠΎΠ±Π½Ρ‹ΠΌ, Ссли ΠΌΡ‹ Π½Π΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ Π½Π°Π΄Π»Π΅ΠΆΠ°Ρ‰ΠΈΠ΅ инструмСнты для этой Ρ€Π°Π±ΠΎΡ‚Ρ‹, ΠΈ Π² Ρ‚Π΅Ρ‡Π΅Π½ΠΈΠ΅ ΠΌΠ½ΠΎΠ³ΠΈΡ… Π»Π΅Ρ‚ гСнСрация ΠΊΠΎΠ΄Π° Π±Ρ‹Π»Π° ΠΏΠΎΠ»Π΅Π·Π½Ρ‹ΠΌ Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅ΠΌ, ΠΎΠ±Π»Π΅Π³Ρ‡Π°ΡŽΡ‰ΠΈΠΌ Π½Π°ΡˆΡƒ Тизнь Π² экосистСмС Java. ДостиТСниС Ρ…ΠΎΡ€ΠΎΡˆΠ΅Π³ΠΎ уровня нСизмСняСмости Π² Π½Π°ΡˆΠΈΡ… ΠΊΠΎΠ΄ΠΎΠ²Ρ‹Ρ… Π±Π°Π·Π°Ρ… Ρ‚Ρ€Π΅Π±ΡƒΠ΅Ρ‚ Π±ΠΎΠ»ΡŒΡˆΠΈΡ… усилий ΠΏΡ€ΠΈ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ этого Π²Ρ€ΡƒΡ‡Π½ΡƒΡŽ, ΠΈ это ΠΏΠΎΠ΄Π²Π΅Ρ€ΠΆΠ΅Π½ΠΎ Π½Π΅ΠΏΡ€Π΅Π΄Π½Π°ΠΌΠ΅Ρ€Π΅Π½Π½Ρ‹ΠΌ ошибкам, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΈΠ·Π±Π΅ΠΆΠ°Ρ‚ΡŒ этого ΠΈ ΡƒΠΌΠ΅Π½ΡŒΡˆΠΈΡ‚ΡŒ Π½Π°ΡˆΡƒ ΠΊΠΎΠ΄ΠΎΠ²ΡƒΡŽ Π±Π°Π·Ρƒ (мСньшС ΠΊΠΎΠ΄Π°, мСньшС ошибок), ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Immutables.org Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ°.

ΠžΡ€ΠΈΠ³ΠΈΠ½Π°Π»: “https://dev.to/cchacin/immutability-in-java-made-easy-372g”