item 89 hyowon - JAVA-JIKIMI/EFFECTIVE-JAVA3 GitHub Wiki

[item 89]์ธ์Šคํ„ด์Šค ์ˆ˜๋ฅผ ํ†ต์ œํ•ด์•ผ ํ•œ๋‹ค๋ฉด readResolve๋ณด๋‹ค๋Š” ์—ด๊ฑฐ ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜๋ผ

์ตœ์ดˆ ์ฝ”๋“œ

public class Elvis {
	public static final Elvis INSTANCE = new Elvis();
	private Elvis();

	public void leaveTheBuilding() {...}

	// ์ธ์Šคํ„ด์Šค ํ†ต์ œ๋ฅผ ์œ„ํ•œ readResolve
	public Object readResolve() {
		return INSTANCE;
	}
}
  • ์œ„์™€ ๊ฐ™์€ ์‹ฑ๊ธ€ํ„ด์— readResolve ๋ฉ”์„œ๋“œ๋ฅผ ์ฃผ์–ด ์ธ์Šคํ„ด์Šค๋ฅผ ํ†ต์ œํ•จ
    • readResolve๋Š” ์ธ์Šคํ„ด์Šค๋ฅผ ์—ญ์ง๋ ฌํ™” ํ•  ๊ฒฝ์šฐ์— ๊ธฐ์กด ์—ญ์ง๋ ฌํ™”ํ•  ๋•Œ ์‚ฌ์šฉํ–ˆ๋˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๋ถˆ๋Ÿฌ์™€ ์ธ์Šคํ„ด์Šค๊ฐ€ ์˜ค์ง ํ•˜๋‚˜๋งŒ ๋งŒ๋“ค์–ด์งˆ ์ˆ˜ ์žˆ๊ฒŒ ๋ณด์žฅํ•ด์ฃผ๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.
    • readResolve ๊ธฐ๋Šฅ์„ ์ด์šฉํ•˜๋ฉด readObject๊ฐ€ ๋งŒ๋“ค์–ด๋‚ธ ์ธ์Šคํ„ด์Šค๋ฅผ ๋‹ค๋ฅธ ๊ฒƒ์œผ๋กœ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ๋‹ค. ๋ฉ”์„œ๋“œ๋ฅผ ์ ์ ˆํžˆ ์ •์˜ํ•ด ๋‘”๋‹ค๋ฉด, ์—ญ์ง๋ ฌํ™”๋œ ์ƒˆ ๊ฐ์ฒด๋ฅผ ์ธ์ˆ˜๋กœ ํ•˜์—ฌ ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜๊ณ , ๊ธฐ์กด (์ง๋ ฌํ™” ํ•˜๊ธฐ์ „์ธ) ๊ฐ์ฒด ์ฐธ์กฐ๊ฐ€ ๋ฐ˜ํ™˜๋œ๋‹ค. ์ดํ›„ ์—ญ์ง๋ ฌํ™”๋œ ์ƒˆ ๊ฐ์ฒด๋Š” ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜ ๋Œ€์ƒ์ด ๋œ๋‹ค.
    • ๋ฌธ์ œ์ ) readResolve๋ฅผ ์ธ์Šคํ„ด์Šค ํ†ต์ œ ๋ชฉ์ ์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ๊ฐ์ฒด ์ฐธ์กฐ ํƒ€์ž… ์ธ์Šคํ„ด์Šค ํ•„๋“œ๋Š” ๋ชจ๋‘ transient๋กœ ์„ ์–ธํ•ด์•ผ ํ•œ๋‹ค.
  • ์ฆ‰, ์‹ฑ๊ธ€ํ„ด์— transient๊ฐ€ ์„ ์–ธ๋˜์ง€ ์•Š์€ ํ•„๋“œ๊ฐ€ ์žˆ๋‹ค๋ฉด ๊ณต๊ฒฉ์˜ ์—ฌ์ง€๊ฐ€ ์ƒ๊ธด๋‹ค.

first draft

public class Elvis implements Serializable {
	public static final Elvis INSTANCE = new Elvis();
  private Elvis() { }

  private String[] favoriteSongs = {"Hound Dog", "Heartbreak Hotel"};
 
  public void printFavorites() {
    System.out.println(Arrays.toString(favoriteSongs));
  }
  
  private Object readResolve() {
    return INSTANCE;
  }
}
  • ์‹ฑ๊ธ€ํ„ด์ด non_transient ํ•œ ์ฐธ์กฐ ํ•„๋“œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๋ฉด, ๊ทธ ํ•„๋“œ์˜ ๋‚ด์šฉ์€ readResolve ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋˜๊ธฐ ์ „์— ์—ญ์ง๋ ฌํ™”๋œ๋‹ค.
  • ์ž˜ ์กฐ์ž‘๋œ ์ŠคํŠธ๋ฆผ์„ ์จ์„œ ํ•ด๋‹น ์ฐธ์กฐ ํ•„๋“œ์˜ ๋‚ด์šฉ์ด ์—ญ์ง๋ ฌํ™”๋˜๋Š” ์‹œ์ ์— ํ•ด๋‹น ์ธ์Šคํ„ด์Šค์˜ ์ฐธ์กฐ๋ฅผ ํ›”์ณ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

first draft ๊ณต๊ฒฉ(๋„๋‘‘ ํด๋ž˜์Šค ๋ฐ ์‹คํ–‰์ฝ”๋“œ)

public class ElvisStealer implements Serializable {
  private static final long serialVersionUID = 0;
  static Elvis impersonator;
  private Elvis payload;
  
  private Object readResolve() {
    // resolve๋˜๊ธฐ ์ „์˜ Elvis ์ธ์Šคํ„ด์Šค์˜ ์ฐธ์กฐ๋ฅผ ์ €์žฅ
    impersonator = payload;
    // favoriteSongs ํ•„๋“œ์— ๋งž๋Š” ํƒ€์ž…์˜ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜
    return new String[] {"There is no cow level"};
  }
}

public class ElvisImpersonator {
  private static final byte[] serializedForm = new byte[]{
      (byte) 0xac, (byte) 0xed, 0x00, 0x05, 0x73, 0x72, 0x00, 0x05,
      // ์ฝ”๋“œ ์ƒ๋žต
  };
  
  private static Object deserialize(byte[] sf) {
		try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(sf)) {
      try (ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream)) {
        return objectInputStream.readObject();
      } catch (IOException | ClassNotFoundException e) {
        throw new IllegalArgumentException(e);
      }
  }
  
  public static void main(String[] args) {
    // ElvisStealer.impersonator ๋ฅผ ์ดˆ๊ธฐํ™”ํ•œ ๋‹ค์Œ,
    // ์ง„์งœ Elvis(์ฆ‰, Elvis.INSTANCE)๋ฅผ ๋ฐ˜ํ™˜
    Elvis elvis = (Elvis) deserialize(serializedForm);
    Elvis impersonator = ElvisStealer.impersonator;
    elvis.printFavorites(); // [Hound Dog, Heartbreak Hotel]
    impersonator.printFavorites(); // [There is no cow level]
  }
}
  • ์‹ฑ๊ธ€ํ„ด์— ์„œ๋กœ ๋‹ค๋ฅธ ๋‘ ๊ฐœ์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Œ์„ ์ฆ๋ช…ํ•œ ์ฝ”๋“œ
  • ์ฐธ์กฐ๋ฅผ ํ›”์ณ์™€ ๋‹ค๋ฅธ ๊ฐ’์„ ๋„ฃ์—ˆ๋‹ค(impersonator)
  • ๋ชจ๋“  ํ•„๋“œ๋ฅผ transient ์„ ์–ธ์„ ํ•ด์„œ ๊ณ ์น  ์ˆ˜๋„ ์žˆ์ง€๋งŒ, Elvis๋ฅผ ์›์†Œ ํ•˜๋‚˜์งœ๋ฆฌ ์—ด๊ฑฐ ํƒ€์ž…์œผ๋กœ ๋ฐ”๊พธ๋Š” ํŽธ์ด ๋” ๋‚ซ๋‹ค.

ํ•ด๊ฒฐ์ฑ…

public enum Elvis {
	INSTANCE;
	private String[] favoriteSongs = 
		{"Hound Dog", "Heartbreak Hotel"};
	private void printFavourites() {
		System.out.println(Arrays.toString(favoriteSongs));
	}
}
  • ์ง๋ ฌํ™” ๊ฐ€๋Šฅํ•œ ์ธ์Šคํ„ด์Šค ํ†ต์ œ ํด๋ž˜์Šค๋ฅผ ์—ด๊ฑฐ ํƒ€์ž…์„ ์ด์šฉํ•ด ๊ตฌํ˜„ํ•˜๋ฉด ์„ ์–ธํ•œ ์ƒ์ˆ˜ ์™ธ์— ๋‹ค๋ฅธ ๊ฐ์ฒด๋Š” ์กด์žฌํ•˜์ง€ ์•Š์Œ์„ ์ž๋ฐ”๊ฐ€ ๋ณด์žฅํ•ด์ค€๋‹ค.
  • ๊ณต๊ฒฉ์ž๊ฐ€ AccessibleObject.setAccessible ๋ฉ”์„œ๋“œ์™€ ๊ฐ™์€ ํŠน๊ถŒ ๋ฉ”์„œ๋“œ๋ฅผ ์•…์šฉํ–ˆ์„ ๋•Œ๋Š” ์˜ˆ์™ธ๋‹ค. (์ž„์˜์˜ ๋„ค์ดํ‹ฐ๋ธŒ ์ฝ”๋“œ๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ํŠน๊ถŒ์„ ๊ฐ€๋กœ์ฑˆ ๊ณต๊ฒฉ์ž์—๊ฒŒ๋Š” ๋ชจ๋“  ๋ฐฉ์–ด๊ฐ€ ๋ฌด๋ ฅํ™”๋œ๋‹ค.)

readResolve ๋ฐฉ์‹์€ ์–ธ์ œ ์‚ฌ์šฉ?

  • ์ปดํŒŒ์ผํƒ€์ž„์—๋Š” ์–ด๋–ค ์ธ์Šคํ„ด์Šค๋“ค์ด ์žˆ๋Š”์ง€ ์•Œ ์ˆ˜ ์—†๋Š” ์ƒํ™ฉ์—” ์—ด๊ฑฐ ํƒ€์ž… ์‚ฌ์šฉ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.

readResolve ๋ฉ”์„œ๋“œ์˜ ์ ‘๊ทผ์ œํ•œ์ž ์ž‘์„ฑ

  • final ํด๋ž˜์Šค๋ผ๋ฉด private
  • final์ด ์•„๋‹Œ ํด๋ž˜์Šค๋ผ๋ฉด ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ์„ ํ—ˆ๊ฐ€ํ•  ๋ฒ”์œ„๋ฅผ ๊ณ ๋ คํ•ด ์ ‘๊ทผ์ œํ•œ์ž๋ฅผ ์ž‘์„ฑํ•œ๋‹ค. (private, package-private, protexted, public)

 

์ •๋ฆฌ

๋ถˆ๋ณ€์‹์„ ์ง€ํ‚ค๊ธฐ ์œ„ํ•ด ์ธ์Šคํ„ด์Šค๋ฅผ ํ†ต์ œํ—ค์•ผ ํ•œ๋‹ค๋ฉด ์—ด๊ฑฐ ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜๋ผ.
์—ด๊ฑฐ ํƒ€์ž… ์‚ฌ์šฉ์ด ์—ฌ์˜์น˜ ์•Š์€ ์ƒํ™ฉ์—์„œ ์ง๋ ฌํ™”์™€ ์ธ์Šคํ„ด์Šค ํ†ต์ œ๋ฅผ ํ•˜๋ ค๋ฉด 
readResolve๋ฉ”์„œ๋“œ๋ฅผ ์ž‘์„ฑํ•ด ๋„ฃ๊ณ , ํด๋ž˜์Šค์˜ ์ฐธ์กฐ ํƒ€์ž… ์ธ์Šคํ„ด์Šค ํ•„๋“œ๋ฅผ transient๋กœ ์„ ์–ธํ•œ๋‹ค.