<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>wisdom</title>
    <link>https://wisdom-cs.tistory.com/</link>
    <description>프로그래밍과 공부 기록</description>
    <language>ko</language>
    <pubDate>Thu, 2 Jul 2026 00:01:17 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>wisdom11</managingEditor>
    <image>
      <title>wisdom</title>
      <url>https://tistory1.daumcdn.net/tistory/4619195/attach/8612a094d065411298a0d7b14a87d55e</url>
      <link>https://wisdom-cs.tistory.com</link>
    </image>
    <item>
      <title>블로그 이사갑니다!</title>
      <link>https://wisdom-cs.tistory.com/68</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요! 티스토리에서 벨로그로 이전했습니다!ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 모든 글을 다 옮기진 않았지만, 남은 글들은 내용 수정도 하고 앞으로 공부할 내용이랑 합쳐서 올리려고 해요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@wisdom-one&quot;&gt;https://velog.io/@wisdom-one&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1661109073824&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;wisdom-one (wisdom) - velog&quot; data-og-description=&quot;SQL vs NoSQL RDB는 관계형 데이터 모델을 기초로 두고, 모든 데이터를 2차원 테이블 형태로 표현하는 데이터베이스를 말한다.구성된 테이블이 다른 테이블들과 관계를 맺고 모여있는 집합체이며, &quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@wisdom-one&quot; data-og-url=&quot;https://velog.io/@wisdom-one&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/mKlYf/hyPvTREbHe/H1RJI9TijGCkqTF6vpJXWK/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/bhkGvn/hyPv5q0NmF/YnIPVZPCACsawjyFmnXgGK/img.png?width=894&amp;amp;height=1200&amp;amp;face=0_0_894_1200,https://scrap.kakaocdn.net/dn/cJKYKG/hyPv300B2I/xh1VtyYVP0wmgBffI7gOIk/img.png?width=894&amp;amp;height=1200&amp;amp;face=0_0_894_1200&quot;&gt;&lt;a href=&quot;https://velog.io/@wisdom-one&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@wisdom-one&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/mKlYf/hyPvTREbHe/H1RJI9TijGCkqTF6vpJXWK/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/bhkGvn/hyPv5q0NmF/YnIPVZPCACsawjyFmnXgGK/img.png?width=894&amp;amp;height=1200&amp;amp;face=0_0_894_1200,https://scrap.kakaocdn.net/dn/cJKYKG/hyPv300B2I/xh1VtyYVP0wmgBffI7gOIk/img.png?width=894&amp;amp;height=1200&amp;amp;face=0_0_894_1200');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;wisdom-one (wisdom) - velog&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;SQL vs NoSQL RDB는 관계형 데이터 모델을 기초로 두고, 모든 데이터를 2차원 테이블 형태로 표현하는 데이터베이스를 말한다.구성된 테이블이 다른 테이블들과 관계를 맺고 모여있는 집합체이며,&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;블로그 이사 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;티스토리는 다양한 커스텀이 가능하다는 장점이 있지만, 저에게는 직접 커스텀을 해야 한다는게 꽤 번거로운 작업으로 느껴졌어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요즘 부쩍 정리할 게 많아서.. 최대한 빠르고 간단하게 정리할 수 있는 블로그가 저한테는 더 맞는 것 같다는 생각을 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 커스텀 없이 간단하게 글을 예쁘게 쓸 수 있는 velog로&amp;nbsp; 이사가게 되었어요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주변에서도 요즘은 거의 벨로그를 많이 하더라구요!&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몇 번 써보니까 velog는 노션에 정리한 걸 복사 붙여넣기 해도 보기 좋고, 확실히 커스텀은 불가하지만 자체 UI가 깔끔하고 예뻐서 만족하고 있습니다!&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>wisdom11</author>
      <guid isPermaLink="true">https://wisdom-cs.tistory.com/68</guid>
      <comments>https://wisdom-cs.tistory.com/68#entry68comment</comments>
      <pubDate>Mon, 22 Aug 2022 04:14:59 +0900</pubDate>
    </item>
    <item>
      <title>[Effective Java] 아이템35: ordinal 메서드 대신 인스턴스 필드를 사용하라</title>
      <link>https://wisdom-cs.tistory.com/67</link>
      <description>&lt;div class=&quot;markdown-body&quot;&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;✔️ ordinal 메서드란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 열거 타입 상수는 하나의 정숫값에 대응된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 모든 열거 타입은 해당 상수가 그 열거 타입에서 몇 번째 위치인지를 반환하는 메서드를 제공하는데, 이 메서드가 &lt;code&gt;ordinal&lt;/code&gt; 메서드다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;✔️ ordinal의 잘못된 사용&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언뜻 보면 굉장히 편리한 메서드처럼 보이고, 이 메서드를 이용하고 싶은 유혹에 빠지기 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 아이템 이름에서도 알 수 있듯이 사용하지 않는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연주자가 1명인 솔로(solo)부터 10명인 디텍트(dectet)까지 정의한 열거 타입 예제를 보자.&lt;/p&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;public enum Ensemble {
    SOLO, DUET, TRIO, QUARTET, QUINTET,
    SEXTET, SEPTET, OCTET, NONET, DECTET;

    public int numberOfMusicians() { return ordinal() + 1; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동작은 하지만 끔찍한 코드다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;상수 선언 순서를 바꾸는 순간 numberOfMusicians 가 오동작한다.&lt;/li&gt;
&lt;li&gt;이미 사용 중인 정수와 값이 같은 상수는 추가할 방법이 없다. &lt;br /&gt;예를 들어, 8중주(octet) 상수가 이미 있기 때문에 똑같이 8명이 연주하는 복4중주(double quartet)는 추가할 수 없다.&lt;/li&gt;
&lt;li&gt;중간에 값을 비워둘 수도 없다. &lt;br /&gt;예를 들어, 12명이 연주하는 3중 4중주(triple quartet)를 추가한다고 해보자. 그러려면 중간에 11명짜리 상수도 채워야 하는데 11명으로 구성된 연주를 일컫는 이름이 없기 때문에 더미(dummy) 상수를 같이 추가해야만 한다. &lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Enum의 API 문서를 보면 ordinal에 대해 이렇게 쓰여 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;ldquo;대부분의 프로그래머는 이 메서드를 쓸 일이 없다. 이 메서드는 &lt;b&gt;EnumSet과 EnumMap과 같이 열거 타입 기반의 범용 자료구조에 쓸 목적&lt;/b&gt;으로 설계되었다.&amp;rdquo;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;따라서, 이런 용도가 아니라면 ordinal 메서드는 절대 사용하지 말자.&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;✔️ 인스턴스 필드 사용&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 ordinal 메서드를 사용하고 numberOfMusicians를 구현하는 방법은 무엇일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그것은 바로 &lt;b&gt;열거 타입 상수에 연결된 값을 인스턴스 필드에 저장&lt;/b&gt;하는 것이다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;public enum Ensemble {
    SOLO(1), DUET(2), TRIO(3), QUARTET(4), QUINTET(5),
    SEXTET(6), SEPTET(7), OCTET(8), DOUBLE_QUARTET(8),
    NONET(9), DECTET(10), TRIPLE_QUARTET(12);

    private final int numberOfMusicians;
    Ensemble(int size) { this.numberOfMusicians = size; }
    public int numberOfMusicians() { return numberOfMusicians; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;numberOfMusicians 인스턴스에 연주자의 인원 수를 저장하면, 같은 값을 갖는 상수가 존재해도 되고(OCTET, DOUBLE_QUARTET), 중간에 빈 값(11)이 있어도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;</description>
      <category>Language/Java</category>
      <author>wisdom11</author>
      <guid isPermaLink="true">https://wisdom-cs.tistory.com/67</guid>
      <comments>https://wisdom-cs.tistory.com/67#entry67comment</comments>
      <pubDate>Mon, 15 Aug 2022 12:01:48 +0900</pubDate>
    </item>
    <item>
      <title>[Spring Data JPA] 기본 사용법 정리</title>
      <link>https://wisdom-cs.tistory.com/66</link>
      <description>&lt;div class=&quot;markdown-body&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;제목을 입력해주세요_-002.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Qh8cm/btrJsOQytGe/W1XK075cDrjIYdrbp7swd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Qh8cm/btrJsOQytGe/W1XK075cDrjIYdrbp7swd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Qh8cm/btrJsOQytGe/W1XK075cDrjIYdrbp7swd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQh8cm%2FbtrJsOQytGe%2FW1XK075cDrjIYdrbp7swd0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;720&quot; data-filename=&quot;제목을 입력해주세요_-002.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;전에 공부했던 Spring Data JPA의 기본 사용법을 정리하고자 한다.&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✔️ &lt;b&gt;Dependency&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;build.gradle 파일의 dependencies 부분에 다음을 추가하자.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;implementation &amp;lsquo;org.springframework.boot:spring-boot-starter-data-jpa&amp;rsquo;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✔️ &lt;b&gt;공통 인터페이스, JpaRepository 적용&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring&amp;nbsp;Data&amp;nbsp;JPA는 &lt;b&gt;&lt;code&gt;JpaRepository&lt;/code&gt;&lt;/b&gt; 라는 공통 인터페이스를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 인터페이스는 기본적인 CRUD와 페이징 등 유용한 기능을 제공해주며, 적용하는 방법도 간단하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;code&gt;JpaRepository&amp;lt;T, ID&amp;gt;&lt;/code&gt;&lt;/b&gt;를 상속하는 인터페이스를 구성하면 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;T&lt;/code&gt;&lt;/b&gt;: 엔티티 타입&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;ID&lt;/code&gt;&lt;/b&gt;: 식별자 타입&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public interface EntityRepository extends JpaRepository&amp;lt;T, ID&amp;gt; {}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+) &lt;b&gt;&lt;code&gt;@Repository&lt;/code&gt;&lt;/b&gt; 애너테이션은 생략 가능하다. 컴포넌트 스캔과 JPA 예외를 스프링 예외로 변환하는 과정을 스프링 데이터 JPA가 자동으로 처리해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✔️&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;주요 메서드&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JpaRepository가 기본적으로 제공하는 주요 메소드는 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style7&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 265px;&quot;&gt;&lt;b&gt;메서드&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 401px;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 265px;&quot;&gt;&lt;b&gt;&lt;code&gt;&amp;lt;S extends T&amp;gt; S save(S)&lt;/code&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 401px;&quot;&gt;새로운 엔티티는 저장하고, 이미 있는 엔티티는 병합한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 265px;&quot;&gt;&lt;b&gt;&lt;code&gt;delete(T)&lt;/code&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 401px;&quot;&gt;엔티티 하나를 삭제한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 265px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;&lt;code&gt;Optional&amp;lt;T&amp;gt; findById(ID)&lt;/code&gt;&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 401px;&quot;&gt;ID로 엔티티 하나를 조회한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 265px;&quot;&gt;&lt;b&gt;&lt;code&gt;List&amp;lt;T&amp;gt; findAll(...)&lt;/code&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 401px;&quot;&gt;모든 엔티티를 조회한다.&lt;br /&gt;정렬(Sort)이나 페이징(Pageable) 조건을 파라미터로 제공할 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✔️&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Query Creation&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Data JPA는 메서드의 이름으로 쿼리를 생성해준다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 JpaRepository를 상속한 인터페이스 안에 규칙에 맞게 메서드를 선언해주기만 하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  이름 규칙&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드 이름에 관한 규칙은 다음과 같다. (By 다음에는 필드명이 들어간다)&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style7&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;text-align: center; width: 18.6047%; height: 20px;&quot;&gt;&lt;b&gt;쿼리 종류&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 100%; height: 20px;&quot;&gt;&lt;b&gt;이름 규칙&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 18.6047%; text-align: center; height: 20px;&quot;&gt;조회&lt;/td&gt;
&lt;td style=&quot;width: 100%; height: 20px;&quot;&gt;&lt;b&gt;&lt;code&gt;find...By&lt;/code&gt;&lt;/b&gt;, &lt;b&gt;&lt;code&gt;read...By&lt;/code&gt;&lt;/b&gt;, &lt;b&gt;&lt;code&gt;query...By&lt;/code&gt;&lt;/b&gt;, &lt;b&gt;&lt;code&gt;get...By&lt;/code&gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 18.6047%; text-align: center; height: 20px;&quot;&gt;COUNT&lt;/td&gt;
&lt;td style=&quot;width: 100%; height: 20px;&quot;&gt;&lt;b&gt;&lt;code&gt;count...By&lt;/code&gt;&lt;/b&gt; 반환타입 long&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 18.6047%; text-align: center; height: 20px;&quot;&gt;EXISTS&lt;/td&gt;
&lt;td style=&quot;width: 100%; height: 20px;&quot;&gt;&lt;b&gt;&lt;code&gt;exists...By&lt;/code&gt;&lt;/b&gt; 반환타입 boolean&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 18.6047%; text-align: center; height: 20px;&quot;&gt;삭제&lt;/td&gt;
&lt;td style=&quot;width: 82.3219%; height: 20px;&quot;&gt;&lt;b&gt;&lt;code&gt;delete...By&lt;/code&gt;&lt;/b&gt;, &lt;b&gt;&lt;code&gt;remove...By&lt;/code&gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 18.6047%; text-align: center; height: 19px;&quot;&gt;DISTINCT&lt;/td&gt;
&lt;td style=&quot;width: 100%; height: 19px;&quot;&gt;&lt;b&gt;&lt;code&gt;findDistinct&lt;/code&gt;&lt;/b&gt;, &lt;b&gt;&lt;code&gt;findMemberDistinctBy&lt;/code&gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 18.6047%; text-align: center; height: 19px;&quot;&gt;LIMIT&lt;/td&gt;
&lt;td style=&quot;width: 100%; height: 19px;&quot;&gt;&lt;b&gt;&lt;code&gt;findFirst3&lt;/code&gt;&lt;/b&gt;, &lt;b&gt;&lt;code&gt;findFirst&lt;/code&gt;&lt;/b&gt;, &lt;b&gt;&lt;code&gt;findTop&lt;/code&gt;&lt;/b&gt;, &lt;b&gt;&lt;code&gt;findTop3&lt;/code&gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;color: #0b0a0a;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;+ &lt;/span&gt;&lt;/span&gt;엔티티의 필드명이 변경되면 인터페이스에 정의한 메서드 이름도 꼭 함께 변경해야 한다&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;. &lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;그렇지 않으면 애플리케이션을 시작하는 시점에 오류가 발생한다&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #0b0a0a;&quot;&gt;  키워드 종류&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0b0a0a;&quot;&gt;메서드 이름에 들어가는 키워드는 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style7&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 18.6047%;&quot;&gt;&lt;b&gt;Keywords&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 37.3256%;&quot;&gt;&lt;b&gt;Sample&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 43.9535%;&quot;&gt;&lt;b&gt;JPQL snippet&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.6047%;&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;code&gt;Distinct&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 37.3256%;&quot;&gt;&lt;span&gt;findDistinctByLastnameAndFirstname&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 43.9535%;&quot;&gt;&lt;span&gt;select distinct &amp;hellip; where x.lastname = ?1 and x.firstname = ?2&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.6047%;&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;code&gt;And&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 37.3256%;&quot;&gt;&lt;span&gt;findByLastnameAndFirstname&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 43.9535%;&quot;&gt;&lt;span&gt;&amp;hellip; where x.lastname = ?1 and x.firstname = ?2&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.6047%;&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;code&gt;Or&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 37.3256%;&quot;&gt;&lt;span&gt;findByLastnameOrFirstname&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 43.9535%;&quot;&gt;&lt;span&gt;&amp;hellip; where x.lastname = ?1 or x.firstname = ?2&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.6047%;&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;code&gt;Is&lt;/code&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;Equals&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 37.3256%;&quot;&gt;&lt;span&gt;findByFirstname,findByFirstnameIs,findByFirstnameEquals&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 43.9535%;&quot;&gt;&lt;span&gt;&amp;hellip; where x.firstname = ?1&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.6047%;&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;code&gt;Between&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 37.3256%;&quot;&gt;&lt;span&gt;findByStartDateBetween&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 43.9535%;&quot;&gt;&lt;span&gt;&amp;hellip; where x.startDate between ?1 and ?2&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.6047%;&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;code&gt;LessThan&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 37.3256%;&quot;&gt;&lt;span&gt;findByAgeLessThan&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 43.9535%;&quot;&gt;&lt;span&gt;&amp;hellip; where x.age &amp;lt; ?1&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.6047%;&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;code&gt;LessThanEqual&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 37.3256%;&quot;&gt;&lt;span&gt;findByAgeLessThanEqual&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 43.9535%;&quot;&gt;&lt;span&gt;&amp;hellip; where x.age &amp;lt;= ?1&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.6047%;&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;code&gt;GreaterThan&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 37.3256%;&quot;&gt;&lt;span&gt;findByAgeGreaterThan&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 43.9535%;&quot;&gt;&lt;span&gt;&amp;hellip; where x.age &amp;gt; ?1&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.6047%;&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;code&gt;GreaterThanEqual&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 37.3256%;&quot;&gt;&lt;span&gt;findByAgeGreaterThanEqual&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 43.9535%;&quot;&gt;&lt;span&gt;&amp;hellip; where x.age &amp;gt;= ?1&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.6047%;&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;code&gt;After&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 37.3256%;&quot;&gt;&lt;span&gt;findByStartDateAfter&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 43.9535%;&quot;&gt;&lt;span&gt;&amp;hellip; where x.startDate &amp;gt; ?1&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.6047%;&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;code&gt;Before&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 37.3256%;&quot;&gt;&lt;span&gt;findByStartDateBefore&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 43.9535%;&quot;&gt;&lt;span&gt;&amp;hellip; where x.startDate &amp;lt; ?1&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.6047%;&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;code&gt;IsNull&lt;/code&gt;,&lt;span&gt;&amp;nbsp;&lt;br /&gt;&lt;/span&gt;&lt;code&gt;Null&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 37.3256%;&quot;&gt;&lt;span&gt;findByAge(Is)Null&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 43.9535%;&quot;&gt;&lt;span&gt;&amp;hellip; where x.age is null&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.6047%;&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;code&gt;IsNotNull&lt;/code&gt;,&lt;span&gt;&amp;nbsp;&lt;br /&gt;&lt;/span&gt;&lt;code&gt;NotNull&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 37.3256%;&quot;&gt;&lt;span&gt;findByAge(Is)NotNull&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 43.9535%;&quot;&gt;&lt;span&gt;&amp;hellip; where x.age not null&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.6047%;&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;code&gt;Like&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 37.3256%;&quot;&gt;&lt;span&gt;findByFirstnameLike&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 43.9535%;&quot;&gt;&lt;span&gt;&amp;hellip; where x.firstname like ?1&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.6047%;&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;code&gt;NotLike&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 37.3256%;&quot;&gt;&lt;span&gt;findByFirstnameNotLike&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 43.9535%;&quot;&gt;&lt;span&gt;&amp;hellip; where x.firstname not like ?1&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.6047%;&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;code&gt;StartingWith&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 37.3256%;&quot;&gt;&lt;span&gt;findByFirstnameStartingWith&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 43.9535%;&quot;&gt;&lt;span&gt;&amp;hellip; where x.firstname like ?1&lt;span&gt;&amp;nbsp;&lt;/span&gt;(parameter bound with appended&lt;span&gt;&amp;nbsp;&lt;/span&gt;%)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.6047%;&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;code&gt;EndingWith&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 37.3256%;&quot;&gt;&lt;span&gt;findByFirstnameEndingWith&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 43.9535%;&quot;&gt;&lt;span&gt;&amp;hellip; where x.firstname like ?1&lt;span&gt;&amp;nbsp;&lt;/span&gt;(parameter bound with prepended&lt;span&gt;&amp;nbsp;&lt;/span&gt;%)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.6047%;&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;code&gt;Containing&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 37.3256%;&quot;&gt;&lt;span&gt;findByFirstnameContaining&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 43.9535%;&quot;&gt;&lt;span&gt;&amp;hellip; where x.firstname like ?1&lt;span&gt;&amp;nbsp;&lt;/span&gt;(parameter bound wrapped in&lt;span&gt;&amp;nbsp;&lt;/span&gt;%)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.6047%;&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;code&gt;OrderBy&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 37.3256%;&quot;&gt;&lt;span&gt;findByAgeOrderByLastnameDesc&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 43.9535%;&quot;&gt;&lt;span&gt;&amp;hellip; where x.age = ?1 order by x.lastname desc&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.6047%;&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;code&gt;Not&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 37.3256%;&quot;&gt;&lt;span&gt;findByLastnameNot&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 43.9535%;&quot;&gt;&lt;span&gt;&amp;hellip; where x.lastname &amp;lt;&amp;gt; ?1&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.6047%;&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;code&gt;In&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 37.3256%;&quot;&gt;&lt;span&gt;findByAgeIn(Collection&amp;lt;Age&amp;gt; ages)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 43.9535%;&quot;&gt;&lt;span&gt;&amp;hellip; where x.age in ?1&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.6047%;&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;code&gt;NotIn&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 37.3256%;&quot;&gt;&lt;span&gt;findByAgeNotIn(Collection&amp;lt;Age&amp;gt; ages)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 43.9535%;&quot;&gt;&lt;span&gt;&amp;hellip; where x.age not in ?1&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.6047%;&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;code&gt;True&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 37.3256%;&quot;&gt;&lt;span&gt;findByActiveTrue()&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 43.9535%;&quot;&gt;&lt;span&gt;&amp;hellip; where x.active = true&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.6047%;&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;code&gt;False&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 37.3256%;&quot;&gt;&lt;span&gt;findByActiveFalse()&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 43.9535%;&quot;&gt;&lt;span&gt;&amp;hellip; where x.active = false&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.6047%;&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;code&gt;IgnoreCase&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 37.3256%;&quot;&gt;&lt;span&gt;findByFirstnameIgnoreCase&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 43.9535%;&quot;&gt;&lt;span&gt;&amp;hellip; where UPPER(x.firstname) = UPPER(?1)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #0b0a0a;&quot;&gt;쿼리 메서드의 파라미터에는 특별한 파라미터를 사용할 수도 있다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;Pageable&lt;/code&gt;&lt;/b&gt;,&amp;nbsp;&lt;b&gt;&lt;code&gt;Slice&lt;/code&gt;&lt;/b&gt;,&amp;nbsp;&lt;b&gt;&lt;code&gt;Sort&lt;/code&gt;&lt;/b&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1660239900915&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Page&amp;lt;User&amp;gt; findByLastname(String lastname, Pageable pageable);

Slice&amp;lt;User&amp;gt; findByLastname(String lastname, Pageable pageable);

List&amp;lt;User&amp;gt; findByLastname(String lastname, Sort sort);

List&amp;lt;User&amp;gt; findByLastname(String lastname, Pageable pageable);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;자세한 내용은 공식문서를 참고하자.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods&amp;nbsp;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;</description>
      <category>Back-End/Spring Boot</category>
      <author>wisdom11</author>
      <guid isPermaLink="true">https://wisdom-cs.tistory.com/66</guid>
      <comments>https://wisdom-cs.tistory.com/66#entry66comment</comments>
      <pubDate>Fri, 12 Aug 2022 03:41:56 +0900</pubDate>
    </item>
    <item>
      <title>[Effective Java] 아이템34: int 상수 대신 열거 타입을 사용하라</title>
      <link>https://wisdom-cs.tistory.com/65</link>
      <description>&lt;div class=&quot;markdown-body&quot;&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;✔️ 열거 타입 (Enum Type)&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  열거 패턴의 한계&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바에서 열거 타입을 지원하기 전에는 &lt;span style=&quot;color: #1a5490; font-weight: bold;&quot;&gt;&lt;code&gt;정수 열거 패턴&lt;/code&gt;&lt;/span&gt;(int enum pattern)을 사용하곤 했다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public static final int APPLE_FUJI 		= 0;
public static final int APPLE_PIPPIN 		= 1;
public static final int APPLE_GRANNY_SMITH 	= 2;

public static final int GRAPE_NAVEL 		= 0;
public static final int GRAPE_TEMPLE 		= 1;
public static final int GRAPE_BLOOD 		= 2;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정수 열거 패턴에는 단점이 많다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;타입 안전을 보장할 수 없다.&lt;/li&gt;
&lt;li&gt;표현력이 좋지 않다.&lt;/li&gt;
&lt;li&gt;정수 열거 패턴을 위한 별도 이름공간(namespace)를 지원하지 않는다.&lt;br /&gt;즉, 이름 충돌을 방지하기 위해서는 접두어를 써서 구분해야 한다. (사과용 상수에는 APPLE_, 오렌지용 상수는 ORANGE_를 붙였다)&lt;/li&gt;
&lt;li&gt;문자열로 출력하기가 까다롭다.&lt;/li&gt;
&lt;li&gt;같은 정수 열거 그룹에 속한 모든 상수를 순회하는 방법이 마땅치 않다. (심지어 전체 상수 개수도 알 수 없다)&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 열거 패턴의 단점을 모두 해결해주는 것이 바로 &lt;span style=&quot;color: #1a5490; font-weight: bold;&quot;&gt;&lt;code&gt;열거 타입&lt;/code&gt;&lt;/span&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  열거 타입이란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;열거 타입이란, 일정 개수의 상수 값을 정의한 다음, 그 외의 값은 허용하지 않는 타입이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바의 열거 타입은 &lt;b&gt;완전한 형태의 클래스&lt;/b&gt;라서 다른 언어의 열거 타입보다 훨씬 강력하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;열거 타입: 클래스. 외부에서 접근할 수 있는 생성자를 제공하지 않으므로 사실상 final이다.&lt;/li&gt;
&lt;li&gt;상수: 상수 하나당 자신의 인스턴스를 하나씩 만들어, public static final 필드로 공개한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  열거 타입의 장점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 열거 타입 선언으로 만들어진 인스턴스들은 딱 하나씩만 존재한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 열거 타입은 인스턴스 통제된다.&lt;br /&gt;&lt;code&gt;&lt;span style=&quot;color: #1a5490; font-weight: bold;&quot;&gt;싱글턴&lt;/span&gt;&lt;/code&gt;은 원소가 하나뿐인 열거 타입이라고 할 수 있고, &lt;code&gt;&lt;span style=&quot;color: #1a5490; font-weight: bold;&quot;&gt;열거 타입&lt;/span&gt;&lt;/code&gt;은 싱글턴을 일반화한 형태라고 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 컴파일타임 타입 안전성을 제공한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 타입의 값을 할당하려하면 컴파일 오류가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 열거 타입에는 각자의 이름 공간이 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이름이 같은 상수가 있더라도 공존할 수 있다.&lt;br /&gt;열거 패턴처럼 접두어를 써서 구분할 필요가 없는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. 새로운 상수를 추가하거나 순서를 바꿔도 다시 컴파일하지 않아도 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공개되는 것은 오직 필드의 이름뿐이라, 정수 열거 패턴과 달리 상수 값이 클라이언트로 컴파일되어 각인되지 않기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5. 열거 타입의 toString 메서드는 출력하기에 적합한 문자열을 반환한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 열거 타입 값의 toString은 &lt;b&gt;상수 이름&lt;/b&gt;을 문자열로 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;6. 열거 타입에 정의된 상수들의 값 배열을 반환하는 &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;values&lt;/code&gt;&lt;/span&gt; 메서드를 제공한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;열거 타입은 &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;&lt;code&gt;values&lt;/code&gt;&lt;/b&gt;&lt;/span&gt;&amp;nbsp;라는 정적 메서드를 통해 정의된 상수 값들을 순회할 수 있다.&lt;br /&gt;이때 값들은 선언된 순서대로 저장된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;7. 임의의 메서드나 필드를 추가할 수 있고, 임의의 인터페이스를 구현하게 할 수도 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  열거 타입은 언제 써야 할까?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 원소를 컴파일타임에 다 알 수 있는 상수 집합이라면 항상 열거 타입을 사용하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 열거 타입에 정의된 상수 개수가 영원히 고정 불변일 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;열거 타입은 나중에 상수가 추가돼도 바이너리 수준에서 호환되도록 설계되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;✔️ 데이터와 메서드를 갖는 열거 타입&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 상수와 연관된 데이터를 해당 상수 자체에 내재시키고 싶을 때는 어떻게 할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;태양계의 8개 행성을 예로 보자.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public enum Planet {
    MERCURY(3.302e+23, 2.439e6),
    VENUS  (4.869e+24, 6.052e6),
    EARTH  (5.975e+24, 6.378e6),
    MARS   (6.419e+23, 3.393e6),
    JUPITER(1.899e+27, 7.149e7),
    SATURN (5.685e+26, 6.027e7),
    URANUS (8.683e+25, 2.556e7),
    NEPTUNE(1.024e+26, 2.477e7);

    private final double mass;           // 질량(단위: 킬로그램)
    private final double radius;         // 반지름(단위: 미터)
    private final double surfaceGravity; // 표면중력(단위: m / s^2)

    // 중력상수(단위: m^3 / kg s^2)
    private static final double G = 6.67300E-11;

    // 생성자
    Planet(double mass, double radius) {
        this.mass = mass;
        this.radius = radius;
        surfaceGravity = G * mass / (radius * radius);
    }

    public double mass()           { return mass; }
    public double radius()         { return radius; }
    public double surfaceGravity() { return surfaceGravity; }

    public double surfaceWeight(double mass) {
        return mass * surfaceGravity;  // F = ma
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 열거 타입 상수 오른쪽 괄호 안 숫자는 생성자에 넘겨지는 매개변수다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  열거 타입 상수 각각을 특정 데이터와 연결지으려면,&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 데이터의 &lt;span style=&quot;color: #1a5490; font-weight: bold;&quot;&gt;&lt;code&gt;필드&lt;/code&gt;&lt;/span&gt; 를 추가하고&lt;/li&gt;
&lt;li&gt;데이터를 받아 인스턴스 필드에 저장하는 &lt;span style=&quot;color: #1a5490; font-weight: bold;&quot;&gt;&lt;code&gt;생성자&lt;/code&gt;&lt;/span&gt; 를 추가하면 된다.&lt;/li&gt;
&lt;li&gt;생성자에 넘겨지는 데이터는 각 열거 타입 상수 오른쪽 괄호 안에 넣는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+) 열거 타입은 근본적으로 불변이라 모든 필드는 &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;&lt;code&gt;final&lt;/code&gt;&lt;/b&gt;&lt;/span&gt; 이어야 한다.(아이템 17)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+) 또한, 필드는 &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;&lt;code&gt;private&lt;/code&gt;&lt;/b&gt;&lt;/span&gt;&amp;nbsp;으로 두고 별도의 &lt;span style=&quot;color: #1a5490; font-weight: bold;&quot;&gt;&lt;code&gt;public 접근자 메서드&lt;/code&gt;&lt;/span&gt; 를 추가하는게 좋다.(아이템 16)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;✔️ 상수별 메서드 구현 (Constant-specific method implementation)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상수마다 동작이 달라져야 하는 상황에서는 switch 문 보다는 &lt;span style=&quot;color: #1a5490; font-weight: bold;&quot;&gt;&lt;code&gt;상수별 메서드 구현&lt;/code&gt;&lt;/span&gt;(constant-specific method implementation)을 이용하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상수별 메서드 구현이란, 추상 메서드를 선언하고 각 상수별 클래스 몸체(constant-specific class body)를 상수에 맞게 재정의하는 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 상수별 메서드 구현을 활용한 계산기 예제다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public enum Operation {
    PLUS(&quot;+&quot;) {public double apply(double x, double y) {return x + y;}},
    MINUS(&quot;-&quot;) {public double apply(double x, double y) {return x - y;}},
    TIMES(&quot;*&quot;) {public double apply(double x, double y) {return x * y;}},
    DIVIDE(&quot;/&quot;) {public double apply(double x, double y) {return x / y;}};

        private final String symbol;

    Operation(String symbol) { this.symbol = symbol; }

    @Override public String toString() { return symbol; }

        // 추상 메서드
        public abstract double apply(double x, double y);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;&lt;code&gt;apply&lt;/code&gt;&lt;/b&gt;&lt;/span&gt; 라는 추상 메서드를 선언하고, 각 상수에서 용도에 맞게 재정의하였다. (상수별 메서드 구현)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;symbol&lt;/code&gt;&lt;/span&gt;&lt;/b&gt; 이라는 데이터를 추가(상수별 데이터)하고 &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;&lt;code&gt;toString&lt;/code&gt;&lt;/b&gt;&lt;/span&gt; 을 재정의하여, 계산식을 편하게 출력할 수 있도록 하였다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 toString 을 재정의하는 경우, toString 이 반환하는 문자열을 해당 열거 타입 상수로 변환해주는 &lt;b&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;fromString&lt;/code&gt;&lt;/span&gt;&lt;/b&gt; 메서드도 함께 제공하는 걸 고려해보자.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;private static final Map&amp;lt;String, Operation&amp;gt; stringToEnum =
        Stream.of(values()).collect(
                toMap(Object::toString, e -&amp;gt; e));

// 지정한 문자열에 해당하는 Operation을 (존재한다면) 반환한다.
public static Optional&amp;lt;Operation&amp;gt; fromString(String symbol) {
    return Optional.ofNullable(stringToEnum.get(symbol));
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;✔️ 전략 열거 타입 패턴&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상수별 메서드 구현은 열거 타입 상수끼리 코드를 공유하기 어렵다는 단점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상수끼리 코드를 공유하고자 할 때는 &lt;span style=&quot;color: #1a5490; font-weight: bold;&quot;&gt;&lt;code&gt;전략 열거 타입 패턴&lt;/code&gt;&lt;/span&gt; 을 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주중과 주말의 잔업수당을 구하는 예제를 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주중(weekday)에는 오버타임이 발생할 경우 잔업수당이 주어지고, 주말(weekend)에는 무조건 잔업수당이 주어진다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;enum PayrollDay {
    MONDAY(WEEKDAY), TUESDAY(WEEKDAY), WEDNESDAY(WEEKDAY),
    THURSDAY(WEEKDAY), FRIDAY(WEEKDAY),
    SATURDAY(WEEKEND), SUNDAY(WEEKEND);

    private final PayType payType;

    PayrollDay(PayType payType) { this.payType = payType; }

    int pay(int minutesWorked, int payRate) {
        return payType.pay(minutesWorked, payRate);
    }

    // 전략 열거 타입
    enum PayType {
        WEEKDAY {
            int overtimePay(int minsWorked, int payRate) {
                return minsWorked &amp;lt;= MINS_PER_SHIFT ? 0 :
                        (minsWorked - MINS_PER_SHIFT) * payRate / 2;
            }
        },
        WEEKEND {
            int overtimePay(int minsWorked, int payRate) {
                return minsWorked * payRate / 2;
            }
        };

        abstract int overtimePay(int mins, int payRate);
        private static final int MINS_PER_SHIFT = 8 * 60;

        int pay(int minsWorked, int payRate) {
            int basePay = minsWorked * payRate;
            return basePay + overtimePay(minsWorked, payRate);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;&lt;code&gt;PayType&lt;/code&gt;&lt;/b&gt;&lt;/span&gt; 이라는 전략 열거 타입을 이용해, 주중과 주말의 잔업수당을 따로 계산하도록 하였다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;✔️ switch 문&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상수별로 다르게 동작해야 할 때, 상수별 메서드 구현과 전략 열거 타입 패턴을 이용하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;&lt;code&gt;switch&lt;/code&gt;&lt;/b&gt;&lt;/span&gt; 문의 경우, 코드가 예쁘지 않고, 깨지기 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 새로운 상수를 추가하면 case 문도 추가해야 하고, 혹시라도 깜빡한다면 제대로 동작하지 않게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, &lt;b&gt;switch 문은 열거 타입의 상수별 동작을 구현하는 데 적합하지 않다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 기존 열거 타입에 상수별 동작을 혼합해 넣을 때는 switch 문이 좋은 선택이 될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기존 열거 타입에 없는 기능을 수행하도록 하는 메서드를 추가&lt;/b&gt;할 때, 의미상 열거 타입에 속하지 않는다면 switch 문을 사용하는 것이 좋다.&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;public static Operation inverse(Operation op) {
    switch(op) {
        case PLUS:   return Operation.MINUS;
        case MINUS:  return Operation.PLUS;
        case TIMES:  return Operation.DIVIDE;
        case DIVIDE: return Operation.TIMES;

        default:  throw new AssertionError(&quot;Unknown op: &quot; + op);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 정리&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;열거 타입은 확실히 정수 상수보다 뛰어나다. 더 읽기 쉽고 안전하고 강력하다.&lt;/li&gt;
&lt;li&gt;대다수 열거 타입이 명시적 생성자나 메서드 없이 쓰이지만, 각 상수를 특정 데이터와 연결짓거나 상수마다 다르게 동작하게 할 때는 필요하다.&lt;/li&gt;
&lt;li&gt;하나의 메서드가 상수별로 다르게 동작해야 할 때는, switch 문 대신 &lt;span style=&quot;color: #1a5490; font-weight: bold;&quot;&gt;&lt;code&gt;상수별 메서드 구현&lt;/code&gt;&lt;/span&gt; 을 사용하자.&lt;/li&gt;
&lt;li&gt;열거 타입 상수 일부가 같은 동작을 공유한다면, &lt;span style=&quot;color: #1a5490; font-weight: bold;&quot;&gt;&lt;code&gt;전략 열거 타입 패턴&lt;/code&gt;&lt;/span&gt; 을 사용하자.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;</description>
      <category>Language/Java</category>
      <author>wisdom11</author>
      <guid isPermaLink="true">https://wisdom-cs.tistory.com/65</guid>
      <comments>https://wisdom-cs.tistory.com/65#entry65comment</comments>
      <pubDate>Thu, 11 Aug 2022 19:31:12 +0900</pubDate>
    </item>
    <item>
      <title>[Effective Java] 아이템33: 타입 안전 이종 컨테이너를 고려하라</title>
      <link>https://wisdom-cs.tistory.com/64</link>
      <description>&lt;div class=&quot;markdown-body&quot;&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;✔️ 타입 안전 이종 컨테이너 패턴&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제네릭은 컬렉션과 단일원소 컨테이너에 흔히 쓰인다. 이때 매개변수화되는 대상은 원소가 아닌 컨테이너 자신이다. 따라서 &lt;b&gt;하나의 컨테이너에서 매개변수화할 수 있는 타입의 수가 제한된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 더 유연한 수단이 필요할 때도 종종 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 데이터베이스의 행은 임의 개수의 열을 가질 수 있는데 모두 열을 타입 안전하게 이용할 수 있다면 멋질 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Q. 하나의 컨테이너에서 매개변수화할 수 있는 타입 개수를 제한하지 않고 사용할 수 있는 방법은 없을까?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해법은 &lt;b&gt;컨테이너 대신 키를 매개변수화&lt;/b&gt;한 다음, &lt;b&gt;컨테이너에 값을 넣거나 뺄 때 매개변수화한 키를 함께 제공&lt;/b&gt;하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 설계 방식을 &lt;span style=&quot;color: #1a5490; font-family: 'Noto Sans Demilight', 'Noto Sans KR'; font-weight: bold;&quot;&gt;&lt;code&gt;타입 안전 이종 컨테이너 패턴&lt;/code&gt;&lt;/span&gt;(type safe heterogeneous container pattern)이라고 한다.&lt;/p&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;✔️ 타입 안전 이종 컨테이너 패턴 예제&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즐겨찾는 인스턴스를 저장하고 검색할 수 있는 Favorites 클래스 예제를 보자.&lt;/p&gt;
&lt;pre class=&quot;fsharp&quot;&gt;&lt;code&gt;public class Favorites {
    private Map&amp;lt;Class&amp;lt;?&amp;gt;, Object&amp;gt; favorites = new HashMap&amp;lt;&amp;gt;();

    public &amp;lt;T&amp;gt; void putFavorite(Class&amp;lt;T&amp;gt; type, T instance) {
        favorites.put(Objects.requireNonNull(type), instance);
    }

    public &amp;lt;T&amp;gt; T getFavorite(Class&amp;lt;T&amp;gt; type) {
        return type.cast(favorites.get(type));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;키 타입 &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;Class&amp;lt;?&amp;gt;&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 타입의 Class 객체를 매개변수화한 키로 사용하며, 이러한 Class 객체를 &lt;span style=&quot;color: #1a5490; font-family: 'Noto Sans Demilight', 'Noto Sans KR'; font-weight: bold;&quot;&gt;&lt;code&gt;타입 토큰&lt;/code&gt;&lt;/span&gt;이라고 한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #1a5490; font-weight: bold;&quot;&gt;&lt;code&gt;타입 토큰&lt;/code&gt;&lt;/span&gt;(type token): 컴파일타임 타입 정보와 런타임 타입 정보를 알아내기 위해 메서드들이 주고받는 class 리터럴.&lt;/li&gt;
&lt;li&gt;키 타입에 &lt;span style=&quot;color: #1a5490; font-family: 'Noto Sans Demilight', 'Noto Sans KR'; font-weight: bold;&quot;&gt;&lt;code&gt;비한정적 와일트카드 타입&lt;/code&gt;&lt;/span&gt;을 사용하여, &lt;b&gt;모든 키가 서로 다른 매개변수화 타입일 수 있도록&lt;/b&gt; 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;값 타입 &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;Object&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;값 타입으로 &lt;b&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;Object&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;를 사용하여, 키와 값 사이의 타입 관계를 보증하지 않는다.&lt;/li&gt;
&lt;li&gt;하지만 우리는 이 관계가 성립함을 안다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;putFavorite&lt;/code&gt;&lt;/span&gt; 메서드&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Class 객체와 인스턴스를 favorites에 추가한다.&lt;/li&gt;
&lt;li&gt;이때 키와 값 사이의 타입 링크(type linkage) 정보는 버려진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;getFavorite&lt;/code&gt;&lt;/span&gt; 메서드&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Class의 &lt;b&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;cast&lt;/code&gt;&lt;/span&gt;&lt;/b&gt; 메서드를 사용해 객체 참조를 Class 객체가 가리키는 타입으로 &lt;b&gt;동적 형변환&lt;/b&gt;한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;&lt;code&gt;cast&lt;/code&gt;&lt;/b&gt;&lt;/span&gt; 메서드
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;형변환 연산자의 동적 버전&lt;/li&gt;
&lt;li&gt;주어진 인수가 Class 객체가 알려주는 타입의 인스턴스인지를 검사한 다음, 맞다음 그 인수를 그대로 반환하고 아니면 &lt;b&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;ClassCastException&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;을 던진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1660122310873&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Class&amp;lt;T&amp;gt; { 
    T cast(Object obj); 
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 사용하는 클라이언트 코드는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;public static void main(String[] args) {
      Favorites f = new Favorites();

      f.putFavorite(String.class, &quot;Java&quot;);
      f.putFavorite(Integer.class, 0xcafebabe);
      f.putFavorite(Class.class, Favorites.class);

      String favoriteString = f.getFavorite(String.class);
      int favoriteInteger = f.getFavorite(Integer.class);
      Class&amp;lt;?&amp;gt; favoriteClass = f.getFavorite(Class.class);

      System.out.printf(&quot;%s %x %s%n&quot;, favoriteString,
              favoriteInteger, favoriteClass.getName());
        // 출력 결과: Java cafebabe Favorites
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  Favorites 클래스의 제약 사항&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;1. Class 객체를 (제네릭이 아닌) 로 타입으로 넘기면 Favorites 인스턴스의 타입 안전성이 쉽게 깨진다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음에 나오는 동적 형변환을 통해 해결 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 실체화 불가 타입에는 사용할 수 없다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, String, String[]은 저장할 수 있지만 List은 저장할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 제약에 대한 완벽히 만족스러운 우회로는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;✔️ 동적 형변환&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Favorites 클래스에는 Class 객체를 (제네릭이 아닌) 로 타입으로 넘기면 Favorites 인스턴스의 타입 안전성이 쉽게 깨진다는 제약이 존재한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Q. 그렇다면, Favorites가 타입 불변식을 어기는 일이 없도록 보장하려면 어떻게 해야할까?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;동적 형변환&lt;/b&gt;을 통해 인수로 주어진 인스턴스의 타입이 type으로 명시한 타입과 같은지 확인하면 된다!&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;public &amp;lt;T&amp;gt; void putFavorite(Class&amp;lt;T&amp;gt; type, T instance) {
    favorites.put(Objects.requireNonNull(type), type.cast(instance));
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+) java.utils.Collections에는 이 방식을 적용한 컬렉션 래퍼들이 존재한다.&lt;br /&gt;&lt;b&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;checkedSet&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;, &lt;b&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;checkedList&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;, &lt;b&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;checkedMap&lt;/code&gt;&lt;/span&gt;&lt;/b&gt; 메서드&lt;/p&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;✔️ 한정적 타입 토큰&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Favorites가 사용하는 타입 토큰은 비한정적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 때로는 이 메서드들이 허용하는 타입을 제한하고 싶을 수 있는데, 이때는 &lt;span style=&quot;color: #1a5490; font-weight: bold;&quot;&gt;&lt;code&gt;한정적 타입 토큰&lt;/code&gt;&lt;/span&gt;을 활용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #1a5490; font-weight: bold;&quot;&gt;&lt;code&gt;한정적 타입 토큰&lt;/code&gt;&lt;/span&gt; 이란, &lt;span style=&quot;color: #1a5490; font-weight: bold;&quot;&gt;&lt;code&gt;한정적 타입 매개변수&lt;/code&gt;&lt;/span&gt;나 &lt;span style=&quot;color: #1a5490; font-weight: bold;&quot;&gt;&lt;code&gt;한정적 와일드카드&lt;/code&gt;&lt;/span&gt;를 사용하여 표현 가능한 타입을 제한하는 타입 토큰이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애너테이션 API는 한정적 타입 토큰을 적극적으로 사용한다.&lt;/p&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;public &amp;lt;T extends Annotation&amp;gt; T getAnnotation(Class&amp;lt;T&amp;gt; annotationType);&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;annotationType 인수는 애너테이션 타입을 뜻하는 한정적 타입 토큰이다.&lt;/li&gt;
&lt;li&gt;애너테이션된 요소는 키가 애너테이션 타입인 타입 안전 이종 컨테이너다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Q. Class&amp;lt;?&amp;gt; 타입의 객체가 있고, 이를 한정적 타입 토큰을 받는 메서드에 넘기려면 어떻게 해야 할까?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드에 넘기기 위해서는 객체를 &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;&lt;b&gt;Class&amp;lt;? extends Annotation&amp;gt;&lt;/b&gt;&lt;/code&gt;&lt;/span&gt;으로 형변환해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운 좋게도, Class 클래스는 이런 형변환을 안전하고 동적으로 수행해주는 메서드를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;&lt;code&gt;asSubclass&lt;/code&gt;&lt;/b&gt;&lt;/span&gt; 메서드&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;호출된 인스턴스 자신의 Class 객체를 인수가 명시한 클래스로 형변환한다.&lt;/li&gt;
&lt;li&gt;형변환에 성공하면 인수로 받은 클래스 객체를 반환하고, 실패하면 &lt;b&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;ClassCastException&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;을 던진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;static Annotation getAnnotation(AnnotatedElement element,
                                String annotationTypeName) {
    Class&amp;lt;?&amp;gt; annotationType = null; // 비한정적 타입 토큰
    try {
        annotationType = Class.forName(annotationTypeName);
    } catch (Exception ex) {
        throw new IllegalArgumentException(ex);
    }
    return element.getAnnotation(
            annotationType.asSubclass(Annotation.class));
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 정리&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컬렉션 API로 대표되는 일반적인 제네릭 형태에서는 한 컨테이너가 다룰 수 있는 타입 매개변수의 수가 고정되어 있다.&lt;/li&gt;
&lt;li&gt;하지만 컨테이너 자체가 아닌 키를 타입 매개변수로 바꾸면 이런 제약이 없는 &lt;span style=&quot;color: #1a5490; font-weight: bold;&quot;&gt;&lt;code&gt;타입 안전 이종 컨테이너&lt;/code&gt;&lt;/span&gt;를 만들 수 있다.&lt;/li&gt;
&lt;li&gt;타입 안전 이종 컨테이너는 &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;&lt;code&gt;Class&lt;/code&gt;&lt;/b&gt;&lt;/span&gt;를 키로 쓰며, 이런 식으로 쓰이는 Class 객체를 &lt;span style=&quot;color: #1a5490; font-weight: bold;&quot;&gt;&lt;code&gt;타입 토큰&lt;/code&gt;&lt;/span&gt;이라 한다.&lt;/li&gt;
&lt;li&gt;또한, 직접 구현한 키 타입을 쓸 수 있다.&lt;/li&gt;
&lt;li&gt;예컨대 데이터베이스의 행(컨테이너)를 표현한 &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;&lt;code&gt;DatabaseRow&lt;/code&gt;&lt;/b&gt;&lt;/span&gt; 타입에는 제네릭 타입인 &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;&lt;code&gt;Column&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/b&gt;&lt;/span&gt;를 키로 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Language/Java</category>
      <author>wisdom11</author>
      <guid isPermaLink="true">https://wisdom-cs.tistory.com/64</guid>
      <comments>https://wisdom-cs.tistory.com/64#entry64comment</comments>
      <pubDate>Wed, 10 Aug 2022 18:11:38 +0900</pubDate>
    </item>
    <item>
      <title>[Effective Java] 아이템32: 제네릭과 가변인수를 함께 쓸 때는 신중하라</title>
      <link>https://wisdom-cs.tistory.com/63</link>
      <description>&lt;div class=&quot;markdown-body&quot;&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;✔️ 가변인수 메서드와 제네릭&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;&lt;b&gt;가변인수&lt;/b&gt;&lt;/code&gt;&lt;/span&gt;(varargs, variable arguments) 메서드와 &lt;b&gt;&lt;code&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;제네릭&lt;/span&gt;&lt;/code&gt;&lt;/b&gt;은 자바 5 때 함께 추가되었으니 서로 잘 어우러지리라 기대하겠지만, 그렇지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가변인수 메서드를 호출하면 가변인수를 담기 위한 배열이 자동으로 하나 만들어지며, 이 배열은 클라이언트에게 노출된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 결과 varargs 매개변수에 &lt;b&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;제네릭&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;이나 &lt;b&gt;&lt;code&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;매개변수화 타입&lt;/span&gt;&lt;/code&gt;&lt;/b&gt;이 포함되면 알기 어려운 컴파일 경고가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;제네릭과 varargs를 혼용하면 타입 안전성이 깨지게 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;따라서 제네릭 varargs 배열 매개변수에 값을 저장하는 것은 안전하지 않다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 제네릭 배열을 프로그래머가 직접 생성하는 건 허용하지 않으면서 제네릭 varargs 매개변수를 받는 메서드를 선언할 수 있게 한 이유는 무엇일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것은 제네릭이나 매개변수화 타입의 varargs 매개변수를 받는 메서드가 실무에서 매우 유용하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 언어 설계자는 이 모순을 수용하기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;✔️ &lt;span style=&quot;color: #1a5490;&quot;&gt;@SafeVarargs&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바 7에서부터는 &lt;b&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;@SafeVarargs&lt;/code&gt;&lt;/span&gt;&lt;/b&gt; 애너테이션이 추가되어, 제네릭 가변인수 메서드 작성자가 클라이언트 측에서 발생하는 경고를 숨길 수 있게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 메서드가 타입 안전하다면 &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;&lt;b&gt;@SafeVarargs&lt;/b&gt;&lt;/code&gt;&lt;/span&gt; 애너테이션을 달아 컴파일러가 경고를 하지 않도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 안전한게 확실하지 않다면 절대 이 애너테이션을 달아서는 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Q. 그렇다면, 메서드가 안전한지는 어떻게 확신할 수 있을까?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;메서드가 varargs 매개변수를 담은 제네릭 배열에 아무것도 저장하지 않고(그 매개변수들을 덮어쓰지 않고) 그 배열의 참조가 밖으로 노출되지 않는다면(신뢰할 수 없는 코드가 배열에 접근할 수 없다면) 타입 안전하다.&amp;rdquo;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;달리 말하면, varargs 매개변수 배열이 호출자로부터 그 메서드로 &amp;ldquo;&lt;b&gt;순수하게 인수들을 전달하는 일만 한다면&lt;/b&gt;&amp;rdquo;(varargs의 목적대로만 쓰인다면) 그 메서드는 안전하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하자면, 다음의 두 가지 조건을 모두 만족하는 제네릭 varargs 메서드는 안전하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;varargs 매개변수 배열에 아무것도 저장하지 않는다.&lt;/li&gt;
&lt;li&gt;그 배열(혹은 복제본)을 신뢰할 수 없는 코드에 노출하지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;@SafeVarargs&lt;/code&gt;&lt;/span&gt;의 제약사항&lt;/b&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;재정의할 수 없는 메서드&lt;/b&gt;&lt;/span&gt;&lt;/code&gt;에만 달아야 한다.&lt;br /&gt;&amp;rarr; 재정의한 메서드도 안전할지는 보장할 수 없기 때문이다.&lt;/li&gt;
&lt;li&gt;자바 8에서는, 오직&amp;nbsp;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;&lt;b&gt;static 메서드&lt;/b&gt;&lt;/code&gt;&lt;/span&gt;와 &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;&lt;b&gt;final 인스턴스 메서드&lt;/b&gt;&lt;/code&gt;&lt;/span&gt;에만 붙일 수 있다.&lt;/li&gt;
&lt;li&gt;자바 9부터는, &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;&lt;b&gt;private 인스턴스 메서드&lt;/b&gt;&lt;/code&gt;&lt;/span&gt;에도 허용된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;✔️ 제네릭 varargs 메서드 예제&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;❎ 제네릭 매개변수 배열 참조를 노출하는 메서드 - 안전하지 않음&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;// 코드 32-2 자신의 제네릭 매개변수 배열의 참조를 노출한다. - 안전하지 않다!
static &amp;lt;T&amp;gt; T[] toArray(T... args) {
    return args;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;toArray 메서드가 반환하는 배열의 타입은 컴파일타임에 결정되는데, 그 시점에는 컴파일러에게 충분한 정보가 주어지지 않아 타입을 잘못 판단할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 자신의 varargs 매개변수 배열을 그대로 반환하면 힙 오염을 이 메서드를 호출한 쪽의 콜스택으로까지 전이하는 결과를 낳을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;static &amp;lt;T&amp;gt; T[] pickTwo(T a, T b, T c) {
    switch(ThreadLocalRandom.current().nextInt(3)) {
        case 0: return toArray(a, b);
        case 1: return toArray(a, c);
        case 2: return toArray(b, c);
    }
    throw new AssertionError(); // 도달할 수 없다.
}

public static void main(String[] args) {
    String[] attributes = pickTwo(&quot;좋은&quot;, &quot;빠른&quot;, &quot;저렴한&quot;); // ClassCastException 발생
    System.out.println(Arrays.toString(attributes));
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, pickTwo 메서드를 다음과 같이 정의한다면, 이 메서드를 본 컴파일러는 toArray에 넘길 T 인스턴스 2개를 담을 varargs 매개변수 배열을 만드는 코드를 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드가 만드는 배열의 타입은 &lt;b&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;Object[]&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;인데, 이는 pickTwo에 어떤 타입의 객체를 넘기더라도 담을 수 있는 가장 구체적인 타입이기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;main 메서드까지 작성한 후 컴파일을 하면 별다른 경고 없이 컴파일이 되지만, 실행하게 되면 &lt;b&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;ClassCastException&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;을 던진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 컴파일러가 pickTwo의 반환값인 &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;&lt;b&gt;Object[]&lt;/b&gt;&lt;/code&gt;&lt;/span&gt;을 attributes에 저장하기 위해 &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;&lt;b&gt;String[]&lt;/b&gt;&lt;/code&gt;&lt;/span&gt;로 형변환하는 코드를 자동으로 생성하였기 때문에 발생하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;Object[]&lt;/b&gt;&lt;/span&gt;&lt;/code&gt;가 &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;&lt;b&gt;String[]&lt;/b&gt;&lt;/code&gt;&lt;/span&gt;의 하위 타입이 아니기 때문에 형변환이 실패한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;✅ 제네릭 varargs 매개변수를 안전하게 사용하는 메서드 - &amp;nbsp;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;@SafeVarargs&lt;/code&gt;&lt;/span&gt; 사용&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;php&quot;&gt;&lt;code&gt;// 코드 32-3 제네릭 varargs 매개변수를 안전하게 사용하는 메서드
@SafeVarargs
static &amp;lt;T&amp;gt; List&amp;lt;T&amp;gt; flatten(List&amp;lt;? extends T&amp;gt;... lists) {
    List&amp;lt;T&amp;gt; result = new ArrayList&amp;lt;&amp;gt;();
    for (List&amp;lt;? extends T&amp;gt; list : lists)
        result.addAll(list);
    return result;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 제네릭 varargs 매개변수를 안전하게 사용하는 전형적인 예시이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 메서드에는 &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;&lt;b&gt;@SafeVarargs&lt;/b&gt;&lt;/code&gt;&lt;/span&gt; 애너테이션이 달려 있으니 선언하는 쪽과 사용하는 쪽 모두에서 경고를 내지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;제네릭이나 매개변수화 타입의 varargs 매개변수를 받는 모든 메서드에는 &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;@SafeVarargs&lt;/code&gt;&lt;/span&gt; 를 달자!&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, &lt;b&gt;&lt;a href=&quot;https://wisdom-cs.tistory.com/63#:~:text=%EC%A0%95%EB%A6%AC%ED%95%98%EC%9E%90%EB%A9%B4%2C%20%EB%8B%A4%EC%9D%8C%EC%9D%98%20%EB%91%90,%EC%BD%94%EB%93%9C%EC%97%90%20%EB%85%B8%EC%B6%9C%ED%95%98%EC%A7%80%20%EC%95%8A%EB%8A%94%EB%8B%A4.&quot;&gt;안전하지 않은(두 가지 조건을 만족하지 않는)&lt;/a&gt;&lt;/b&gt; 제네릭 varargs 메서드는 수정하여야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;✅ 제네릭 varargs 매개변수를 &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;List&lt;/code&gt;&lt;/span&gt;로 대체한 메서드&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;@SafeVarargs&lt;/b&gt;&lt;/span&gt;&lt;/code&gt; 애너테이션이 유일한 답은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아이템 28의 조언에 따라 varargs 매개변수를 List 매개변수로 바꿀 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  flatten 메서드에 적용한 예&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;static &amp;lt;T&amp;gt; List&amp;lt;T&amp;gt; flatten(List&amp;lt;List&amp;lt;? extends T&amp;gt;&amp;gt; lists) {
    List&amp;lt;T&amp;gt; result = new ArrayList&amp;lt;&amp;gt;();
    for (List&amp;lt;? extends T&amp;gt; list : lists)
        result.addAll(list);
    return result;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매개변수 선언부만 수정이 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;&lt;b&gt;List&amp;lt;? extends T&amp;gt;... lists&lt;/b&gt;&lt;/code&gt;&lt;/span&gt; &amp;rarr; &lt;code&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;List&amp;lt;List&amp;lt;? extends T&amp;gt;&amp;gt; lists&lt;/b&gt;&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    List&amp;lt;Integer&amp;gt; flatList = flatten(List.of(
            List.of(1, 2), List.of(3, 4, 5), List.of(6,7)));
    System.out.println(flatList);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 팩터리 메서드인 &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;&lt;b&gt;List.of&lt;/b&gt;&lt;/code&gt;&lt;/span&gt;를 활용하면 임의 개수의 인수를 넘길 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 사용할 수 있는 이유는 &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;&lt;b&gt;List.of&lt;/b&gt;&lt;/code&gt;&lt;/span&gt;에도 &lt;b&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;@SafeVarargs&lt;/code&gt;&lt;/span&gt;&lt;/b&gt; 애너테이션이 달려 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컴파일러가 메서드의 타입 안전성을 검증할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;@SafeVarargs&lt;/b&gt;&lt;/span&gt;&lt;/code&gt; 애너테이션을 직접 달지 않아도 되고, 실수로 안전하다고 판단할 걱정이 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트 코드가 살짝 지저분해진다.&lt;/li&gt;
&lt;li&gt;속도가 조금 느려질 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;  &lt;/b&gt;pickTwo 메서드에 적용한 예 - &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;List.of&lt;/code&gt;&lt;/span&gt; 사용&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// 배열 대신 List를 이용해 안전하게 바꿘 PickTwo
static &amp;lt;T&amp;gt; List&amp;lt;T&amp;gt; pickTwo(T a, T b, T c) {
    switch(ThreadLocalRandom.current().nextInt(3)) {
        case 0: return List.of(a, b);
        case 1: return List.of(a, c);
        case 2: return List.of(b, c);
    }
    throw new AssertionError();
}

public static void main(String[] args) {
    List&amp;lt;String&amp;gt; attributes = pickTwo(&quot;좋은&quot;, &quot;빠른&quot;, &quot;저렴한&quot;);
    System.out.println(attributes);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;toArray의 List 버전이 바로 &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;&lt;b&gt;List.of&lt;/b&gt;&lt;/code&gt;&lt;/span&gt; 다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바 라이브러리 차원에서 제공하니 직접 작성할 필요도 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열 없이 제네릭만 사용하므로 타입 안전하다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심 정리&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;가변인수&lt;/code&gt;&lt;/span&gt;&lt;/b&gt;와 &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;code&gt;&lt;b&gt;제네릭&lt;/b&gt;&lt;/code&gt;&lt;/span&gt;은 궁합이 좋지 않다.&lt;/li&gt;
&lt;li&gt;가변인수 기능은 배열을 노출하여 추상화가 완벽하지 못하고, 배열과 제네릭의 타입 규칙이 서로 다르기 때문이다.&lt;/li&gt;
&lt;li&gt;제네릭 varargs 매개변수는 타입 안전하지는 않지만 허용된다.&lt;/li&gt;
&lt;li&gt;메서드에 제네릭 혹은 매개변수화된 varargs 매개변수를 사용하고자 한다면, 타입 안전한지 확인한 다음 &lt;b&gt;&lt;code&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;@SafeVarargs&lt;/span&gt;&lt;/code&gt;&lt;/b&gt; 애너테이션을 달아 사용하는 데 불편함이 없게끔 하자.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;</description>
      <category>Language/Java</category>
      <author>wisdom11</author>
      <guid isPermaLink="true">https://wisdom-cs.tistory.com/63</guid>
      <comments>https://wisdom-cs.tistory.com/63#entry63comment</comments>
      <pubDate>Tue, 9 Aug 2022 19:31:10 +0900</pubDate>
    </item>
    <item>
      <title>[Effective Java] 아이템31: 한정적 와일드카드를 사용해 API 유연성을 높이라</title>
      <link>https://wisdom-cs.tistory.com/62</link>
      <description>&lt;div class=&quot;markdown-body&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아이템 28에서 이야기했듯 매개변수화 타입은 불공변(invariant)이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 서로 다른 타입 Type1과 Type2가 있을 때 List&amp;lt;Type1&amp;gt;은 List&amp;lt;Type2&amp;gt;의 하위 타입도 상위 타입도 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 때론 불공변 방식보다 유연한 무언가가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #edf7ff;&quot;&gt;&lt;b&gt;유연성을 높이기 위해, 원소의 생산자(producer)나 소비자(consumer)용 입력 매개변수에 와일드카드 타입을 사용하자.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;✔️ PECS&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;팩스&lt;/b&gt;(&lt;b&gt;PECS&lt;/b&gt;)&lt;/span&gt; 란, &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;producer-extends, consumer-super&lt;/b&gt;&lt;/span&gt; 를 의미한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;매개변수화 타입 T가 &lt;b&gt;&lt;code&gt;생산자&lt;/code&gt;&lt;/b&gt;라면 &lt;b&gt;&lt;code&gt;&amp;lt;? extends T&amp;gt;&lt;/code&gt;&lt;/b&gt;를 사용한다.&lt;/li&gt;
&lt;li&gt;&lt;span&gt;매개변수화 타입 T가&lt;span&gt;&lt;b&gt; &lt;code&gt;소비자&lt;/code&gt;&lt;/b&gt;라면 &lt;b&gt;&lt;code&gt;&amp;lt;? super T&amp;gt;&lt;/code&gt;&lt;/b&gt;를 사용한다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;  생산자 매개변수에 와일드카드 타입 적용&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;그 동안 살펴본 예제 중 생산자 매개변수에 와일드카드 타입을 적용해보자.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1659733373459&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Stack
public void pushAll(Iterable&amp;lt;? extends E&amp;gt; src) {
    for (E e : src)
        push(e);
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1659733422080&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public Chooser(Collection&amp;lt;? extends T&amp;gt; choices) {
    choiceList = new ArrayList&amp;lt;&amp;gt;(choices);
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1659733540946&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static &amp;lt;E&amp;gt; Set&amp;lt;E&amp;gt; union(Set&amp;lt;? extends E&amp;gt; s1, Set&amp;lt;? extends E&amp;gt; s2)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;*단, 반환 타입에는 한정적 와일드카드 타입을 사용하면 안 된다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;유연성을 높여주기는커녕 클라이언트 코드에서도 와일드카드 타입을 써야 하기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  소비자 매개변수에 와일드카드 적용&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;다음은 소비자 매개변수에 와일드카드 타입을 적용한 예시이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1659733458652&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public void popAll(Collection&amp;lt;? super E&amp;gt; dst) {
    while (!isEmpty())
        dst.add(pop());
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;✔️ Comparable, Comparator&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아이템 30에서의 max 메서드에도 적용해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1659733893808&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static &amp;lt;E extends Comparable&amp;lt;? super E&amp;gt;&amp;gt; E max(List&amp;lt;? extends E&amp;gt; list)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;Comparable&lt;/code&gt;은 언제나 &lt;span style=&quot;color: #1a5490;&quot;&gt;소비자&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;다. &lt;/span&gt;&lt;b&gt;&lt;code&gt;Comparator&lt;/code&gt;&lt;/b&gt;도 마찬가지이다.&lt;/li&gt;
&lt;li&gt;Comparable&amp;lt;E&amp;gt; &amp;rarr; &lt;b&gt;&lt;code&gt;Comparable&amp;lt;? super E&amp;gt;&lt;/code&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Comparator&amp;lt;E&amp;gt;&amp;nbsp; &amp;rarr; &lt;b&gt;&lt;code&gt;Comparator&amp;lt;? super E&amp;gt;&lt;/code&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;✔️ 타입 매개변수 vs 와일드카드&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입 매개변수와 와일드카드를 모두 사용할 수 있는 경우에는 어떤 것을 사용해야 할까?&lt;/p&gt;
&lt;pre id=&quot;code_1659735005102&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 와일드카드 타입을 실제 타입으로 바꿔주는 private 도우미 메서드
private static &amp;lt;E&amp;gt; void swapHelper(List&amp;lt;E&amp;gt; list, int i, int j) {
    list.set(i, list.set(j, list.get(i)));
}

public static void swap(List&amp;lt;?&amp;gt; list, int i, int j) {
    swapHelper(list, i, j);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메서드 선언에 타입 매개변수가 한 번만 나오면 와일드카드로 대체하라.&lt;/li&gt;
&lt;li&gt;이때 비한정적 타입 매개변수라면 비한정적 와일드카드로 바꾸고, 한정적 타입 매개변수라면 한정적 와일드카드로 바꿔라.&lt;/li&gt;
&lt;li&gt;그렇지 않은 경우는 타입 매개변수를 사용하라.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;핵심 정리&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;조금 복잡하더라도 와일드카드 타입을 적용하면 API가 훨씬 유연해진다.&lt;/li&gt;
&lt;li&gt;그러니 널리 쓰일 라이브러리를 작성한다면 반드시 와일드카드 타입을 적절히 사용해줘야 한다.&lt;/li&gt;
&lt;li&gt;PECS 공식을 기억하자.&lt;br /&gt;즉, 생산자(producer)는 extends를, 소비자(consumer)는 super를 사용한다.&lt;br /&gt;Comparable과 Comparator는 모두 소비자라는 사실도 잊지 말자.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Language/Java</category>
      <author>wisdom11</author>
      <guid isPermaLink="true">https://wisdom-cs.tistory.com/62</guid>
      <comments>https://wisdom-cs.tistory.com/62#entry62comment</comments>
      <pubDate>Sat, 6 Aug 2022 09:34:08 +0900</pubDate>
    </item>
    <item>
      <title>[Effective Java] 아이템30: 이왕이면 제네릭 메서드로 만들라</title>
      <link>https://wisdom-cs.tistory.com/61</link>
      <description>&lt;div class=&quot;markdown-body&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스와 마찬가지로 메서드도 제네릭으로 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  단순한 제네릭 메서드&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 집합의 합집합을 반환하는 메서드를 예시로 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;로 타입 사용&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1659728485986&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static Set union(Set s1, Set s2) {
    Set result = new HashSet(s1);
    result.addAll(s2);
    return result;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드는 컴파일은 되지만 경고가 두 개 발생한다. 경고를 없애기 위해서는, 메서드를 타입 안전하게 만들어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 집합의 원소 타입을 타입 매개변수로 명시하고, 메서드 안에서도 이 타입 매개변수만 사용하게 수정하자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;제네릭 메서드&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1659728501210&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static &amp;lt;E&amp;gt; Set&amp;lt;E&amp;gt; union(Set&amp;lt;E&amp;gt; s1, Set&amp;lt;E&amp;gt; s2) {
    Set&amp;lt;E&amp;gt; result = new HashSet&amp;lt;&amp;gt;(s1);
    result.addAll(s2);
    return result;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 메서드는 경고 없이 컴파일되며, 타입 안전하고, 쓰기도 쉽다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  제네릭 싱글턴 팩터리 패턴&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때때로 불변 객체를 여러 타입으로 호라용할 수 있게 만들어야 할 때가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제네릭은 런타임에 타입 정보가 소거되므로 하나의 객체를 어떤 타입으로든 매개변수화할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이렇게 하려면 요청한 타입 매개변수에 맞게 매번 그 객체의 타입을 바꿔주는 정적 팩터리를 만들어야 하는데, 이 패턴을 &lt;b&gt;&lt;code&gt;제네릭 싱글턴 팩터리&lt;/code&gt;&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번엔 항등함수를 담은 클래스를 다음과 같이 만들어보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입별로 하나씩 만들 필요 없이 제네릭 싱글턴 하나면 충분하다.&lt;/p&gt;
&lt;pre id=&quot;code_1659729118326&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; private static UnaryOperator&amp;lt;Object&amp;gt; IDENTITY_FN = (t) -&amp;gt; t;

@SuppressWarnings(&quot;unchecked&quot;)
public static &amp;lt;T&amp;gt; UnaryOperator&amp;lt;T&amp;gt; identityFunction() {
    return (UnaryOperator&amp;lt;T&amp;gt;) IDENTITY_FN;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  재귀적 타입 한정 (Recursive type bound)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상대적으로 드물긴 하지만 자기 자신이 들어간 표현식을 사용하여 타입 매개변수의 허용 범위를 한정할 수 있다.&lt;br /&gt;이것이 바로 &lt;b&gt;&lt;code&gt;재귀적 타입 한정&lt;/code&gt;&lt;/b&gt;이라는 개념이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재귀적 타입 한정은 주로 타입의 자연적 순서를 정하는 &lt;b&gt;&lt;code&gt;Comparable&lt;/code&gt;&lt;/b&gt; 인터페이스와 함께 쓰인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 컬렉션에서 최댓값을 반환하는 메서드다.&lt;/p&gt;
&lt;pre id=&quot;code_1659729407142&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static &amp;lt;E extends Comparable&amp;lt;E&amp;gt;&amp;gt; E max(Collection&amp;lt;E&amp;gt; c) {
    if (c.isEmpty())
        throw new IllegalArgumentException(&quot;컬렉션이 비어 있습니다.&quot;);

    E result = null;
    for (E e : c)
        if (result == null || e.compareTo(result) &amp;gt; 0)
            result = Objects.requireNonNull(e);

    return result;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입 한정인 &lt;b&gt;&lt;code&gt;&amp;lt;E extends Comparable&amp;lt;E&amp;gt;&amp;gt;&lt;/code&gt;&lt;/b&gt;는 &quot;&lt;b&gt;모든 타입 E는 자신과 비교할 수 있다&lt;/b&gt;&quot;라고 읽을 수 있다.&lt;br /&gt;즉, 상호 비교 가능하다는 뜻을 아주 정확하게 표현한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재귀적 타입 한정은 훨씬 복잡해질 가능성이 있긴 하지만, 다행히 그런 일은 잘 일어나지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 아이템에서 설명한 관용구, 여기에 와일드카들르 사용한 변형(아이템 31), 그리고 시뮬레이트한 셀프 타입 관용구(아이템 2)를 이해하고 나면 실전에서 마주치는 대부분의 재귀적 타입 한정을 무리 없이 다룰 수 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;핵심정리&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;제네릭 타입과 마찬가지로, 클라이언트에서 입력 매개변수와 반환값을 명시적으로 형변환해야 한느 메서드보다 제네릭 메서드가 더 안전하며 사용하기도 수비다.&lt;/li&gt;
&lt;li&gt;타입과 마찬가지로, 메서드도 형변환 없이 사용할 수 있는 편이 좋으며, 많은 경우 그렇게 하려면 제네릭 메서드가 되어야 한다.&lt;/li&gt;
&lt;li&gt;역시 타입과 마찬가지로, 형변환을 해줘야 하는 기존 메서드는 제네릭하게 만들자. 기존 클라이언트는 그대로 둔 채 새로운 사용자의 삶을 훨씬 편하게 만들어줄 것이다. (아이템 26)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;</description>
      <category>Language/Java</category>
      <author>wisdom11</author>
      <guid isPermaLink="true">https://wisdom-cs.tistory.com/61</guid>
      <comments>https://wisdom-cs.tistory.com/61#entry61comment</comments>
      <pubDate>Sat, 6 Aug 2022 09:33:36 +0900</pubDate>
    </item>
    <item>
      <title>[Effective Java] 아이템29: 이왕이면 제네릭 타입으로 만들라</title>
      <link>https://wisdom-cs.tistory.com/60</link>
      <description>&lt;div class=&quot;markdown-body&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://wisdom-cs.tistory.com/29&quot;&gt;아이템7&lt;/a&gt;에서 다룬 스택 코드는 원래 &lt;b&gt;&lt;code&gt;제네릭 타입&lt;/code&gt;&lt;/b&gt;이어야 마땅하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 타입 매개변수 E를 추가하고, Object를 적절한 타입 매개변수로 바꾸어준다.&lt;/p&gt;
&lt;pre id=&quot;code_1659726348440&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Stack&amp;lt;E&amp;gt; {
    private E[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;

    public Stack() {
        // 컴파일 오류. 실체화 불가 타입으로는 배열을 만들 수 없다. 
        elements = new E[DEFAULT_INITIAL_CAPACITY];
    }

    public void push(E e) {
        ensureCapacity();
        elements[size++] = e;
    }

    public E pop() {
        if (size == 0)
            throw new EmptyStackException();
        E result = elements[--size];
        elements[size] = null; // 다 쓴 참조 해제
        return result;
    }
    
    // isEmpty와 ensureCapacity 메서드는 그대로다.
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드는 &lt;b&gt;&lt;code&gt;new E[DEFAULT_INITIAL_CAPACITY]&lt;/code&gt;&lt;/b&gt; 부분에서 오류가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위한 방법은 두 가지가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1️⃣ Object 배열을 생성한 다음 제네릭 배열로 형변환하기&lt;/p&gt;
&lt;pre id=&quot;code_1659726623321&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 배열 elements는 push(E)로 넘어온 E 인스턴스만 담는다.
// 따라서 타입 안전성을 보장하지만,
// 이 배열의 런타임 타입은 E[]가 아닌 Object[]다!
@SuppressWarnings(&quot;unchecked&quot;)
public Stack() {
    elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 수정하면, 컴파일러에서 &lt;b&gt;&lt;code&gt;unchecked cast&lt;/code&gt;&lt;/b&gt; 경고를 내보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 이 경우에 elements 배열은 private 필드에 저장되고, push 메서드로 전달되는 원소의 타입은 항상 E이므로 비검사 형변환은 안전하다. 비검사 형변환이 안전하기 때문에 &lt;b&gt;&lt;code&gt;@SuppressWarnings&lt;/code&gt;&lt;/b&gt; 애너테이션을 달아 경고를 숨겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법은 &lt;b&gt;가독성이 좋고, 뒤의 방법보다 코드도 더 짧다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2️⃣ 필드 타입을 Object[]로 바꾸기&lt;/p&gt;
&lt;pre id=&quot;code_1659727065169&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Stack&amp;lt;E&amp;gt; {
    private Object[] elements;
    
    ...
    
    // 비검사 경고를 적절히 숨긴다.
    public E pop() {
        if (size == 0)
            throw new EmptyStackException();

        // push에서 E 타입만 허용하므로 이 형변환은 안전하다.
        @SuppressWarnings(&quot;unchecked&quot;) E result = (E) elements[--size];

        elements[size] = null; // 다 쓴 참조 해제
        return result;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pop() 메서드에서 반환한 원소를 E로 형변환하면 컴파일러가 &lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;code&gt;unchecked cast&lt;/code&gt;&lt;/b&gt; 경고를 내보낸다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에도 형변환이 안전하다고 증명하고, 위와 같이 경고를 숨길 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법은 배열에서 &lt;b&gt;원소를 읽을 때마다 형변환을 해주어야 한다.&lt;/b&gt;&lt;br /&gt;그러나 &lt;b&gt;첫 번째 방식&lt;/b&gt;은 배열의 런타임 타입과 컴파일타임 타입이 달라 &lt;b&gt;힙 오염&lt;/b&gt;(heap pollution)을 일으킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;핵심 정리&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트에서 직접 형변환해야 하는 타입보다 제네릭 타입이 더 안전하고 쓰기 편하다.&lt;br /&gt;그러니 새로운 타입을 설계할 때는 형변환 없이도 사용할 수 있도록 하라.&lt;/li&gt;
&lt;li&gt;그렇게 하려면 제네릭 타입으로 만들어야 할 경우가 많다.&lt;br /&gt;기존 타입 중 제네릭이었어야 하는 게 있다면 제네릭 타입으로 변경하자.&lt;/li&gt;
&lt;li&gt;기존 클라이언트에는 아무 영향을 주지 않으면서, 새로운 사용자를 후러씬 편하게 해주는 길이다. (아이템 26)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;</description>
      <category>Language/Java</category>
      <author>wisdom11</author>
      <guid isPermaLink="true">https://wisdom-cs.tistory.com/60</guid>
      <comments>https://wisdom-cs.tistory.com/60#entry60comment</comments>
      <pubDate>Sat, 6 Aug 2022 09:32:44 +0900</pubDate>
    </item>
    <item>
      <title>[Effective Java] 아이템28: 배열보다는 리스트를 사용하라</title>
      <link>https://wisdom-cs.tistory.com/59</link>
      <description>&lt;div class=&quot;markdown-body&quot;&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;✔️ 배열 vs 제네릭 타입&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 배열은 공변(covariant)인 반면, 제네릭은 불공변(invariant)이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sub가 Super의 하위 타입이라면 배열 Sub[]는 배열 Super[]의 하위 타입이 되지만, List&amp;lt;Sub&amp;gt;은 List&amp;lt;Super&amp;gt;의 하위 타입도 아니고 상위 타입도 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 배열은 실체화(reify)된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열은 런타임에도 원소의 타입을 확인한다. 즉, &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;런타임&lt;/b&gt;&lt;/span&gt;에 &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;예외&lt;/b&gt;&lt;/span&gt;가 발생할 수 있다.&lt;br /&gt;반면, 제네릭은 타입 정보가 런타임에는 소거된다. 원소 타입을 컴파일타임에만 검사하며 런타임에는 알 수조차 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  위와 같은 차이로 인해, 배열과 제네릭은 잘 어우러지지 못한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 배열은 제네릭 타입(&lt;code&gt;new List&amp;lt;E&amp;gt;[]&lt;/code&gt;), 매개변수화 타입(&lt;code&gt;new List&amp;lt;String&amp;gt;[]&lt;/code&gt;), 타입 매개변수(&lt;code&gt;new E[]&lt;/code&gt;)로 사용할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;✔️ Chooser 클래스 예제&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자에서 컬렉션을 받고, 컬렉션 안의 원소 중 하나를 무작위로 선택해 반환하는 &lt;code&gt;choose&lt;/code&gt; 메소드를 제공하는 &lt;code&gt;Chooser&lt;/code&gt; 클래스를 구현해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  제네릭을 쓰지 않은 방법 &amp;rarr; 제네릭 적용이 시급하다!&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1659114114009&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Chooser {
	
    private final Object[] choiceArray;
    
    public Chooser(Collection choices) {
        choiceArray = choices.toArray();
    }
    
    public Object choose() {
        Random rnd = ThreadLocalRandom.current();
        return choiceArray[rnd.nextInt(choiceArray.length)];
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 클래스를 사용하려면 choose 메서드를 호출할 때마다 반환된 Object를 원하는 타입으로 형변환해야 한다.&lt;br /&gt;이때 만약 다른 타입의 원소가 들어 있었다면 &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;런타임에 형변환 오류가 발생&lt;/b&gt;&lt;/span&gt;할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이 클래스는 제네릭 타입으로 만들어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  제네릭으로 만들기 위한 시도 &lt;b&gt;&amp;rarr; 경고&lt;/b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1659114527515&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Chooser&amp;lt;T&amp;gt; { 
	
    private final T[] choiceArray;
    
    public Chooser(Collection&amp;lt;T&amp;gt; choices) {
    	
        // 1. 컴파일 오류
        // choiceArray = choices.toArray();	
        
        // 2. 경고
        choiceArray = (T[]) choices.toArray();
    }
    
    public Object choose() {
        Random rnd = ThreadLocalRandom.current();
        return choiceArray[rnd.nextInt(choiceArray.length)];
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;code&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;1번&lt;/span&gt;&lt;/code&gt;&lt;/b&gt;과 같이 작성할 경우, 배열 &lt;code&gt;Object[]&lt;/code&gt;이 배열 &lt;code&gt;T[]&lt;/code&gt;과 타입이 맞지 않아&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt; 컴파일 오류&lt;/b&gt;&lt;/span&gt;가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위해 &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;&lt;code&gt;2번&lt;/code&gt;&lt;/b&gt;&lt;/span&gt;과 같이 작성한 경우, 오류는 발생하지 않지만 런타임에 형변환이 안전한지 보장할 수 없다는 &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;경고&lt;/b&gt;&lt;/span&gt; 메세지가 나온다.&amp;nbsp;&lt;br /&gt;이 코드의 경우, 동작하기는 하지만 &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;컴파일러가 안전을 보장하지 못 한다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  리스트 기반 &lt;b&gt;&amp;rarr; 타입 안전성 확보&lt;/b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비검사 형변환 경고를 제거하려면 배열 대신 &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;리스트&lt;/b&gt;&lt;/span&gt;를 쓰면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 코드는 오류나 경고 없이 컴파일된다.&lt;/p&gt;
&lt;pre id=&quot;code_1659114999689&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Chooser&amp;lt;T&amp;gt; {
    private final List&amp;lt;T&amp;gt; choiceList;

    public Chooser(Collection&amp;lt;T&amp;gt; choices) {
        choiceList = new ArrayList&amp;lt;&amp;gt;(choices);
    }

    public T choose() {
        Random rnd = ThreadLocalRandom.current();
        return choiceList.get(rnd.nextInt(choiceList.size()));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;핵심 정리&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배열은 공변이고 실체화되는 반면, 제네릭은 불공변이고 타입 정보가 소거된다.&lt;/li&gt;
&lt;li&gt;배열은 런타임에는 타입 안전하지만 컴파일타임에는 그렇지 않다.&lt;br /&gt;제네릭은 그 반대다.&lt;/li&gt;
&lt;li&gt;이러한 이유로 배열과 제네릭을 섞어 쓰기란 쉽지 않다.&lt;/li&gt;
&lt;li&gt;둘을 섞어 쓰다가 컴파일 오류나 경고를 만나면, 가장 먼저 배열을 리스트로 대체하는 방법을 적용해보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;</description>
      <category>Language/Java</category>
      <author>wisdom11</author>
      <guid isPermaLink="true">https://wisdom-cs.tistory.com/59</guid>
      <comments>https://wisdom-cs.tistory.com/59#entry59comment</comments>
      <pubDate>Sat, 30 Jul 2022 02:29:30 +0900</pubDate>
    </item>
  </channel>
</rss>