๐ŸŽฏ1์ฃผ์ฐจ Unit 2.6 โ€” Nested / Inner / Anonymous ํด๋ž˜์Šค

Psjยท3์ผ ์ „

F-lab

๋ชฉ๋ก ๋ณด๊ธฐ
28/42

๐ŸŽฏ Unit 2.6 โ€” Nested / Inner / Anonymous ํด๋ž˜์Šค

F-lab Java 1์ฃผ์ฐจ / Phase 2 / Unit 2.6 ๋ณธ๊ฒฉ ํ•™์Šต ์ž๋ฃŒ
9-์„น์…˜ ๋งˆ์Šคํ„ฐ ํ”„๋กฌํ”„ํŠธ ํ˜•์‹์œผ๋กœ ๊นŠ์ด ํŒŒํ—ค์นœ๋‹ค.

์„ ์ˆ˜ ์ง€์‹: Unit 2.4 (๋‹คํ˜•์„ฑ)
๋‹ค์Œ Unit: Phase 3 โ€” SOLID 5์›์น™

์ด Unit์˜ ์˜๋ฏธ: Phase 2์˜ ๋งˆ๋ฌด๋ฆฌ + ๋žŒ๋‹ค(3์ฃผ์ฐจ)์˜ ์ „์‹ .
ํด๋ž˜์Šค ์•ˆ์˜ ํด๋ž˜์Šค๋ผ๋Š” ๊ฐœ๋…์„ ์ดํ•ดํ•ด์•ผ Spring ์ฝ”๋“œ, ์ฝœ๋ฐฑ ํŒจํ„ด, ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๋ฅผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋‹ค.


๐ŸŒ 1. ์„ธ์ƒ ์† ๋น„์œ 

Nested ํด๋ž˜์Šค = "์ง‘ ์•ˆ์˜ ์ž‘์€ ๋ฐฉ"

ํฐ ์ง‘์„ ์ƒ์ƒํ•ด๋ณด์„ธ์š”. ์ง‘ ์•ˆ์—๋Š” ๋‹ค์–‘ํ•œ ๋ฐฉ์ด ์žˆ์Šต๋‹ˆ๋‹ค:

  • ๊ฑฐ์‹ค โ€” ๋ชจ๋‘๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ๊ณต๊ฐ„
  • ๋ถ€์—Œ โ€” ์š”๋ฆฌ ๋„๊ตฌ๋“ค์ด ๋ชจ์ธ ๊ณต๊ฐ„
  • ์„œ์žฌ โ€” ์ฑ…๊ณผ ์ฑ…์ƒ์ด ์žˆ๋Š” ๊ณต๊ฐ„

๊ฐ ๋ฐฉ์€:

  • ์ง‘์˜ ์ผ๋ถ€ (์ง‘๊ณผ ๋ถ„๋ฆฌ๋  ์ˆ˜ ์—†์Œ)
  • ์ง‘ ์•ˆ์˜ ๋‹ค๋ฅธ ๊ณต๊ฐ„ ํ™œ์šฉ ๊ฐ€๋Šฅ (๊ฑฐ์‹ค์—์„œ ๋ถ€์—Œ์œผ๋กœ ์Œ์‹ ๊ฐ€์ ธ์˜ด)
  • ๋ฐ–์—์„œ๋Š” ์ž˜ ์•ˆ ๋ณด์ž„ (์ง‘ ์•ˆ์—์„œ๋งŒ ์˜๋ฏธ)

โ†’ Nested ํด๋ž˜์Šค๋„ ๋งˆ์ฐฌ๊ฐ€์ง€ โ€” ์™ธ๋ถ€ ํด๋ž˜์Šค ์•ˆ์—์„œ๋งŒ ์˜๋ฏธ ์žˆ๋Š” ์ž‘์€ ๊ณต๊ฐ„.


Anonymous ํด๋ž˜์Šค = "์ž„์‹œ ๋ฉ”๋ชจ์ง€"

ํšŒ์˜ ์ค‘ ์ž ๊น ๋ฉ”๋ชจํ•  ์ผ์ด ์ƒ๊ฒผ์Šต๋‹ˆ๋‹ค:

  • ์ •์‹ ๋…ธํŠธ๋ฅผ ๊บผ๋‚ด๊ธฐ์—” ๋ฒˆ๊ฑฐ๋กœ์›€
  • ํฌ์ŠคํŠธ์ž‡ ํ•œ ์žฅ ์— ์“ฑ ์ ์Œ
  • ํšŒ์˜ ๋๋‚˜๋ฉด ๋ฒ„๋ฆผ (๋˜๋Š” ๊ทธ ์ž๋ฆฌ์—์„œ๋งŒ ์‚ฌ์šฉ)

ํŠน์ง•:

  • ์ด๋ฆ„ ์—†์Œ (ํฌ์ŠคํŠธ์ž‡์€ "์ด๋ฆ„ ๋ถ™์€ ๋…ธํŠธ"๊ฐ€ ์•„๋‹˜)
  • ์ฆ‰์„์—์„œ ๋งŒ๋“ค์–ด์ง
  • ํ•œ ๋ฒˆ๋งŒ ์‚ฌ์šฉ

โ†’ Anonymous ํด๋ž˜์Šค โ€” ์ด๋ฆ„ ์—†์ด ์ฆ‰์„์—์„œ ์ •์˜ํ•˜๋Š” ์ผํšŒ์šฉ ํด๋ž˜์Šค.


๋” ์ผ์ƒ์ ์ธ ๋น„์œ  โ€” "์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ"

์Šค๋งˆํŠธํฐ ์•Œ๋žŒ ์•ฑ์—์„œ:

"์ด ์•Œ๋žŒ์ด ์šธ๋ฆฌ๋ฉด ์ด ๋™์ž‘ ์„ ํ•ด์ค˜"

์•Œ๋žŒ: 7์‹œ 30๋ถ„ โ†’ ?
   โ†“
๋‹น์‹ : ๊ทธ๋•Œ๋งŒ์˜ ๋™์ž‘ ์ •์˜
   "์Œ์•… ๋„๊ณ  + ์ปคํŠผ ์—ด๊ณ  + ์ปคํ”ผ ๋จธ์‹  ์ผœ๊ธฐ"

์ด ๋™์ž‘์€:

  • ๊ทธ ์•Œ๋žŒ๋งŒ์„ ์œ„ํ•œ ์ฝ”๋“œ
  • ์žฌ์‚ฌ์šฉ ์•ˆ ํ•จ
  • ์ด๋ฆ„ ๋ถ™์ผ ํ•„์š” ์—†์Œ

โ†’ ์ต๋ช… ํด๋ž˜์Šค์˜ ์ •์‹ . ์ž๋ฐ”์—์„œ๋Š” ํด๋ฆญ ๋ฆฌ์Šค๋„ˆ, ์ฝœ๋ฐฑ ๋“ฑ์—์„œ ์ž์ฃผ ์‚ฌ์šฉ.


๐Ÿ”ฅ 2. ํƒ„์ƒ ๋ฐฐ๊ฒฝ

์™œ ํด๋ž˜์Šค ์•ˆ์— ํด๋ž˜์Šค๊ฐ€ ํ•„์š”ํ•œ๊ฐ€?

์ž๋ฐ” ์ดˆ๊ธฐ (1.0, 1995) ์—๋Š” ๋ชจ๋“  ํด๋ž˜์Šค๊ฐ€ ๋ณ„๋„ ํŒŒ์ผ ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‹ค Java 1.1 (1997) ์—์„œ Nested ํด๋ž˜์Šค๊ฐ€ ๋„์ž….

๊ทธ ์ „์˜ ๋ฌธ์ œ โ€” ์ž‘์€ ๋„์šฐ๋ฏธ ํด๋ž˜์Šค์˜ ์™ธ๋กœ์›€:

// MyList.java
public class MyList {
    private Object[] data;
    
    public Iterator iterator() {
        return new MyListIterator(this);
    }
}

// MyListIterator.java โ€” ๋ณ„๋„ ํŒŒ์ผ
public class MyListIterator implements Iterator {
    private MyList list;
    private int index;
    
    public MyListIterator(MyList list) {
        this.list = list;
    }
    
    public boolean hasNext() { ... }
    public Object next() { ... }
}

๋ฌธ์ œ:

  • MyListIterator ๋Š” MyList ๋งŒ์„ ์œ„ํ•œ ํด๋ž˜์Šค
  • ๊ทธ๋Ÿฐ๋ฐ ๋ณ„๋„ ํŒŒ์ผ์— ์žˆ์Œ โ†’ ๊ด€๊ณ„๊ฐ€ ํ๋ ค์ง
  • MyList ์˜ ๋‚ด๋ถ€ ์ •๋ณด ์ ‘๊ทผ ์–ด๋ ค์›€ (private ๋ชป ๋ด„)
  • ๋‹ค๋ฅธ ๊ณณ์—์„œ MyListIterator ์ž˜๋ชป ์‚ฌ์šฉํ•  ์œ„ํ—˜

Nested ํด๋ž˜์Šค์˜ ๋„์ž… โ€” Java 1.1 (1997)

public class MyList {
    private Object[] data;
    
    // ํด๋ž˜์Šค ์•ˆ์˜ ํด๋ž˜์Šค โญ
    private class MyListIterator implements Iterator {
        private int index;
        
        public boolean hasNext() {
            return index < data.length;  // ์™ธ๋ถ€ ํด๋ž˜์Šค์˜ private ์ ‘๊ทผ ๊ฐ€๋Šฅ!
        }
        public Object next() { ... }
    }
    
    public Iterator iterator() {
        return new MyListIterator();
    }
}

ํšจ๊ณผ:

  • MyListIterator ๊ฐ€ MyList ์˜ ์ผ๋ถ€์ž„์„ ๋ช…ํ™•ํžˆ
  • ์™ธ๋ถ€ ํด๋ž˜์Šค์˜ private ๋ฉค๋ฒ„ ์ ‘๊ทผ ๊ฐ€๋Šฅ
  • ๋‹ค๋ฅธ ๊ณณ์—์„œ ์ž˜๋ชป ์“ธ ์ˆ˜ ์—†์Œ (์บก์Аํ™”)
  • ํ•œ ํŒŒ์ผ์— ํ•จ๊ป˜ โ†’ ๊ด€๋ฆฌ ์‰ฌ์›€

์ต๋ช… ํด๋ž˜์Šค์˜ ๋“ฑ์žฅ โ€” ์ฝœ๋ฐฑ์˜ ์‹œ๋Œ€

๋ฌธ์ œ: ํ•œ ๋ฒˆ๋งŒ ์“ธ ํด๋ž˜์Šค๋ฅผ ๋งค๋ฒˆ ์ •์˜ํ•˜๊ธฐ ๋ฒˆ๊ฑฐ๋กœ์›€

// Java 1.1 ์ด์ „ โ€” ๋ณ„๋„ ํด๋ž˜์Šค ์ •์˜ ํ•„์ˆ˜
public class MyButtonClickListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
        System.out.println("ํด๋ฆญ๋จ");
    }
}

button.addActionListener(new MyButtonClickListener());
// ๋ฒ„ํŠผ 1๊ฐœ๋ฅผ ์œ„ํ•ด ๋ณ„๋„ ํŒŒ์ผ/ํด๋ž˜์Šค ์ •์˜ โŒ

Java 1.1์˜ ์ต๋ช… ํด๋ž˜์Šค:

button.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        System.out.println("ํด๋ฆญ๋จ");
    }
});
// ํ•œ ๋ฒˆ์— ๋ โœ…

โ†’ GUI ํ”„๋กœ๊ทธ๋ž˜๋ฐ, ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ, ์ฝœ๋ฐฑ ํŒจํ„ด ์˜ ํญ๋ฐœ์  ํ™œ์šฉ.


Java 8์˜ ๋žŒ๋‹ค (2014) โ€” ์ต๋ช… ํด๋ž˜์Šค์˜ ์ง„ํ™”

20๋…„ ๊ฐ€๊นŒ์ด ์ต๋ช… ํด๋ž˜์Šค๊ฐ€ ์ฝœ๋ฐฑ์˜ ํ‘œ์ค€์ด์—ˆ์ง€๋งŒ, ๋„ˆ๋ฌด ์žฅํ™ฉํ•˜๋‹ค ๋Š” ๋ฌธ์ œ:

// ์ต๋ช… ํด๋ž˜์Šค
Comparator<String> comp = new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.length() - b.length();
    }
};

// Java 8 ๋žŒ๋‹ค โ€” ๊ฐ™์€ ์ผ์„ ํ•œ ์ค„์—
Comparator<String> comp = (a, b) -> a.length() - b.length();

๋žŒ๋‹ค์˜ ๋ณธ์งˆ:

  • ์ต๋ช… ํด๋ž˜์Šค๋ฅผ ๋” ๊ฐ„๊ฒฐํ•˜๊ฒŒ
  • ํ•จ์ˆ˜ํ˜• ์ธํ„ฐํŽ˜์ด์Šค (๋ฉ”์„œ๋“œ 1๊ฐœ) ํ•œ์ •
  • โ†’ 3์ฃผ์ฐจ์—์„œ ๋ณธ๊ฒฉ ํ•™์Šต

โ†’ Nested/Inner/Anonymous๋Š” ๋žŒ๋‹ค์˜ ์ „์‹ . ์ง„ํ™”์˜ ์—ญ์‚ฌ๋ฅผ ์•Œ์•„์•ผ ๋žŒ๋‹ค๋ฅผ ์ •ํ™•ํžˆ ์ดํ•ด.


ํ•ต์‹ฌ ํ†ต์ฐฐ

"ํด๋ž˜์Šค ์•ˆ์˜ ํด๋ž˜์Šค๋Š” '๊ฐ•ํ•œ ๊ด€๊ณ„' ๋ฅผ ํ‘œํ˜„ํ•˜๋Š” ๋„๊ตฌ๋‹ค."

์™ธ๋ถ€ ํด๋ž˜์Šค์™€ ๋–ผ๋ ค์•ผ ๋—„ ์ˆ˜ ์—†๋Š” ์ž‘์€ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค ๋•Œ, ๋ณ„๋„ ํŒŒ์ผ์ด ์•„๋‹Œ ์•ˆ์— ๋‘๋ฉด ๊ด€๊ณ„๊ฐ€ ๋ช…ํ™• ํ•ด์ง€๊ณ  ์บก์Аํ™”๊ฐ€ ๊ฐ•ํ•ด์ง„๋‹ค.

์ต๋ช… ํด๋ž˜์Šค๋Š” ๋” ๋‚˜์•„๊ฐ€ ์ด๋ฆ„์กฐ์ฐจ ํ•„์š” ์—†๋Š” ์ผํšŒ์šฉ ๊ฐ์ฒด ๋ฅผ ์ฆ‰์„์—์„œ ๋งŒ๋“œ๋Š” ๋„๊ตฌ. ์ด ์‚ฌ๊ณ ๋ฐฉ์‹์ด ๋žŒ๋‹ค๋กœ ์ง„ํ™”.


๐Ÿ’ฃ 3. ์—†์œผ๋ฉด ์ƒ๊ธฐ๋Š” ๋ฌธ์ œ

Nested ํด๋ž˜์Šค๊ฐ€ ์—†๋‹ค๋ฉด

์‹œ๋‚˜๋ฆฌ์˜ค: ILIC ์šด์ž„ ์‹œ์Šคํ…œ์˜ ํŽ˜์ด์ง€๋„ค์ด์…˜ ๊ฒฐ๊ณผ

// โŒ ๋ณ„๋„ ํŒŒ์ผ๋กœ ๋ถ„๋ฆฌํ•œ๋‹ค๋ฉด
public class PageRequest {
    private int page;
    private int size;
}

public class PageResult {
    private List<?> data;
    private int totalCount;
    private int currentPage;
}

public class FareService {
    public PageResult findByPage(PageRequest request) { ... }
}

๋ฌธ์ œ:

  • PageRequest, PageResult ๋Š” ํŽ˜์ด์ง€๋„ค์ด์…˜์—์„œ๋งŒ ์“ฐ๋Š”๋ฐ ๊ณต์šฉ ์œ„์น˜์— ์žˆ์Œ
  • ๋‹ค๋ฅธ ์˜์—ญ์—์„œ ์ž˜๋ชป ์‚ฌ์šฉํ•  ์œ„ํ—˜
  • ํŒŒ์ผ ํญ๋ฐœ (๊ด€๋ จ ํด๋ž˜์Šค๋งˆ๋‹ค ๋ณ„๋„ ํŒŒ์ผ)

ํ•ด๊ฒฐ โ€” Nested ํด๋ž˜์Šค

public class FareService {
    
    // ๋ฉ”์„œ๋“œ์˜ ์ž…๋ ฅ โ€” ์™ธ๋ถ€์—์„œ๋„ ์‚ฌ์šฉ
    public static class PageRequest {
        private int page;
        private int size;
    }
    
    // ๋ฉ”์„œ๋“œ์˜ ๊ฒฐ๊ณผ โ€” ์™ธ๋ถ€์—์„œ๋„ ์‚ฌ์šฉ
    public static class PageResult<T> {
        private List<T> data;
        private int totalCount;
        private int currentPage;
    }
    
    public PageResult<Fare> findByPage(PageRequest request) {
        // ...
    }
}

// ์‚ฌ์šฉ
FareService.PageRequest request = new FareService.PageRequest(0, 10);
FareService.PageResult<Fare> result = fareService.findByPage(request);

ํšจ๊ณผ:

  • PageRequest ๊ฐ€ FareService ์™€ ๊ด€๋ จ๋จ์ด ๋ช…ํ™•
  • ๋‹ค๋ฅธ ๊ณณ์—์„œ ์ž˜๋ชป ์‚ฌ์šฉ ์–ด๋ ค์›€
  • ํ•œ ํŒŒ์ผ์— ๋ชจ๋“  ๊ด€๋ จ ์ฝ”๋“œ

์ต๋ช… ํด๋ž˜์Šค๊ฐ€ ์—†๋‹ค๋ฉด โ€” ์ฝœ๋ฐฑ ์ง€์˜ฅ

// Spring์˜ ํŠธ๋žœ์žญ์…˜ ์ฝœ๋ฐฑ
public class FareService {
    
    public void processInTransaction() {
        transactionTemplate.execute(new MyTransactionCallback());
    }
}

// ๋ณ„๋„ ํด๋ž˜์Šค ์ •์˜
public class MyTransactionCallback implements TransactionCallback<Void> {
    @Override
    public Void doInTransaction(TransactionStatus status) {
        // ํŠธ๋žœ์žญ์…˜ ์•ˆ์—์„œ ํ•  ์ผ
        return null;
    }
}

๋ฌธ์ œ:

  • ํ•œ ๋ฒˆ๋งŒ ์“ธ ์ฝœ๋ฐฑ์„ ์œ„ํ•ด ๋ณ„๋„ ํŒŒ์ผ/ํด๋ž˜์Šค
  • ์ฝ”๋“œ ๋ถ„์‚ฐ โ†’ ํ๋ฆ„ ์ถ”์  ์–ด๋ ค์›€
  • ์ž‘์€ ๋™์ž‘ ํ•˜๋‚˜์— ํฐ ๋น„์šฉ

์ต๋ช… ํด๋ž˜์Šค ํ™œ์šฉ

public class FareService {
    public void processInTransaction() {
        transactionTemplate.execute(new TransactionCallback<Void>() {
            @Override
            public Void doInTransaction(TransactionStatus status) {
                // ํŠธ๋žœ์žญ์…˜ ์•ˆ์—์„œ ํ•  ์ผ
                return null;
            }
        });
    }
}

ํšจ๊ณผ:

  • ๊ทธ ์ž๋ฆฌ์— ์ •์˜ + ์‚ฌ์šฉ
  • ํ๋ฆ„์ด ํ•œ๋ˆˆ์— ๋ณด์ž„
  • ๋ณ„๋„ ํŒŒ์ผ ๋ถˆํ•„์š”

๋žŒ๋‹ค๋กœ ๋” ๊ฐ„๊ฒฐํ•˜๊ฒŒ (Java 8+)

public class FareService {
    public void processInTransaction() {
        transactionTemplate.execute(status -> {
            // ํŠธ๋žœ์žญ์…˜ ์•ˆ์—์„œ ํ•  ์ผ
            return null;
        });
    }
}

โ†’ ์ต๋ช… ํด๋ž˜์Šค โ†’ ๋žŒ๋‹ค๋กœ์˜ ์ง„ํ™”. ๊ฐ™์€ ์ผ, ์งง์€ ํ‘œํ˜„.


โœ… 4. ํ•ด๊ฒฐ์ฑ… โ€” 4๊ฐ€์ง€ ์ข…๋ฅ˜์™€ ๋ฌธ๋ฒ•

์ž๋ฐ”์˜ ํด๋ž˜์Šค ์ข…๋ฅ˜ โญ

1. Top-Level Class โ€” ์ผ๋ฐ˜ ํด๋ž˜์Šค (๋ณ„๋„ ํŒŒ์ผ)

2. Nested Class โ€” ํด๋ž˜์Šค ์•ˆ์˜ ํด๋ž˜์Šค
   โ”œโ”€โ”€ Static Nested Class โ€” static ํ‚ค์›Œ๋“œ ์žˆ์Œ
   โ””โ”€โ”€ Inner Class โ€” static ์—†์Œ (= ๋‚ด๋ถ€ ํด๋ž˜์Šค)
        โ”œโ”€โ”€ Member Inner โ€” ํด๋ž˜์Šค ๋ณธ๋ฌธ์— ์ •์˜
        โ”œโ”€โ”€ Local Inner โ€” ๋ฉ”์„œ๋“œ ์•ˆ์— ์ •์˜
        โ””โ”€โ”€ Anonymous Inner โ€” ์ด๋ฆ„ ์—†๋Š” ์ฆ‰์„ ์ •์˜

์šฉ์–ด ์ •๋ฆฌ:

  • Nested Class = ํฐ ์šฐ์‚ฐ (๋ชจ๋“  ์•ˆ์˜ ํด๋ž˜์Šค)
  • Inner Class = static ์—†๋Š” Nested
  • Anonymous Class = ์ด๋ฆ„ ์—†๋Š” Inner

1. Static Nested Class

public class Outer {
    private static String staticData = "static";
    private String instanceData = "instance";
    
    public static class StaticNested {
        public void show() {
            System.out.println(staticData);  // โœ… static ๋ฉค๋ฒ„ ์ ‘๊ทผ OK
            // System.out.println(instanceData);  // โŒ ์ธ์Šคํ„ด์Šค ๋ฉค๋ฒ„ ์ ‘๊ทผ X
        }
    }
}

// ์‚ฌ์šฉ โ€” ์™ธ๋ถ€ ํด๋ž˜์Šค ์ธ์Šคํ„ด์Šค ๋ถˆํ•„์š”
Outer.StaticNested nested = new Outer.StaticNested();
nested.show();

ํŠน์ง• โญ :

  • ์™ธ๋ถ€ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค ์—†์ด ์ƒ์„ฑ ๊ฐ€๋Šฅ
  • ์™ธ๋ถ€ ํด๋ž˜์Šค์˜ static ๋ฉค๋ฒ„๋งŒ ์ ‘๊ทผ
  • ๊ฐ€์žฅ ๋‹จ์ˆœํ•˜๊ณ  ์•ˆ์ „
  • Builder ํŒจํ„ด, DTO ๋“ฑ์— ์ž์ฃผ ์‚ฌ์šฉ

์–ธ์ œ ์‚ฌ์šฉ:

  • ์™ธ๋ถ€ ํด๋ž˜์Šค์™€ ๊ฐ•ํ•œ ๊ด€๊ณ„๊ฐ€ ์žˆ์ง€๋งŒ ์™ธ๋ถ€ ์ธ์Šคํ„ด์Šค์— ์ ‘๊ทผํ•  ํ•„์š” X
  • ์™ธ๋ถ€ ํด๋ž˜์Šค์˜ ๋ณด์กฐ ํด๋ž˜์Šค ์—ญํ• 

2. Inner Class โ€” Member Inner

public class Outer {
    private String instanceData = "instance";
    
    public class Inner {
        public void show() {
            System.out.println(instanceData);  // โœ… ์™ธ๋ถ€ ์ธ์Šคํ„ด์Šค ๋ฉค๋ฒ„ ์ ‘๊ทผ OK
        }
    }
}

// ์‚ฌ์šฉ โ€” ์™ธ๋ถ€ ํด๋ž˜์Šค ์ธ์Šคํ„ด์Šค ๋จผ์ € ํ•„์š”
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();  // โš ๏ธ ํŠน์ดํ•œ ๋ฌธ๋ฒ•
inner.show();

ํŠน์ง• โญ :

  • ์™ธ๋ถ€ ํด๋ž˜์Šค ์ธ์Šคํ„ด์Šค ํ•„์š” (์ƒ์„ฑ ์‹œ ์ )
  • ์™ธ๋ถ€ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค ๋ฉค๋ฒ„ ์ ‘๊ทผ ๊ฐ€๋Šฅ
  • ์ˆจ๊ฒจ์ง„ ์™ธ๋ถ€ ์ฐธ์กฐ ๋ณด์œ  (๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ์œ„ํ—˜)

์–ธ์ œ ์‚ฌ์šฉ:

  • Iterator ๊ฐ™์€ ์ž๋ฃŒ๊ตฌ์กฐ์˜ ๋ณด์กฐ ๊ฐ์ฒด
  • ์™ธ๋ถ€ ํด๋ž˜์Šค์˜ ์ƒํƒœ์— ๊ฐ•ํ•˜๊ฒŒ ์˜์กด

3. Local Inner Class โ€” ๋ฉ”์„œ๋“œ ์•ˆ์˜ ํด๋ž˜์Šค

public class Outer {
    public void doSomething() {
        // ๋ฉ”์„œ๋“œ ์•ˆ์—์„œ๋งŒ ์˜๋ฏธ ์žˆ๋Š” ํด๋ž˜์Šค
        class Helper {
            public void help() {
                System.out.println("๋„์›€");
            }
        }
        
        Helper h = new Helper();
        h.help();
    }
}

ํŠน์ง•:

  • ๋ฉ”์„œ๋“œ ๋‚ด๋ถ€์—์„œ๋งŒ ์‚ฌ์šฉ
  • ์™ธ๋ถ€ ๋ฉ”์„œ๋“œ์˜ ์ง€์—ญ ๋ณ€์ˆ˜ ์ ‘๊ทผ (final ๋˜๋Š” effectively final๋งŒ)

์–ธ์ œ ์‚ฌ์šฉ:

  • ๋ฉ”์„œ๋“œ ์•ˆ์—์„œ๋งŒ ํ•„์š”ํ•œ ๋„์šฐ๋ฏธ
  • ๊ฑฐ์˜ ์•ˆ ์”€ (ํ˜„๋Œ€์—๋Š” ๋žŒ๋‹ค๋กœ ๋Œ€์ฒด)

4. Anonymous Inner Class โ€” ์ต๋ช… ํด๋ž˜์Šค โญ

์ด๋ฆ„ ์—†์ด ์ฆ‰์„์—์„œ ์ •์˜ + ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ:

public class FareService {
    public void process() {
        // ํ•œ ์ค„์งœ๋ฆฌ ์ฝœ๋ฐฑ
        Runnable task = new Runnable() {  // โ† ์ต๋ช… ํด๋ž˜์Šค
            @Override
            public void run() {
                System.out.println("์ž‘์—… ์‹คํ–‰");
            }
        };
        
        task.run();
    }
}

๋ฌธ๋ฒ• โญ :

new ์ธํ„ฐํŽ˜์ด์Šค๋˜๋Š”ํด๋ž˜์Šค() {
    // ๋ฉ”์„œ๋“œ ๊ตฌํ˜„
};

ํŠน์ง•:

  • ์ด๋ฆ„ ์—†์Œ
  • ํ•œ ๋ฒˆ๋งŒ ์‚ฌ์šฉ (๋ณ€์ˆ˜์— ๋‹ด๊ฑฐ๋‚˜ ์ฆ‰์‹œ ์ „๋‹ฌ)
  • ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„ ๋˜๋Š” ํด๋ž˜์Šค ์ƒ์†

์ต๋ช… ํด๋ž˜์Šค์˜ ๋‘ ๊ฐ€์ง€ ํ˜•ํƒœ

ํ˜•ํƒœ 1: ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„

Comparator<String> comp = new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.length() - b.length();
    }
};

ํ˜•ํƒœ 2: ํด๋ž˜์Šค ์ƒ์†

Thread thread = new Thread() {
    @Override
    public void run() {
        System.out.println("์Šค๋ ˆ๋“œ ์‹คํ–‰");
    }
};
thread.start();

4๊ฐ€์ง€ ๋น„๊ต ๋งคํŠธ๋ฆญ์Šค โญ

Static NestedMember InnerLocal InnerAnonymous
์œ„์น˜ํด๋ž˜์Šค ๋ณธ๋ฌธํด๋ž˜์Šค ๋ณธ๋ฌธ๋ฉ”์„œ๋“œ ์•ˆ์ฆ‰์„
์ด๋ฆ„OOOX
static ํ‚ค์›Œ๋“œOXXX
์™ธ๋ถ€ ์ธ์Šคํ„ด์Šค ํ•„์š”XO(๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ์‹œ)(๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ์‹œ)
์™ธ๋ถ€ ๋ฉค๋ฒ„ ์ ‘๊ทผstatic๋งŒ๋ชจ๋‘๋ชจ๋‘ + ์ง€์—ญ๋ณ€์ˆ˜๋ชจ๋‘ + ์ง€์—ญ๋ณ€์ˆ˜
์‚ฌ์šฉ ๋นˆ๋„๋†’์Œ์ค‘๊ฐ„๋งค์šฐ ๋‚ฎ์Œ๋†’์Œ (๋žŒ๋‹ค๋กœ ๋Œ€์ฒด ์ค‘)

๐Ÿ—๏ธ 5. ๋‚ด๋ถ€ ๋™์ž‘ ์›๋ฆฌ

์ปดํŒŒ์ผ ๊ฒฐ๊ณผ โ€” ๋ณ„๋„ .class ํŒŒ์ผ ์ƒ์„ฑ โญ

์ž๋ฐ” ์ปดํŒŒ์ผ๋Ÿฌ๋Š” Nested ํด๋ž˜์Šค๋ฅผ ๋ณ„๋„ .class ํŒŒ์ผ ๋กœ ๋ถ„๋ฆฌํ•ฉ๋‹ˆ๋‹ค:

// Outer.java
public class Outer {
    public static class StaticNested { ... }
    public class Inner { ... }
    
    public void method() {
        class Local { ... }
        Runnable r = new Runnable() { ... };
    }
}

์ปดํŒŒ์ผ ๊ฒฐ๊ณผ:

Outer.class
Outer$StaticNested.class    โ† static nested
Outer$Inner.class           โ† inner
Outer$1Local.class          โ† local
Outer$1.class               โ† anonymous

$ ๊ธฐํ˜ธ โญ โ€” ์ž๋ฐ”์˜ nested ํด๋ž˜์Šค ๋ช…๋ช… ๊ทœ์น™.

โ†’ JVM ์ž…์žฅ์—์„œ๋Š” ๋ชจ๋‘ ๋ณ„๋„์˜ ํด๋ž˜์Šค.


Inner Class์˜ ์ˆจ์€ ์ฐธ์กฐ โš ๏ธ

Inner Class๋Š” ์™ธ๋ถ€ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค ์ฐธ์กฐ ๋ฅผ ์ž๋™์œผ๋กœ ๋ณด์œ :

public class Outer {
    private String data = "outer data";
    
    public class Inner {
        public void show() {
            System.out.println(data);  // ์–ด๋–ป๊ฒŒ ์ ‘๊ทผ?
        }
    }
}

์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๋ณ€ํ™˜ (์˜์‚ฌ ์ฝ”๋“œ):

public class Outer {
    private String data = "outer data";
}

public class Outer$Inner {
    private final Outer this$0;  // โ† ์ˆจ๊ฒจ์ง„ ์™ธ๋ถ€ ์ฐธ์กฐ
    
    public Outer$Inner(Outer outer) {
        this.this$0 = outer;
    }
    
    public void show() {
        System.out.println(this$0.data);  // ์™ธ๋ถ€ ์ฐธ์กฐ๋กœ ์ ‘๊ทผ
    }
}

์ค‘์š”ํ•œ ํ•จ์˜ โญโญ :

  • Inner ์ธ์Šคํ„ด์Šค๋Š” ์™ธ๋ถ€ ์ธ์Šคํ„ด์Šค์— ๊ฐ•ํ•˜๊ฒŒ ์˜์กด
  • Inner ๊ฐ€ ์‚ด์•„์žˆ์œผ๋ฉด ์™ธ๋ถ€๋„ GC ์•ˆ ๋จ โ†’ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ์œ„ํ—˜
  • ์ด๋Ÿฐ ์ด์œ ๋กœ Static Nested ๊ถŒ์žฅ (์™ธ๋ถ€ ์ฐธ์กฐ ์—†์Œ)

Anonymous ํด๋ž˜์Šค์˜ ๋ณ€ํ™˜

public class Outer {
    private int count = 0;
    
    public void process() {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                count++;  // ์™ธ๋ถ€ ํ•„๋“œ ์ ‘๊ทผ
            }
        };
    }
}

์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ƒ์„ฑ:

class Outer$1 implements Runnable {
    private final Outer this$0;
    
    public Outer$1(Outer outer) {
        this.this$0 = outer;
    }
    
    @Override
    public void run() {
        this$0.count++;
    }
}

โ†’ ์ต๋ช… ํด๋ž˜์Šค๋„ ๊ฒฐ๊ตญ ๋ณ„๋„ .class ํŒŒ์ผ + ์™ธ๋ถ€ ์ฐธ์กฐ ๋ณด์œ .


Local Inner์™€ effectively final โญ

Local/Anonymous ํด๋ž˜์Šค๊ฐ€ ๋ฉ”์„œ๋“œ ์ง€์—ญ๋ณ€์ˆ˜ ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ:

public void process() {
    int count = 10;  // ์ง€์—ญ๋ณ€์ˆ˜
    
    Runnable r = new Runnable() {
        @Override
        public void run() {
            System.out.println(count);  // โœ… ์‚ฌ์šฉ ๊ฐ€๋Šฅ
            // count = 20;  // โŒ ์ปดํŒŒ์ผ ์—๋Ÿฌ
        }
    };
}

๊ทœ์น™ โญ :

  • ์ง€์—ญ๋ณ€์ˆ˜๋Š” final ๋˜๋Š” effectively final (์‚ฌ์‹ค์ƒ final) ์ด์–ด์•ผ ํ•จ
  • = ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€

์™œ?:

  • Inner ํด๋ž˜์Šค๋Š” ์ง€์—ญ๋ณ€์ˆ˜๋ฅผ ์ž๊ธฐ ์•ˆ์— ๋ณต์‚ฌ (์™ธ๋ถ€ ๋ฉ”์„œ๋“œ ์ข…๋ฃŒ ํ›„์—๋„ ์‚ด์•„์žˆ์„ ์ˆ˜ ์žˆ์–ด์„œ)
  • ์›๋ณธ๊ณผ ๋ณต์‚ฌ๋ณธ์ด ๋‹ค๋ฅด๋ฉด ํ˜ผ๋ž€ โ†’ ๋ณ€๊ฒฝ ๊ธˆ์ง€๋กœ ์ผ๊ด€์„ฑ ์œ ์ง€

effectively final:

public void process() {
    int count = 10;  // ํ•œ ๋ฒˆ๋„ ์žฌํ• ๋‹น ์•ˆ ๋จ โ†’ effectively final
    
    Runnable r = () -> System.out.println(count);  // โœ…
}

// vs

public void process() {
    int count = 10;
    count = 20;  // ์žฌํ• ๋‹น โ†’ effectively final ์•„๋‹˜
    
    Runnable r = () -> System.out.println(count);  // โŒ ์ปดํŒŒ์ผ ์—๋Ÿฌ
}

์ต๋ช… ํด๋ž˜์Šค โ†’ ๋žŒ๋‹ค ์ง„ํ™” โญ

// ์ต๋ช… ํด๋ž˜์Šค (Java 1.1+)
button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("ํด๋ฆญ");
    }
});

// ๋žŒ๋‹ค (Java 8+)
button.addActionListener(e -> System.out.println("ํด๋ฆญ"));

๋žŒ๋‹ค๊ฐ€ ๊ฐ€๋Šฅํ•œ ์กฐ๊ฑด โ€” ํ•จ์ˆ˜ํ˜• ์ธํ„ฐํŽ˜์ด์Šค (๋ฉ”์„œ๋“œ 1๊ฐœ):

@FunctionalInterface
public interface ActionListener {
    void actionPerformed(ActionEvent e);  // ๋ฉ”์„œ๋“œ 1๊ฐœ
}

๋žŒ๋‹ค์˜ ํ•œ๊ณ„:

  • ๋ฉ”์„œ๋“œ 1๊ฐœ์ธ ์ธํ„ฐํŽ˜์ด์Šค๋งŒ
  • ๋ฉ”์„œ๋“œ ์—ฌ๋Ÿฌ ๊ฐœ๋‚˜ ํด๋ž˜์Šค ์ƒ์†์€ ์ต๋ช… ํด๋ž˜์Šค ํ•„์š”

โ†’ 3์ฃผ์ฐจ์—์„œ ๋ณธ๊ฒฉ ํ•™์Šต.


๐Ÿ’ป 6. ์‹ค์ „ ์ฝ”๋“œ ์˜ˆ์‹œ

์˜ˆ์‹œ 1: Static Nested Class โ€” Builder ํŒจํ„ด

public class Fare {
    private final Long id;
    private final int amount;
    private final String currency;
    private final Customer customer;
    
    private Fare(Builder builder) {
        this.id = builder.id;
        this.amount = builder.amount;
        this.currency = builder.currency;
        this.customer = builder.customer;
    }
    
    public static Builder builder() {
        return new Builder();
    }
    
    // Static Nested Class โ€” Builder
    public static class Builder {
        private Long id;
        private int amount;
        private String currency = "KRW";  // ๊ธฐ๋ณธ๊ฐ’
        private Customer customer;
        
        public Builder id(Long id) {
            this.id = id;
            return this;
        }
        
        public Builder amount(int amount) {
            if (amount < 0) throw new IllegalArgumentException();
            this.amount = amount;
            return this;
        }
        
        public Builder currency(String currency) {
            this.currency = currency;
            return this;
        }
        
        public Builder customer(Customer customer) {
            this.customer = customer;
            return this;
        }
        
        public Fare build() {
            return new Fare(this);
        }
    }
}

// ์‚ฌ์šฉ
Fare fare = Fare.builder()
    .id(1L)
    .amount(50000)
    .currency("USD")
    .customer(alice)
    .build();

ํšจ๊ณผ:

  • Builder ๊ฐ€ Fare ์™€ ๊ฐ•ํ•˜๊ฒŒ ์—ฐ๊ด€๋จ์ด ๋ช…ํ™•
  • ์™ธ๋ถ€์—์„œ ์ž˜๋ชป ์‚ฌ์šฉ ๋ถˆ๊ฐ€
  • ๊น”๋”ํ•œ API

โ†’ Lombok์˜ @Builder ๋„ ๋‚ด๋ถ€์ ์œผ๋กœ ์ด ํŒจํ„ด.


์˜ˆ์‹œ 2: Static Nested Class โ€” DTO/์‘๋‹ต ๋ž˜ํผ

public class FareController {
    
    @PostMapping("/api/fares")
    public ApiResponse<FareResponse> create(@RequestBody CreateFareRequest request) {
        Fare fare = fareService.create(request);
        return ApiResponse.success(FareResponse.from(fare));
    }
    
    // ์‘๋‹ต DTO โ€” Static Nested
    public static class CreateFareRequest {
        private Long customerId;
        private int amount;
        // getters/setters
    }
    
    public static class FareResponse {
        private Long id;
        private int amount;
        private String status;
        
        public static FareResponse from(Fare fare) {
            FareResponse response = new FareResponse();
            response.id = fare.getId();
            response.amount = fare.getAmount();
            response.status = fare.getStatus().name();
            return response;
        }
    }
}

ํšจ๊ณผ:

  • Request/Response๊ฐ€ ์ปจํŠธ๋กค๋Ÿฌ์™€ ๊ฐ•ํ•˜๊ฒŒ ์—ฐ๊ฒฐ
  • ๋‹ค๋ฅธ ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์ž˜๋ชป ์‚ฌ์šฉ X
  • ํ•œ ํŒŒ์ผ์—์„œ ํ๋ฆ„ ํŒŒ์•… ๊ฐ€๋Šฅ

์˜ˆ์‹œ 3: Inner Class โ€” Iterator ํŒจํ„ด

public class FareCollection {
    private Fare[] fares;
    private int size;
    
    public Iterator<Fare> iterator() {
        return new FareIterator();  // ์™ธ๋ถ€ ์ธ์Šคํ„ด์Šค์˜ it
    }
    
    // Inner Class โ€” ์™ธ๋ถ€์˜ fares, size ์ง์ ‘ ์ ‘๊ทผ
    private class FareIterator implements Iterator<Fare> {
        private int index = 0;
        
        @Override
        public boolean hasNext() {
            return index < size;  // ์™ธ๋ถ€ ํด๋ž˜์Šค์˜ size ์ ‘๊ทผ
        }
        
        @Override
        public Fare next() {
            return fares[index++];  // ์™ธ๋ถ€ ํด๋ž˜์Šค์˜ fares ์ ‘๊ทผ
        }
    }
}

// ์‚ฌ์šฉ
FareCollection collection = new FareCollection();
for (Fare fare : collection) {  // for-each ๊ฐ€๋Šฅ
    // ...
}

ํšจ๊ณผ:

  • FareIterator ๊ฐ€ FareCollection ์˜ ๋‚ด๋ถ€์— ๊ฐ•ํ•˜๊ฒŒ ์˜์กด
  • ์™ธ๋ถ€ ํด๋ž˜์Šค์˜ private ๋ฉค๋ฒ„ ์ž์œ ๋กญ๊ฒŒ ์ ‘๊ทผ
  • ๋‹ค๋ฅธ ๊ณณ์—์„œ ์‚ฌ์šฉ ๋ถˆ๊ฐ€ (private)

โ†’ ArrayList, LinkedList ๋“ฑ ์ž๋ฐ” ์ปฌ๋ ‰์…˜์ด ์ •ํ™•ํžˆ ์ด ํŒจํ„ด.


์˜ˆ์‹œ 4: Anonymous Class โ€” Spring ์ฝœ๋ฐฑ

@Service
public class FareService {
    
    @Autowired
    private TransactionTemplate transactionTemplate;
    
    public void processInTransaction(Long fareId) {
        // ์ต๋ช… ํด๋ž˜์Šค๋กœ ์ฝœ๋ฐฑ ์ •์˜
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                Fare fare = fareRepository.findById(fareId).orElseThrow();
                fare.process();
                fareRepository.save(fare);
            }
        });
    }
}

// ๋žŒ๋‹ค๋กœ (Java 8+)
public void processInTransactionLambda(Long fareId) {
    transactionTemplate.execute(status -> {
        Fare fare = fareRepository.findById(fareId).orElseThrow();
        fare.process();
        fareRepository.save(fare);
        return null;
    });
}

โ†’ ์ฝœ๋ฐฑ์€ ์ต๋ช… ํด๋ž˜์Šค์˜ ๋Œ€ํ‘œ ์‚ฌ์šฉ์ฒ˜. ๋žŒ๋‹ค๋กœ ์ง„ํ™”.


์˜ˆ์‹œ 5: Anonymous Class โ€” ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ

public class IlicEventBus {
    private final List<EventListener> listeners = new ArrayList<>();
    
    public void register(EventListener listener) {
        listeners.add(listener);
    }
}

public class IlicApplication {
    public void init() {
        // ์šด์ž„ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ๋“ฑ๋ก
        eventBus.register(new EventListener() {
            @Override
            public void onEvent(Event event) {
                if (event instanceof FareCreatedEvent fareEvent) {
                    System.out.println("์šด์ž„ ์ƒ์„ฑ๋จ: " + fareEvent.getFareId());
                }
            }
        });
        
        // ๊ฒฐ์ œ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ๋“ฑ๋ก
        eventBus.register(new EventListener() {
            @Override
            public void onEvent(Event event) {
                if (event instanceof PaymentCompletedEvent paymentEvent) {
                    System.out.println("๊ฒฐ์ œ ์™„๋ฃŒ: " + paymentEvent.getPaymentId());
                }
            }
        });
    }
}

// ๋žŒ๋‹ค๋กœ (์ธํ„ฐํŽ˜์ด์Šค ๋ฉ”์„œ๋“œ 1๊ฐœ์ผ ๋•Œ)
eventBus.register(event -> {
    if (event instanceof FareCreatedEvent fareEvent) {
        System.out.println("์šด์ž„ ์ƒ์„ฑ๋จ: " + fareEvent.getFareId());
    }
});

์˜ˆ์‹œ 6: Anonymous Class โ€” Comparator

List<Fare> fares = ...;

// ์ต๋ช… ํด๋ž˜์Šค
fares.sort(new Comparator<Fare>() {
    @Override
    public int compare(Fare a, Fare b) {
        return Integer.compare(a.getAmount(), b.getAmount());
    }
});

// ๋žŒ๋‹ค (Java 8+)
fares.sort((a, b) -> Integer.compare(a.getAmount(), b.getAmount()));

// ๋ฉ”์„œ๋“œ ์ฐธ์กฐ (๋” ๊ฐ„๊ฒฐ)
fares.sort(Comparator.comparingInt(Fare::getAmount));

โ†’ ์ต๋ช… ํด๋ž˜์Šค โ†’ ๋žŒ๋‹ค โ†’ ๋ฉ”์„œ๋“œ ์ฐธ์กฐ ์˜ ์ง„ํ™” ํ๋ฆ„.


์˜ˆ์‹œ 7: ILIC ๋„๋ฉ”์ธ์˜ Nested ํ™œ์šฉ

public class FareSearchCriteria {
    private final Range<Integer> amountRange;
    private final Range<LocalDate> dateRange;
    private final List<FareStatus> statuses;
    
    // Static Nested โ€” Range ํด๋ž˜์Šค (FareSearchCriteria ์ „์šฉ)
    public static class Range<T extends Comparable<T>> {
        private final T from;
        private final T to;
        
        public Range(T from, T to) {
            if (from != null && to != null && from.compareTo(to) > 0) {
                throw new IllegalArgumentException("from > to");
            }
            this.from = from;
            this.to = to;
        }
        
        public boolean contains(T value) {
            if (from != null && value.compareTo(from) < 0) return false;
            if (to != null && value.compareTo(to) > 0) return false;
            return true;
        }
    }
}

// ์‚ฌ์šฉ
FareSearchCriteria.Range<Integer> amountRange = new FareSearchCriteria.Range<>(1000, 100000);

โš ๏ธ 7. ์ฃผ์˜์‚ฌํ•ญ & ํ”ํ•œ ์‹ค์ˆ˜

์‹ค์ˆ˜ 1: ๋ฌด๋ถ„๋ณ„ํ•œ Inner Class ์‚ฌ์šฉ โ€” ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ โš ๏ธ

public class Outer {
    private byte[] hugeData = new byte[100 * 1024 * 1024];  // 100MB
    
    public class Inner {
        // Outer ์ฐธ์กฐ ์ž๋™ ๋ณด์œ 
    }
    
    public Inner createInner() {
        return new Inner();
    }
}

// ์‚ฌ์šฉ
Outer outer = new Outer();
Inner inner = outer.createInner();
outer = null;  // outer ์ฐธ์กฐ ํ•ด์ œ ์‹œ๋„

// ๊ทธ๋Ÿฌ๋‚˜ inner๊ฐ€ ์‚ด์•„์žˆ์œผ๋ฉด outer๋„ GC ์•ˆ ๋จ โŒ
// โ†’ 100MB ๋ˆ„์ˆ˜

ํ•ด๊ฒฐ โ€” Static Nested ์‚ฌ์šฉ:

public class Outer {
    private byte[] hugeData = new byte[100 * 1024 * 1024];
    
    public static class StaticInner {
        // Outer ์ฐธ์กฐ ์—†์Œ โœ…
    }
}

์›์น™ โญ :

"ํ•„์š” ์—†์œผ๋ฉด Static Nested"
"Inner Class๋Š” ์ •๋ง ์™ธ๋ถ€ ์ธ์Šคํ„ด์Šค์— ์ ‘๊ทผํ•ด์•ผ ํ•  ๋•Œ๋งŒ"

โ†’ Joshua Bloch (Effective Java) ๊ฐ•๋ ฅ ๊ถŒ์žฅ.


์‹ค์ˆ˜ 2: ์ต๋ช… ํด๋ž˜์Šค์—์„œ this ํ—ท๊ฐˆ๋ฆผ

public class Outer {
    public void process() {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.println(this);  // โš ๏ธ ์ด this๋Š” ๋ˆ„๊ตฌ?
                System.out.println(Outer.this);  // โ† ์™ธ๋ถ€ ํด๋ž˜์Šค์˜ this
            }
        };
    }
}

๊ทœ์น™:

  • this = ์ต๋ช… ํด๋ž˜์Šค ์ธ์Šคํ„ด์Šค
  • ์™ธ๋ถ€ํด๋ž˜์Šค๋ช….this = ์™ธ๋ถ€ ํด๋ž˜์Šค ์ธ์Šคํ„ด์Šค

์‹ค์ˆ˜ 3: Local Inner์˜ ์ง€์—ญ๋ณ€์ˆ˜ ๋ณ€๊ฒฝ ์‹œ๋„

public void process() {
    int count = 0;
    
    Runnable r = new Runnable() {
        @Override
        public void run() {
            count++;  // โŒ ์ปดํŒŒ์ผ ์—๋Ÿฌ โ€” effectively final ์•„๋‹˜
        }
    };
}

ํ•ด๊ฒฐ โ€” ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•œ ์ปจํ…Œ์ด๋„ˆ ์‚ฌ์šฉ:

public void process() {
    int[] count = {0};  // ๋ฐฐ์—ด์€ ์ฐธ์กฐ๊ฐ€ final์ด๋ฉด OK
    // ๋˜๋Š”
    AtomicInteger count = new AtomicInteger(0);
    
    Runnable r = new Runnable() {
        @Override
        public void run() {
            count[0]++;  // โœ…
            // count.incrementAndGet();  // AtomicInteger
        }
    };
}

โ†’ ์•ฝ๊ฐ„์˜ ์šฐํšŒ. ์ง„์งœ ๊ถŒ์žฅ์€ ์ƒํƒœ๋ฅผ ์™ธ๋ถ€์—์„œ ๊ด€๋ฆฌ.


์‹ค์ˆ˜ 4: ์ต๋ช… ํด๋ž˜์Šค๋ฅผ ๋žŒ๋‹ค๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋Š” ๊ฑธ ๋ชจ๋ฆ„

// โŒ ์ž๋ฐ” 8+ ํ™˜๊ฒฝ์—์„œ ๋„ˆ๋ฌด ์žฅํ™ฉ
button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        doSomething();
    }
});

// โœ… ๋žŒ๋‹ค๋กœ ๊ฐ„๊ฒฐํ•˜๊ฒŒ
button.addActionListener(e -> doSomething());

์กฐ๊ฑด โญ :

  • ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ํ•จ์ˆ˜ํ˜• ์ธํ„ฐํŽ˜์ด์Šค (๋ฉ”์„œ๋“œ 1๊ฐœ)
  • ๋žŒ๋‹ค๊ฐ€ ๊ฐ€๋Šฅ

์‹ค์ˆ˜ 5: ๋„ˆ๋ฌด ๊นŠ์€ ์ค‘์ฒฉ

// โŒ ๊ฐ€๋…์„ฑ ์ง€์˜ฅ
service.process(new Runnable() {
    @Override
    public void run() {
        helper.execute(new Callback() {
            @Override
            public void onComplete() {
                another.handle(new Handler() {
                    @Override
                    public void handle() {
                        // ... ์ฝœ๋ฐฑ ์ง€์˜ฅ โŒ
                    }
                });
            }
        });
    }
});

ํ•ด๊ฒฐ โ€” ๋žŒ๋‹ค + ๋ฉ”์„œ๋“œ ๋ถ„๋ฆฌ:

service.process(this::doWork);

private void doWork() {
    helper.execute(this::onComplete);
}

private void onComplete() {
    another.handle(this::onHandle);
}

private void onHandle() { ... }

โ†’ ์ฝœ๋ฐฑ ์ง€์˜ฅ์€ ๋น„๋™๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ๊ณ ์ „์  ๋ฌธ์ œ. ํ˜„๋Œ€์—๋Š” CompletableFuture, Reactive ๋กœ ํ•ด๊ฒฐ.


์‹ค์ˆ˜ 6: Static ํ‚ค์›Œ๋“œ ๋น ๋œจ๋ฆผ

public class Outer {
    public class Helper {  // โš ๏ธ static ๋น ์ง โ€” Inner Class ๋จ
        // ...
    }
}

// ์‚ฌ์šฉ ์‹œ
Outer.Helper h = new Outer.Helper();  // โŒ ์ปดํŒŒ์ผ ์—๋Ÿฌ
// Outer ์ธ์Šคํ„ด์Šค ํ•„์š”

Outer outer = new Outer();
Outer.Helper h = outer.new Helper();  // โš ๏ธ ํŠน์ดํ•œ ๋ฌธ๋ฒ•

๊ทœ์น™:

"๊ธฐ๋ณธ์€ static, ์ •๋ง ํ•„์š”ํ•  ๋•Œ๋งŒ ์ธ์Šคํ„ด์Šค ๋ฉค๋ฒ„ ์ ‘๊ทผ์šฉ inner"


์‹ค์ˆ˜ 7: Anonymous Class์—์„œ ์ƒˆ ๋ฉ”์„œ๋“œ ์ถ”๊ฐ€ ์‹œ๋„

Runnable r = new Runnable() {
    @Override
    public void run() { ... }
    
    public void newMethod() {  // โš ๏ธ ์ •์˜๋Š” ๊ฐ€๋Šฅ
        // ...
    }
};

r.newMethod();  // โŒ ์ปดํŒŒ์ผ ์—๋Ÿฌ โ€” Runnable์— ์—†์Œ

โ†’ ์ต๋ช… ํด๋ž˜์Šค์— ์ƒˆ ๋ฉ”์„œ๋“œ ์ถ”๊ฐ€ํ•ด๋„ ์™ธ๋ถ€์—์„œ ํ˜ธ์ถœ ๋ถˆ๊ฐ€ (์ธํ„ฐํŽ˜์ด์Šค ํƒ€์ž…์œผ๋กœ๋งŒ ๋‹ค๋ฃธ).

โ†’ ์ƒˆ ๋ฉ”์„œ๋“œ๊ฐ€ ํ•„์š”ํ•˜๋ฉด ์ด๋ฆ„ ์žˆ๋Š” ํด๋ž˜์Šค ์‚ฌ์šฉ.


๐Ÿ”— 8. ์—ฐ๊ด€ ๊ฐœ๋… ๋งต

์ง์ ‘ ์ด์–ด์ง€๋Š” ํ•™์Šต

[Phase 2 ์™„๋ฃŒ]
        โ†“
[Phase 3: SOLID 5์›์น™] โ€” ๊ฐ์ฒด ์„ค๊ณ„์˜ ํ™ฉ๊ธˆ๋ฅ 
        โ†“
[Phase 4: JVM ๋ฉ”๋ชจ๋ฆฌ ๋ชจ๋ธ]
        โ†“
... ๊ณ„์†

๋žŒ๋‹ค๋กœ์˜ ์ง„ํ™” (3์ฃผ์ฐจ)

[Anonymous Class (Java 1.1)]
        โ†“
[Lambda (Java 8)]
        โ†“
[Method Reference (Java 8)]
        โ†“
[Stream API (Java 8)]

โ†’ 3์ฃผ์ฐจ์—์„œ ๋ณธ๊ฒฉ ํ•™์Šต.


์ด Unit์˜ ๊ฐœ๋…์ด ํ™œ์šฉ๋˜๋Š” ๊ณณ

1์ฃผ์ฐจ ๋‚ด:

  • Phase 6 (์ปฌ๋ ‰์…˜): Iterator๋Š” Inner Class ํŒจํ„ด

๋ฏธ๋ž˜ ์ฃผ์ฐจ:

  • 3์ฃผ์ฐจ (๋žŒ๋‹ค): ์ต๋ช… ํด๋ž˜์Šค์˜ ์ง„ํ™” ํ˜•ํƒœ
  • 5์ฃผ์ฐจ (Spring): ApplicationListener, Configuration ์ฝœ๋ฐฑ ๋“ฑ
  • 8-9์ฃผ์ฐจ (AOP): Pointcut, Advice ์ •์˜ ์‹œ ์ต๋ช… ํด๋ž˜์Šค/๋žŒ๋‹ค
  • 15์ฃผ์ฐจ (Spring MVC): ๋‹ค์–‘ํ•œ ์ฝœ๋ฐฑ ํŒจํ„ด

Static Nested vs Inner โ€” ์˜์‚ฌ๊ฒฐ์ •

"์™ธ๋ถ€ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค ๋ฉค๋ฒ„์— ์ ‘๊ทผํ•ด์•ผ ํ•˜๋‚˜?"
        โ†“
   YES โ”€โ†’ Inner Class
   |       (Iterator, View ๋“ฑ ๊ฐ•ํ•œ ๊ฒฐํ•ฉ)
   |
   NO โ”€โ”€โ†’ Static Nested Class โญ (๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ)
           (Builder, DTO, ๋ณด์กฐ ํด๋ž˜์Šค)

์›์น™: ๊ธฐ๋ณธ์€ static, ์ •๋ง ํ•„์š”ํ•  ๋•Œ๋งŒ inner.


๋ฉด์ ‘ ๋‹จ๊ณจ ์งˆ๋ฌธ ๋งคํ•‘

์งˆ๋ฌธ์ด Unit์—์„œ์˜ ๋‹ต
"Inner Class์™€ Static Nested Class์˜ ์ฐจ์ด?"static ์œ ๋ฌด + ์™ธ๋ถ€ ์ธ์Šคํ„ด์Šค ์ ‘๊ทผ ์ฐจ์ด
"Inner Class์˜ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ์œ„ํ—˜?"์™ธ๋ถ€ ์ฐธ์กฐ ์ž๋™ ๋ณด์œ  โ†’ ์™ธ๋ถ€ GC ๋ฐฉํ•ด
"์ต๋ช… ํด๋ž˜์Šค๋ž€?"์ด๋ฆ„ ์—†์ด ์ฆ‰์„์—์„œ ์ •์˜ํ•˜๋Š” ์ผํšŒ์šฉ ํด๋ž˜์Šค
"์ต๋ช… ํด๋ž˜์Šค์™€ ๋žŒ๋‹ค์˜ ์ฐจ์ด?"๋žŒ๋‹ค๋Š” ํ•จ์ˆ˜ํ˜• ์ธํ„ฐํŽ˜์ด์Šค ํ•œ์ •, ๋” ๊ฐ„๊ฒฐ
"Local Inner Class์˜ effectively final?"์ง€์—ญ๋ณ€์ˆ˜๋Š” ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€์—ฌ์•ผ ์‚ฌ์šฉ ๊ฐ€๋Šฅ

๐Ÿ“ 9. ํ•ต์‹ฌ ์š”์•ฝ โ€” 3์ค„ ์ •๋ฆฌ

1๏ธโƒฃ Nested ํด๋ž˜์Šค๋Š” "๊ฐ•ํ•œ ๊ด€๊ณ„" ๋ฅผ ํ‘œํ˜„ํ•˜๋Š” ๋„๊ตฌ๋‹ค.

์™ธ๋ถ€ ํด๋ž˜์Šค์™€ ๋–ผ๋ ค์•ผ ๋—„ ์ˆ˜ ์—†๋Š” ์ž‘์€ ํด๋ž˜์Šค๋ฅผ ์•ˆ์— ๋‘๋ฉด ๊ด€๊ณ„๊ฐ€ ๋ช…ํ™•ํ•ด์ง€๊ณ  ์บก์Аํ™”๊ฐ€ ๊ฐ•ํ•ด์ง„๋‹ค. Static Nested (์™ธ๋ถ€ ์ฐธ์กฐ ์—†์Œ) ์™€ Inner (์™ธ๋ถ€ ์ฐธ์กฐ ๋ณด์œ ) ๋กœ ๋‚˜๋‰˜๋Š”๋ฐ, ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•ด ๊ฐ€๋Šฅํ•˜๋ฉด Static Nested ๊ถŒ์žฅ.

2๏ธโƒฃ ์ต๋ช… ํด๋ž˜์Šค๋Š” "์ด๋ฆ„ ์—†๋Š” ์ผํšŒ์šฉ ๊ฐ์ฒด" ๋ฅผ ์ฆ‰์„์—์„œ ๋งŒ๋“ ๋‹ค.

ํ•œ ๋ฒˆ๋งŒ ์“ธ ์ฝœ๋ฐฑ์ด๋‚˜ ๋ฆฌ์Šค๋„ˆ์— ์ ํ•ฉ. ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ Outer$1.class ๊ฐ™์€ ๋ณ„๋„ .class ํŒŒ์ผ๋กœ ๋ณ€ํ™˜. ํ•จ์ˆ˜ํ˜• ์ธํ„ฐํŽ˜์ด์Šค์ธ ๊ฒฝ์šฐ Java 8+ ๋žŒ๋‹ค๋กœ ๋” ๊ฐ„๊ฒฐํ•˜๊ฒŒ ํ‘œํ˜„ ๊ฐ€๋Šฅ โ€” ์ต๋ช… ํด๋ž˜์Šค๊ฐ€ ๋žŒ๋‹ค์˜ ์ „์‹ ์ด๋‹ค.

3๏ธโƒฃ Local Inner์˜ ์ง€์—ญ๋ณ€์ˆ˜๋Š” effectively final ์ด์–ด์•ผ ํ•œ๋‹ค.

๋ฉ”์„œ๋“œ ์•ˆ์˜ ํด๋ž˜์Šค๊ฐ€ ์™ธ๋ถ€ ๋ฉ”์„œ๋“œ์˜ ์ง€์—ญ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๊ทธ ๋ณ€์ˆ˜๋Š” final ๋˜๋Š” ์‚ฌ์‹ค์ƒ final (์žฌํ• ๋‹น ์•ˆ ๋จ). ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ง€์—ญ๋ณ€์ˆ˜๋ฅผ ํด๋ž˜์Šค ์•ˆ์— ๋ณต์‚ฌํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์›๋ณธ๊ณผ ๋ณต์‚ฌ๋ณธ์˜ ์ผ๊ด€์„ฑ์„ ์œ„ํ•œ ๊ฐ•์ œ. ์ด ๊ทœ์น™์€ ๋žŒ๋‹ค์—๋„ ๊ทธ๋Œ€๋กœ ์ ์šฉ๋œ๋‹ค.


๐ŸŽ“ ํ•™์Šต ์ž๊ธฐ ์ ๊ฒ€

๊ธฐ๋ณธ ์ดํ•ด

  • 4๊ฐ€์ง€ ์ข…๋ฅ˜ (Static Nested / Member Inner / Local Inner / Anonymous) ๋ฅผ ๊ตฌ๋ณ„ํ•  ์ˆ˜ ์žˆ๋‹ค
  • Static Nested์™€ Inner์˜ ๊ฒฐ์ •์  ์ฐจ์ด๋ฅผ ์•ˆ๋‹ค (์™ธ๋ถ€ ์ฐธ์กฐ)
  • ์ต๋ช… ํด๋ž˜์Šค์˜ ๋ฌธ๋ฒ•์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค
  • effectively final์˜ ์˜๋ฏธ๋ฅผ ์•ˆ๋‹ค

์‹ค์ „ ์ ์šฉ

  • ILIC ์ฝ”๋“œ์—์„œ Builder/DTO๋ฅผ Static Nested๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค
  • ์ต๋ช… ํด๋ž˜์Šค๋ฅผ ๋žŒ๋‹ค๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค (ํ•จ์ˆ˜ํ˜• ์ธํ„ฐํŽ˜์ด์Šค์ธ ๊ฒฝ์šฐ)
  • Inner Class ์‚ฌ์šฉ ์‹œ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ์œ„ํ—˜์„ ํ‰๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค

๋ฉด์ ‘ ๋Œ€๋น„ (1-2๋ถ„ ๋‹ต๋ณ€)

  • "์™œ ํด๋ž˜์Šค ์•ˆ์— ํด๋ž˜์Šค๋ฅผ?" ๋‹ต๋ณ€ ๊ฐ€๋Šฅ
  • "Static Nested์™€ Inner์˜ ์ฐจ์ด?" ๋‹ต๋ณ€ ๊ฐ€๋Šฅ
  • "์ต๋ช… ํด๋ž˜์Šค์™€ ๋žŒ๋‹ค์˜ ๊ด€๊ณ„?" ๋‹ต๋ณ€ ๊ฐ€๋Šฅ
  • "Inner Class์˜ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ์œ„ํ—˜?" ๋‹ต๋ณ€ ๊ฐ€๋Šฅ

์ž๊ธฐ ์ ๊ฒ€ ์งˆ๋ฌธ ๋‹ต๋ณ€

Q1: ์ต๋ช… ํด๋ž˜์Šค๋Š” ์–ธ์ œ ๋žŒ๋‹ค๋กœ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€?

์กฐ๊ฑด: ํ•จ์ˆ˜ํ˜• ์ธํ„ฐํŽ˜์ด์Šค (๋ฉ”์„œ๋“œ 1๊ฐœ) ์˜ ์ต๋ช… ํด๋ž˜์Šค๋งŒ ๋žŒ๋‹ค๋กœ ๋Œ€์ฒด ๊ฐ€๋Šฅ.

๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ:

// ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ๋ฉ”์„œ๋“œ 1๊ฐœ
@FunctionalInterface
public interface Runnable {
    void run();
}

// ์ต๋ช… ํด๋ž˜์Šค
Runnable r = new Runnable() {
    @Override
    public void run() {
        System.out.println("์‹คํ–‰");
    }
};

// ๋žŒ๋‹ค๋กœ ๋ณ€ํ™˜ โœ…
Runnable r = () -> System.out.println("์‹คํ–‰");

๋Œ€ํ‘œ ํ•จ์ˆ˜ํ˜• ์ธํ„ฐํŽ˜์ด์Šค:

  • Runnable (run)
  • Comparator<T> (compare)
  • Consumer<T> (accept)
  • Function<T, R> (apply)
  • Supplier<T> (get)
  • Predicate<T> (test)

๋ถˆ๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ:

1. ๋ฉ”์„œ๋“œ 2๊ฐœ ์ด์ƒ ์ธํ„ฐํŽ˜์ด์Šค

public interface MultiInterface {
    void methodA();
    void methodB();
}

// ์ต๋ช… ํด๋ž˜์Šค๋งŒ ๊ฐ€๋Šฅ โ€” ๋žŒ๋‹ค X
MultiInterface m = new MultiInterface() {
    @Override public void methodA() { ... }
    @Override public void methodB() { ... }
};

2. ํด๋ž˜์Šค ์ƒ์† (์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์•„๋‹Œ)

// ์ต๋ช… ํด๋ž˜์Šค๋กœ ํด๋ž˜์Šค ์ƒ์†
Thread t = new Thread() {
    @Override
    public void run() { ... }
};

// ๋žŒ๋‹ค๋Š” ์ธํ„ฐํŽ˜์ด์Šค ํ•œ์ • โ€” ํด๋ž˜์Šค ์ƒ์† X
// ์œ„ ์ฝ”๋“œ๋ฅผ ๋žŒ๋‹ค๋กœ? ๊ฐ€๋Šฅ โ€” Thread๋Š” Runnable์„ ๋ฐ›์Œ
Thread t = new Thread(() -> { ... });

3. ์ถ”๊ฐ€ ํ•„๋“œ/์ƒํƒœ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ

Runnable r = new Runnable() {
    private int count = 0;  // ์ƒํƒœ ๋ณด์œ 
    
    @Override
    public void run() {
        count++;
        System.out.println(count);
    }
};
// ๋žŒ๋‹ค๋Š” ์ƒํƒœ ๋ณด์œ  X โ€” ์™ธ๋ถ€ ๋ณ€์ˆ˜ ํ™œ์šฉํ•ด์•ผ

๋žŒ๋‹ค์˜ ํ•œ๊ณ„ ์ •๋ฆฌ:

  • โœ… ํ•จ์ˆ˜ํ˜• ์ธํ„ฐํŽ˜์ด์Šค (๋ฉ”์„œ๋“œ 1๊ฐœ)
  • โŒ ํด๋ž˜์Šค ์ƒ์†
  • โŒ ๋ฉ”์„œ๋“œ ์—ฌ๋Ÿฌ ๊ฐœ
  • โŒ ์ถ”๊ฐ€ ํ•„๋“œ/์ƒํƒœ

โ†’ ๊ฐ„๋‹จํ•œ ์ฝœ๋ฐฑ์€ ๋žŒ๋‹ค, ๋ณต์žกํ•œ ๊ฐ์ฒด๋Š” ์ต๋ช… ํด๋ž˜์Šค ๋˜๋Š” ๋ช…๋ช… ํด๋ž˜์Šค.


Q2: ๋‚ด๋ถ€ ํด๋ž˜์Šค๊ฐ€ ์™ธ๋ถ€ ํด๋ž˜์Šค์˜ private ํ•„๋“œ์— ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ ์ด์œ ๋Š”?

ํ•œ ์ค„ ๋‹ต: ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์™ธ๋ถ€ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค ์ฐธ์กฐ๋ฅผ ์ž๋™์œผ๋กœ ๋‚ด๋ถ€ ํด๋ž˜์Šค์— ์ฃผ์ž… ํ•˜๊ธฐ ๋•Œ๋ฌธ.

์ƒ์„ธ ์„ค๋ช…:

public class Outer {
    private String secret = "๋น„๋ฐ€";
    
    public class Inner {
        public void reveal() {
            System.out.println(secret);  // โœ… private ์ ‘๊ทผ OK
        }
    }
}

์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๋ณ€ํ™˜ (์˜์‚ฌ ์ฝ”๋“œ):

public class Outer {
    private String secret = "๋น„๋ฐ€";
    
    // ํ•ฉ์„ฑ ๋ฉ”์„œ๋“œ ์ž๋™ ์ƒ์„ฑ โญ
    static String access$000(Outer outer) {
        return outer.secret;
    }
}

public class Outer$Inner {
    final Outer this$0;  // ์™ธ๋ถ€ ์ธ์Šคํ„ด์Šค ์ฐธ์กฐ ์ž๋™ ๋ณด์œ 
    
    public Outer$Inner(Outer outer) {
        this.this$0 = outer;
    }
    
    public void reveal() {
        // ํ•ฉ์„ฑ ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ์ ‘๊ทผ
        System.out.println(Outer.access$000(this$0));
    }
}

ํ•ต์‹ฌ ๋ฉ”์ปค๋‹ˆ์ฆ˜ โญ :

  1. ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์™ธ๋ถ€ ์ธ์Šคํ„ด์Šค ์ฐธ์กฐ ์ฃผ์ž…

    • Inner ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ ์‹œ ์ž๋™์œผ๋กœ ์™ธ๋ถ€ ์ธ์Šคํ„ด์Šค ๋ฐ›์Œ
    • this$0 ๋ผ๋Š” ์ˆจ๊ฒจ์ง„ ํ•„๋“œ์— ์ €์žฅ
  2. ํ•ฉ์„ฑ ๋ฉ”์„œ๋“œ (Synthetic Method) ์ž๋™ ์ƒ์„ฑ

    • private ํ•„๋“œ ์ ‘๊ทผ์„ ์œ„ํ•ด ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ž๋™ ์ƒ์„ฑ
    • access$000, access$100 ๊ฐ™์€ ์ด๋ฆ„
  3. JVM ์ž…์žฅ์—์„œ๋Š” private ์ง์ ‘ ์ ‘๊ทผ X

    • ์‚ฌ์‹ค์€ ํ•ฉ์„ฑ ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•œ ์šฐํšŒ ์ ‘๊ทผ
    • ๊ทธ๋Ÿฌ๋‚˜ ์ž๋ฐ” ์–ธ์–ด ๋ ˆ๋ฒจ์—์„œ๋Š” ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ณด์ž„

์™œ ์ด๋ ‡๊ฒŒ ๋™์ž‘? โ€” ์–ธ์–ด์˜ ํŽธ์˜์„ฑ์„ ์œ„ํ•œ ์„ค๊ณ„:

"๋‚ด๋ถ€ ํด๋ž˜์Šค๋Š” ์™ธ๋ถ€ ํด๋ž˜์Šค์˜ ์ผ๋ถ€" ๋ผ๋Š” ์ง๊ด€์„ ์ฝ”๋“œ์—์„œ๋„ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด
private ๋ฉค๋ฒ„์—๋„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๋„์™€์คŒ

Static Nested์™€ ๋น„๊ต:

public class Outer {
    private static String staticSecret = "static ๋น„๋ฐ€";
    private String instanceSecret = "instance ๋น„๋ฐ€";
    
    public static class StaticNested {
        public void show() {
            System.out.println(staticSecret);     // โœ… static ์ ‘๊ทผ OK
            // System.out.println(instanceSecret); // โŒ ์™ธ๋ถ€ ์ธ์Šคํ„ด์Šค ์—†์Œ
        }
    }
}

โ†’ Static Nested๋Š” ์™ธ๋ถ€ ์ธ์Šคํ„ด์Šค ์ฐธ์กฐ ์—†์Œ โ†’ ์ธ์Šคํ„ด์Šค ๋ฉค๋ฒ„ ์ ‘๊ทผ X.

Inner์˜ ์œ„ํ—˜์„ฑ โš ๏ธ :
์ด ์ž๋™ ์™ธ๋ถ€ ์ฐธ์กฐ ๋•Œ๋ฌธ์—:

  • Inner ์‚ด์•„์žˆ์œผ๋ฉด ์™ธ๋ถ€๋„ GC ์•ˆ ๋จ โ†’ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜
  • ์ง๋ ฌํ™” ์‹œ ์™ธ๋ถ€ ๊ฐ์ฒด๊นŒ์ง€ ์ง๋ ฌํ™” โ†’ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋™์ž‘

๊ฒฐ๋ก :

"์™ธ๋ถ€ ์ธ์Šคํ„ด์Šค ๋ฉค๋ฒ„์— ์ ‘๊ทผํ•ด์•ผ ํ•œ๋‹ค๋ฉด Inner Class,
๊ทธ๋ ‡์ง€ ์•Š๋‹ค๋ฉด Static Nested Class๋ฅผ ์‚ฌ์šฉํ•˜๋ผ" โ€” Joshua Bloch (Effective Java)


Phase 2 ์™„๋ฃŒ โ€” ๋‹ค์Œ์œผ๋กœ

  • Phase 2 ์กธ์—… โ€” OOP 3๋Œ€ ์ถ• ์™„์„ฑ!
  • Phase 3 (SOLID ์›์น™) ํ•™์Šต ์ค€๋น„ ์™„๋ฃŒ
  • ๊ฐ์ฒด ์„ค๊ณ„์˜ ํ™ฉ๊ธˆ๋ฅ ์„ ๋งŒ๋‚  ์ค€๋น„

๐ŸŽ“ Phase 2 ์ „์ฒด ํ•™์Šต ์ •๋ฆฌ

ํ•™์Šตํ•œ 6๊ฐœ Unit

Unit์ฃผ์ œํ•ต์‹ฌ
2.1๋ฉ”์„œ๋“œ์˜ ๊ตฌ์กฐ์‹œ๊ทธ๋‹ˆ์ฒ˜, ์ ‘๊ทผ ์ œ์–ด์ž, ์˜ค๋ฒ„๋กœ๋”ฉ
2.2๊ฐ€๋ณ€์ธ์žํƒ€์ž…... ๋ณ€์ˆ˜๋ช…, ๋ฐฐ์—ด๋กœ ๋ณ€ํ™˜
2.3์ƒ์†๊ณผ ์ƒ์„ฑ์ž ์ฒด์ด๋‹extends, super(), ๋‹จ์ผ ์ƒ์†
2.4๋‹คํ˜•์„ฑ โ˜…โ˜…โ˜…VMT, ๋™์  ๋ฐ”์ธ๋”ฉ, OCP์˜ ํ† ๋Œ€
2.5instanceof์™€ ํ˜•๋ณ€ํ™˜์•ˆ์ „ํ•œ ๋‹ค์šด์บ์ŠคํŒ…, ํŒจํ„ด ๋งค์นญ
2.6Nested/Inner/Anonymous๋žŒ๋‹ค์˜ ์ „์‹ , ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ์ฃผ์˜

Phase 2์˜ ํ•ต์‹ฌ ํ†ต์ฐฐ

"ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค ์ค„ ์•ˆ๋‹ค โ‰  OOP๋ฅผ ์•ˆ๋‹ค"

๋ฉ”์„œ๋“œ, ์ƒ์†, ๋‹คํ˜•์„ฑ, ํ˜•๋ณ€ํ™˜, ๋‚ด๋ถ€ ํด๋ž˜์Šค โ€” ์ด ๋ชจ๋‘๊ฐ€ ๊ฐ์ฒด๋“ค์ด ํ˜‘๋ ฅํ•˜๋Š” ๋ฐฉ์‹ ์„ ํ‘œํ˜„ํ•˜๋Š” ๋„๊ตฌ. ์ด ๋„๊ตฌ๋“ค์„ ์ž˜ ์กฐํ•ฉํ•ด์•ผ SOLID ์›์น™ (Phase 3) ์œผ๋กœ ์ข‹์€ ์„ค๊ณ„๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.

Phase 3 ๋ฏธ๋ฆฌ๋ณด๊ธฐ โ€” SOLID ์›์น™

Phase 2์˜ ๋„๊ตฌ๋“ค์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์‚ฌ์šฉํ•˜๋Š” 5๊ฐ€์ง€ ์›์น™:

  • SRP โ€” ๋‹จ์ผ ์ฑ…์ž„
  • OCP โ€” ๊ฐœ๋ฐฉ-ํ์‡„ (๋‹คํ˜•์„ฑ์˜ ๊ฒฐ๊ณผ โญ)
  • LSP โ€” ๋ฆฌ์Šค์ฝ”ํ”„ ์น˜ํ™˜ (์ƒ์†์˜ ์ง„์งœ ๊ทœ์น™)
  • ISP โ€” ์ธํ„ฐํŽ˜์ด์Šค ๋ถ„๋ฆฌ
  • DIP โ€” ์˜์กด ์—ญ์ „ (๋‹คํ˜•์„ฑ์˜ ์‘์šฉ)

โ†’ Phase 2์˜ OOP 3๋Œ€ ์ถ•์ด SOLID๋กœ ๊ฝƒํ•€๋‹ค.

profile
Software Developer

0๊ฐœ์˜ ๋Œ“๊ธ€