๊ฐ€๋ณ€๊ฐ์ฒด๋ฅผ ๋‹จ๊ณ„์ ์œผ๋กœ ๋ถˆ๋ณ€๊ฐ์ฒด๋กœ ๋งŒ๋“ค์–ด ๋ณด์ž

๋ฌธ์ œ 1) ๊ฐ์ฒด์˜ ๋ณ€์ˆ˜๊ฐ€ public์ธ ๊ฒฝ์šฐ

๋ฌธ์ œ: ์™ธ๋ถ€์—์„œ ๊ฐ’์„ ๋ณ€๊ฒฝํ•ด๋ฒ„๋ฆด ์ˆ˜ ์žˆ๋‹ค.

  • ๋ณ€๊ฒฝ ์ „ ์ฝ”๋“œ
public class Person {
    public String name;
    public int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

public class ImmutableMain {
    public static void main(String[] args) {
        Person person = new Person("๊ธธ๋™์ด", 20);
        person.age = 21;
    }
}

 

ํ•ด๊ฒฐ 1) private ์ ‘๊ทผ์ž๋กœ ๋ฐ”๊พธ์ž.

์™ธ๋ถ€์—์„œ ๋ณ€์ˆ˜์— ๋ฐ”๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์—†์–ด์ง„๋‹ค.

  • ๋ณ€๊ฒฝ ํ›„ ์ฝ”๋“œ
public class Person {
    public String name;
    public int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

public class ImmutableMain {
    public static void main(String[] args) {
        Person person = new Person("๊ธธ๋™์ด", 20);
        person.age = 21;
    }
}

 

๋ฌธ์ œ 2) setter ๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ

๋ณ€๊ฒฝ์˜ ์ฃผ์ฒด๋Š” ๊ฐ์ฒด ์ž์‹ ์—๊ฒŒ ์žˆ์ง€๋งŒ, ์—ฌ์ „ํžˆ setter ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด ๊ฐ’์ด ๋ณ€๊ฒฝ๋œ๋‹ค.

  • ๋ณ€๊ฒฝ ์ „ ์ฝ”๋“œ
public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

public class ImmutableMain {
    public static void main(String[] args) {
        Person person = new Person("๊ธธ๋™์ด", 20);
        person.setAge(21); // ์™ธ๋ถ€์—์„œ ๊ฐ’ ๋ณ€๊ฒฝ ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ
    }
}

 

ํ•ด๊ฒฐ 2) final ์„ ์ถ”๊ฐ€ํ•˜์ž.

์ดˆ๊ธฐํ™” ์ดํ›„๋กœ๋Š” ๋ณ€์ˆ˜๋ฅผ ์žฌํ• ๋‹นํ•  ์ˆ˜ ์—†๊ฒŒ ๋œ๋‹ค.

final์„ ํ†ตํ•ด ๊ธฐ๋Œ€ํ•  ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„

  1. ์ƒ์„ฑ์ž ์ดˆ๊ธฐํ™”๋ฅผ ๊ฐ•์ œํ•œ๋‹ค.
  2. setter ๋“ฑ ์žฌํ• ๋‹นํ•  ์ˆ˜ ์—†์–ด์ง„๋‹ค.
  • ๋ณ€๊ฒฝ ํ›„ ์ฝ”๋“œ
public class Person {
    private final String name;
    private final int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /*
    public void setAge(int age) {
        this.age = age;  // error ~!
    }
    */
}

 

๋ฌธ์ œ 3) ์ฐธ์กฐ๋ณ€์ˆ˜๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ: ๊ฐ์ฒด

์ฐธ์กฐํƒ€์ž…์˜ ๊ฒฝ์šฐ ์žฌํ• ๋‹น๋  ์ˆ˜๋Š” ์—†์ง€๋งŒ ํ•ด๋‹น ๊ฐ์ฒด์˜ ์ƒํƒœ๋Š” ๋ณ€ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ๋ณ€๊ฒฝ ์ „ ์ฝ”๋“œ

Person ๊ฐ์ฒด: getter๋งŒ ์ œ๊ณตํ•œ๋‹ค.

public class Person {
    private final String name;
    private final int age;
    private final Address address;

    public Person(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public Address getAddress() {
        return address;
    }
}

Address ๊ฐ์ฒด: update ๋ฉ”์†Œ๋“œ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

public class Address {
    private String county;
    private String state;
    private String city;
    private String zipCode;

    public Address(final String county, final String state, final String city, final String zipCode) {
        this.county = county;
        this.state = state;
        this.city = city;
        this.zipCode = zipCode;
    }

    public void update(final String county, final String state, final String city, final String zipCode) {
        this.county = county;
        this.state = state;
        this.city = city;
        this.zipCode = zipCode;
    }

    @Override
    public String toString() {
        return String.format("%s %s %s %s", county, state, city, zipCode);
    }
}
  • ํ…Œ์ŠคํŠธ ์ฝ”๋“œ
    ๊ฐ™์€ ์ฐธ์กฐ๊ฐ’์„ ๋ฐ”๋ผ๋ณด๊ณ  ์žˆ์–ด์„œ ๊ฐ’์ด ๋ณ€ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.
@Test
void ํ•˜์œ„์ฐธ์กฐ๋ณ€์ˆ˜_๋ณ€๊ฒฝ_์‹œ_๊ฐ€๋ณ€() {
    // when
    Address address = new Address("county", "state", "city", "zipCode");
    Person person = new Person("์ด๋ฆ„", 20, address);
    
    // given
    address.update("county-1", "state-1", "city-1", "zipCode-1");
    
    //then
    assertEquals(person.getAddress(), address);
}

 

ํ•ด๊ฒฐ 3) ๋ถˆ๋ณ€๊ฐ์ฒด์˜ ์ฐธ์กฐ๋ณ€์ˆ˜๋„ ๋ถˆ๋ณ€์œผ๋กœ ๋งŒ๋“ค์ž.

Address ๋ณ€์ˆ˜๋„ final ์œผ๋กœ ๋ถˆ๋ณ€ํ™”ํ•œ๋‹ค.

  • ํ•ด๊ฒฐ ํ›„ ์ฝ”๋“œ
public class Address {
    private final String county;
    private final String state;
    private final String city;
    private final String zipCode;

    public Address(final String county, final String state, final String city, final String zipCode) {
        this.county = county;
        this.state = state;
        this.city = city;
        this.zipCode = zipCode;
    }

    @Override
     public String toString() {
        return String.format("%s %s %s %s", county, state, city, zipCode);
    }
}

 

๋ฌธ์ œ 4) ์ฐธ์กฐ๋ณ€์ˆ˜๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ: ์ปฌ๋ ‰์…˜

์ปฌ๋ ‰์…˜ ์ฐธ์กฐ๋ณ€์ˆ˜์˜ ๊ฒฝ์šฐ๋ฅผ ์•Œ์•„๋ณด์ž.

  • ๋ณ€๊ฒฝ ์ „ ์ฝ”๋“œ
    • Person ๊ฐ์ฒด: containsHobby() ๋กœ ํ™•์ธํ•˜๋ ค๋Š” Hobby๋ฅผ ํฌํ•จ ์—ฌ๋ถ€๋งŒ ๋ฆฌํ„ดํ•ด์ค€๋‹ค.
public class Person {
    private final String name;
    private final int age;
    private final Address address;
    private final List<Hobby> hobbies;

    public Person(String name, int age, Address address, List<Hobby> hobbies) {
        this.name = name;
        this.age = age;
        this.address = address;
        this.hobbies = hobbies;
    }

    public boolean containsHobby(Hobby hobby) {
        return hobbies.contains(hobby);
    }
}
  • ํ…Œ์ŠคํŠธ ์ฝ”๋“œ
  @Test
  void ์ปฌ๋ ‰์…˜์ฐธ์กฐ๋ณ€์ˆ˜์—_์š”์†Œ์ถ”๊ฐ€ํ•˜๋ฉด_๊ฐ€๋ณ€() {
      // when
      Address address = new Address("county", "state", "city", "zipCode");
      List<Hobby> hobbies = new ArrayList<>();
      hobbies.add(new Hobby("ํด๋ผ์ด๋ฐ"));
      hobbies.add(new Hobby("๋…์„œ"));
      Person person = new Person("์ด๋ฆ„", 20, address, hobbies);

      // given
      Hobby requestHobby = new Hobby("์ฝ”๋”ฉ");
      hobbies.add(requestHobby);

      // then
      assertTrue(person.containsHobby(requestHobby));
  }
  • ๋ฐ–์—์„œ ์ปฌ๋ ‰์…˜์— ์š”์†Œ๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ํ•ด๋‹น ๊ฐ’์„ ์ฐธ์กฐํ•˜๋Š” ๋ถˆ๋ณ€ํ•˜๋ ค๋Š”๊ฐ์ฒด์˜ ์ปฌ๋ ‰์…˜์š”์†Œ๋„ ๊ฐ€๋ณ€์ด๋‹ค.

 

ํ•ด๊ฒฐ 4-1) new ArrayList<>(target) ๋กœ ์ƒ์„ฑ

  • ๋ณ€๊ฒฝ ํ›„ ์ฝ”๋“œ
public Person(String name, int age, Address address, List<Hobby> hobbies) {
    this.name = name;
    this.age = age;
    this.address = address;
    this.hobbies = new ArrayList<>(hobbies); // ์ƒˆ๋กญ๊ฒŒ ์ƒ์„ฑ
}

์œ„์˜ ํ…Œ์ŠคํŠธ๋ฅผ ๋‹ค์‹œ ์‹คํ–‰ํ•˜๋ฉด ๊ธฐ๋Œ€ํ•œ๋Œ€๋กœ ์‹คํŒจํ•œ๋‹ค.

 

ํ•ด๊ฒฐ 4-2) getter ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ, Collections.unmodifiableList() ํ™œ์šฉํ•˜์ž.

4-1 ์˜ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋”๋ผ๋„ getter ๋กœ ์ปฌ๋ ‰์…˜์„ ๋ฐ˜ํ™˜ํ•˜๋ฉด ์—ฌ์ „ํžˆ ๋ถˆ๋ณ€์ด ์ง€์ผœ์ง€์ง€ ์•Š๋Š”๋‹ค. getter ๋กœ ์ปฌ๋ ‰์…˜์„ ๋ฐ˜ํ™˜ํ•  ๋• Collections.unmodifiableList()๋ฅผ ํ™œ์šฉํ•˜์ž.

public List<Hobby> getHobbies() {
    return Collections.unmodifiableList(hobbies);
}

 

ํ•ด๊ฒฐ 4-3) (์ž๋ฐ”10 ๋ถ€ํ„ฐ ๊ฐ€๋Šฅ) List.copyOf(tatget) ์œผ๋กœ ๋ถˆ๋ณ€์ปฌ๋ ‰์…˜ ์ƒ์„ฑ

์ž๋ฐ”10 ๋ถ€ํ„ฐ๋Š” ์ƒ์„ฑํ•  ๋•Œ ๋ถ€ํ„ฐ ๋ณต์‚ฌ์ƒ์„ฑ ํ•  ๋•Œ ๋ถ€ํ„ฐ ๋ถˆ๋ณ€ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด์ฃผ๋Š” ๋ฉ”์†Œ๋“œ๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ๋‹ค. ์ž๋ฐ”8 ์ดํ›„์˜ LTS์ธ ์ž๋ฐ”11, ์ž๋ฐ”17์„ ์‚ฌ์šฉํ•˜๋ฉด ํ•ด๋‹น ๋ฉ”์†Œ๋“œ๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๊ฒ ๋‹ค.

  • ๋ณ€๊ฒฝ ํ›„ ์ฝ”๋“œ
public Person(String name, int age, Address address, List<Hobby> hobbies) {
    this.name = name;
    this.age = age;
    this.address = address;
    this.hobbies = List.copyOf(hobbies); // List.copyOf() ํ™œ์šฉ
}
  • List.copyOf() ์ฝ”๋“œ
static <E> List<E> copyOf(Collection<? extends E> coll) {
    return ImmutableCollections.listCopy(coll);
}

List ์˜ copyOf() ๋Š” ์ˆ˜์ •์ด ๋ถˆ๊ฐ€ํ•œ ์ปฌ๋ ‰์…˜์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.