2022-02-03 TIL (자바, 알고리즘)

|

1. 알고리즘

silver 4. 보물(1026) 문제 풀이 방법을 추가했다.

  • 처음 풀 때는 B에 있는 수를 재배열하지 말라는 조건을 충족시키지 못했다. 오늘 다시 풀어보면서 B의 최댓값을 구해주는 함수를 따로 생성해 조건을 충족시키면서 문제를 해결했다.
  • 밀린 자바 공부를 진행하느라 오늘은 알고리즘 문제를 풀지 못했다. 아마 토요일까지는 문제를 풀 수 없을 것 같다. 일요일에 밀린 이번주 문제를 한 번에 풀어야겠다.

2. 자바

Chapter 13. 쓰레드Chapter 14. 람다와 스트림 중 람다 부분을 공부했다.

  • Chapter 13. 쓰레드
    • 멀티쓰레딩 장점과 단점
    • start()와 run()
    • 데몬 쓰레드
    • 쓰레드 스케줄링 메서드: sleep(), join(), interrupt(), stop(), suspend(), resume(), yield()
    • 쓰레드 상태: NEW, RUNNABLE, BLOCKED, WAITING, TERMINATED
    • 쓰레드 동기화
      • wait()와 notify()
      • 기아 현상과 경쟁 상태
      • volatile
      • fork&join 프레임웍
  • Chapter 14. 람다와 스트림_람다
    • 람다식이란?
    • 함수형 인터페이스
    • java.util.function 패키지
    • Function의 합성과 Predicate의 결합
      • Function 합성: andThen(), compose(), identity()
      • Predicate 결합: and(), or(), negate(), isEqual()
    • 메서드 참조

3

내일은 오늘 끝내지 못한 스트림 부분과 남은 부분인 입출력과 네트워킹 부분을 공부해야겠다.

자바 기본서 1회독이 끝나가니 그 다음으로 공부할 자바의 심화된 내용을 다루는 책과 스프링 기본서를 찾아봐야겠다.

2022년 설연휴(01-29~02-02) TIL (자바, 알고리즘)

|

1. 알고리즘

silver 5. 수들의 합(1789) 문제를 풀었다.

  • 문제를 처음 읽었을 때 이해하는데 시간이 조금 걸렸고, 아이디어를 생각해내는데도 시간이 걸렸다. (한 30분 정도..)
  • 막상 아이디어를 떠올리고 나니 구현하는데는 오래 걸리지 않았다.

bronze 2. 5와 6의 차이(2864) 문제를 풀었다.

  • 난이도가 높지 않아서 비교적 쉽고 빠르게 풀 수 있었다.

2

추석에도 계획한데로 공부를 진행하려고 했으나… 집중이 되지 않아 알고리즘 두 문제밖에 풀지 못했다..

진도가 밀린만큼 이번 주 분량을 모두 끝내기 위해 내일부터는 다시 집중해서 공부해야 겠다

  • 목, 금: 자바의 정석 끝내기
  • 토: 운영체제 남은 강의 모두 듣기

bronze 2. 5와 6의 차이 (2864)

|

bronze 2. 5 (2864)

출처 : https://www.acmicpc.net/problem/2864

문제

숫자 5를 볼 때, 5로 볼 때도 있지만, 6으로 잘못 볼 수도 있고, 6을 볼 때는, 6으로 볼 때도 있지만, 5로 잘못 볼 수도 있다.

두 수 A와 B가 주어졌을 때, 두 수의 가능한 합 중, 최솟값과 최댓값을 구해 출력하는 프로그램을 작성

입력: 두 정수 A와 B가 주어짐 (1 <= A, N <= 1,000,000)

출력: 두 수의 합 중 최솟값과 최댓값을 출력

풀이

  • 아이디어: 최솟값을 구하는 함수와 최댓값을 구하는 함수 작성
    • 최솟값을 구하는 함수: 주어진 수에 6이 들어있는 경우 6을 5로 바꿔준 후, 두 수의 합을 return
    • 최댓값을 구하는 함수: 주어진 수에 5가 들어있는 경우 5를 6으로 바꿔준 후, 두 수의 합을 return
import java.util.*;

public class Main{
  public static void main(String[] args){
    Scanner sc = new Scanner(System.in);
    int min = 0;
    int max = 0;

    StringTokenizer st = new StringTokenizer(sc.nextLine());
    
    // 두 수 입력 받음
    String num1 = new String(st.nextToken());
    String num2 = new String(st.nextToken());

    min = min(num1, num2);	// 최솟값 구하기
    max = max(num1, num2);	// 최댓값 구하기

    System.out.printf("%d %d%n", min, max);

  }

  // 두 수의 합 중에서 최솟값을 구하는 함수
  public static int min(String num1, String num2){
    int result = 0;
	
    // 주어진 수들 중에서 6이 포함되어 있는 경우, 6을 5로 변경
    if(num1.contains("6")){
      num1 = num1.replace("6", "5");
    }

    if(num2.contains("6")){
      num2 = num2.replace("6", "5");
    }
    
	// String형인 두 변수를 Int형으로 변경 후 더해줌
    result = Integer.parseInt(num1) + Integer.parseInt(num2);

    return result;	// 최솟값 return
  }

  // 두 수의 합 중에서 최댓값을 구하는 함수
  public static int max(String num1, String num2){
    int result = 0;

    // 주어진 수들 중에서 5가 포함되어 있는 경우, 5를 6으로 변경
    if(num1.contains("5")){
      num1 = num1.replace("5", "6");
    }

    if(num2.contains("5")){
      num2 = num2.replace("5", "6");
    }

    // String형인 두 변수를 Int형으로 변경 후 더해줌
    result = Integer.parseInt(num1) + Integer.parseInt(num2);

    return result;	// 최댓값 return
  }
}

silver 5. 수들의 합(1789)

|

silver 5. 수들의 합 (1789)

출처 : https://www.acmicpc.net/problem/1789

문제

서로 다른 N개의 자연수의 합이 S라고 함. S를 알 때, 자연수 N의 최댓값은 얼마일까?

입력: 자연수 S (1 <= S <= 4,297,967,295)

출력: S의 최댓값 출력

풀이

  • 아이디어: 1부터 순서대로 값을 더해나가다가 더한 값이 주어진 수 S보다 커지면 프로그램 종료

    • count 변수에 몇 개의 수를 더했는지 저장
    • S보다 자연수를 더한 값이 커지면 count의 값을 1 빼준 후 출력

    예를 들어 S가 50이라고 가정

    • 1+2+3+4+5+6+7+8+9+10 = 55 (이때의 count 값은 10)
    • 55에서 5만 빼주면 S가 되므로 count에서 1만 감소시키면 됨
import java.util.*;

public class Main{
  public static void main(String[] args){
    Scanner sc = new Scanner(System.in);
    
    int count = 0;	// 더한 값의 개수를 저장
    long add = 1;	// sum에 더해나갈 값
    long sum = 0;	// 자연수들의 합을 저장
    
    // long 변수 사용 이유: 입력 값의 범위가 int형이 담을 수 있는 범위를 넘어감
    long num = sc.nextLong();	

    while (true){
      sum += add ++;	// sum에 add 값을 더한 후, add 값 1 증가
      count ++;		// count 값 1 증가 

      // sum의 값이 num의 값보다 커지면 count를 1 감소시키고 while 문을 벗어남
      if (sum > num) {	
        count --;
        break;
      }
    }
      
    // count 값 출력
    System.out.println(count);
  }
}

Chapter 12. 지네릭스, 열거형, 애너테이션

|

1. 지네릭스(Generics)

  • JDK1.5에서 처음 도입

1.1 지네릭스란?

  • 다양한 타입의 객체들을 다루는 메서드나 컬렉션 클래스에 컴파일 시의 타입 체크(compile-time type check)를 해주는 기능
    • 객체의 타입을 컴파일 시에 체크하기 때문에 객체의 타입 안정성을 높이고 형변환의 버거로움이 줄어듦
    • 타입 안정성을 높인다는 것은 의도하지 않은 타입의 객체가 저장되는 것을 막고, 저장된 객체를 꺼내올 때 원래의 타입과 다른 타입으로 잘못 형변환되어 발생할 수 있는 오류를 줄여준다는 의미

지네릭스의 장점

  1. 타입 안정성을 제공
  2. 타입체크와 형변환을 생략할 수 있어 코드가 간결해짐
  • 다룰 객체의 타입을 미리 명시해줌으로써 번거로운 형변환을 줄여줌

1.2 지네릭 클래스의 선언

클래스에 선언하는 지네릭 타입

class Box {
    Object item;
    
    void setItem(Object item) {this.item = item;}
    Object getItem() {return item;}
}
  • Box 클래스를 지네릭 클래스로 변경하면 클래스 옆에 <T>를 붙인 후, Object를 모두 T로 변경
class Box<T> {
    T item;
    
    void setItem(T item) {this.item = item;}
    T getItem()	{return item;}
}
  • Box<T>에서 T를 ‘타입 변수(type variable)‘라고 함

    • 타입 변수의 경우 T가 아닌 다른 것을 사용해도 됨. 상황에 맞게 의미있는 문자를 선택해서 사용하는 것이 좋음
    • 타입 변수가 여러 개인 경우 콤마를 구분자로 나열

    • 기호의 종류만 다를 뿐 임의의 참조형 타입을 의미한다는 것은 같음
  • 지네릭 클래스된 Box 클래스의 객체를 생성할 때는 참조변수와 성성자에 타입 T대신 사용될 실제 타입을 지정해주어야 함

    Box<String> b = new Box<String>();
    b.setItem(new Object());	// 에러. String이외의 타입은 지정 불가
    b.setItem("ABC");
    String item = b.getItem()	// 형변환 필요없음
    

지네릭스의 용어

class Box<T> {}
  • Box<T>: 지네릭 클래스. ‘T의 Box’ 또는 ‘T Box’라고 읽음
  • T: 타입 변수 또는 타입 매개변수 (T는 타입 문자)
  • Box: 원시 타입(raw type)

지네릭스의 제한

  • 지네릭 클래스 Box의 객체를 생성할 때, 객체별로 다른 타입을 저정하는 것은 적절하지만, 모든 객체에 대해 동일하게 동작해야하는 static 멤버에 타입 변수 T를 사용할 수 없음
    • T는 인스터스변수로 간주되기 때문. static 멤버는 인스턴스변수 참조 불가
    • static 멤버는 타입 변수에 지정된 타입, 즉 대입된 타입의 종류에 관계없이 동일한 것이어야 하기 때문
  • 지네릭 타입의 배열을 생성하는 것은 허용되지 않음
    • 지네릭 배열 타입의 참조변수를 선언하는 것은 가능하지만, 배열을 생성하는 것은 안됨
    • new 연산자는 컴파일 시점에 타입 T가 뭔지 정확하게 알아야 하지만 컴파일하는 시점에서 T가 어떤 타입이 될지 전혀 알 수 없음
    • 지네릭 배열을 생성해야할 필요가 있는 경우, new 연산자대신 ‘Reflection API’의 newInstance()와 같이 동적으로 객체를 생성하는 메서드로 배열을 생성하거나, Object 배열을 생성해 복사한 다음 T[]로 형변환하는 방법 등을 사용

1.3 지네릭 클래스의 객체 생성과 사용

  • Box<>의 객체에는 한 가지 종류 (T타입)의 객체만 저장 가능
class Box<T> {
    ArrayList<T> list = new ArrayList<T>();
    
    void add(T time)		{list.add(item);		}
    T get(int i)			{return list.get(i);	}
    ArrayList<T> getList()	{return list;			}
    int size()				{return list.size();	}
    public String toString	{return list.toString();}
  • 참조변수와 생성자에 대입된 타입(매개변수화된 타입)이 일치해야 함. 일차하지 않으면 에러 발생

    Box<Apple> appleBox = new Box<Apple>();	// OK
    Box<Apple> appleBox = new Box<Graph>();	// 에러
    
  • 두 타입이 상속 관계에 있더라도 에러 발생

    // Apple이 Fruit의 자손이라 가정
    Box<Fruit> appleBox = new Box<Apple>();	// 에러. 대입된 타입이 다름
    
  • 두 지네릭 클래스의 타입이 상속관계에 있고, 대입된 타입이 같은 것은 괜찮음

    // FruitBox가 Box의 자손이라고 가정
    Box<Apple> appleBox = new FruitBox<Apple>();	// OK. 다형성
    
  • JDK1.7부터 추정이 가능한 경우 타입을 생략할 수 있게 됨

    • 참조변수의 타입으로부터 어떤 타입의 객체만 저장하는지를 알 수 있기 때문에, 생성자에 반복해서 타입을 지정해주지 않아도 됨
  • extends를 사용하면, 특정 타입의 자손들만 대입할 수 있게 제한할 수 있음

1.5 와일드 카드

  • 와일드 카드는 기호 ’?’로 표현하는데, 와일드 카드는 어떠한 타입도 될 수 있음

    • ’?’만으로는 Object 타입과 다를게 없으므로, extendssuper로 상한(upper bound)과 하한(lower bound)을 제한할 수 있다

      <? extends T> 와일드 카드의 상한 제한. T와 그 자손들만 가능

      <? super T> 와일드 카드의 하한 제한. T와 그 조상들만 가능

      <?> 제한 없음. 모든 타입이 가능. <? extends Object>와 동일

1.6 지네릭 메서드

// Collections.sort()
static <T> void sort(List<T> list, Comparator<? super T> c)
  • 메서드의 선언부에 지네릭 타입이 선언된 메서드
    • 지네릭 타입의 선언 위치는 반환 타입 바로 앞
  • 지네릭 클래스에 정의된 타입 매개변수와 지네릭 메서드에 정의된 타입 매개변수는 전혀 별개의 것
  • static 멤버에는 타입 매개변수를 사용할 수 없지만, 메서드에 지네릭 타입을 선언하고 사용하는 것은 가능
    • 메서드에 선언된 지네릭 타입은 지역 변수를 선언한 것과 같다고 생각하면 됨
    • 이 타입 매개변수는 메서드 내에서만 지역적으로 사용될 것이므로 메서드가 static이건 아니건 상관없음
  • 지네릭 메서드를 호출할 때, 대입된 타입을 생략할 수 없는 경우에는 참조변수나 클래스 이름을 생략할 수 없음

1.7 지네릭 타입의 형변환

  • 경고가 발생하지만, 지네릭 타입과 넌지네릭(non-generic)타입 간의 형변환은 항상 가능
  • 대입된 타입이 다른 지네릭 타입 간에는 형변환이 불가능
  • 와일드 카드가 사용된 지네릭 타입끼리도 형변환 가능하지만, 와일드 카드는 타입이 확정된 타입이 아니므로 컴파일러는 미확정 타입으로 형변환하는 것이라고 경고

1.8 지네릭 타입의 제거

  • 컴파일러는 지네릭 타입을 이용해 소스파일을 체크하고, 필요한 곳에 형변환을 넣어준 후 지네릭 타입을 제거하므로 컴파일된 파일(*.class)에는 지네릭 타입에 대한 정보가 없음

지네릭 타입 제거 과정

  1. 지네릭 타입의 경계(bound)를 제거
    • 지네릭 타입이 <T extends Fruit>라면 T는 Fruit로 치환. <T>인 경우 T는 Object로 치환됨
    • 치환된 후 클래스 옆의 선언은 제거됨
  2. 지네릭 타입을 제거한 후에 타입이 일치하ㅈ 않으면, 형변환을 추가함
    • 와일드 카드가 포함되어 있는 경우에는 적절한 타입으로의 형변환이 추가됨

2. 열거형(enums)

2.1 열거형이란?

  • 서로 관련된 상수를 편리하게 선언하기 위한 것으로 여러 상수를 정의할 때 사용하면 유용
  • 열거형이 갖는 값뿐만 아니라 타입도 관리하기 때문에 논리적인 오류를 줄일 수 있음
  • 타입에 안전한 열거형(typesafe enum)이기 때문에 실제 값이 같아도 타입이 다르면 컴파일 에러가 발생
  • 상수의 값이 바뀌면, 해당 상수를 참조하는 모든 소스를 다시 컴파일해야 했지만, 열거형 상수를 사용하면 기존의 소스를 다시 컴파일하지 않아도 됨

2.2 열거형의 정의와 사용

enum 열거형이름	{ 상수면1, 상수명2, ... }
  • {} 안에 상수의 이름을 나열해 정의
  • 열거형에 정의된 상수를 사용하는 방법: 열거형이름.상수명
  • 열거형 상수간의 비교에는 ‘==’ 사용이 가능. >, <와 같은 비교 연산자는 사용할 수 없고 compareTo()는 사용 가능
    • compareTo()는 두 비교대상이 같으면 0, 왼쪽이 크면 양수, 오른쪽이 크면 음수를 반환
  • switch문의 조건식에도 열거형 사용 가능
    • case문에는 열거형의 이름은 적지 않고 상수의 이름만 적어야 한다는 제약이 있음

모든 열거형의 조상 - java.lang.Enum

  • Enum 클래스에 정의된 메서드
  • values(): 열거형의 모든 상수를 배열에 담아 반환. 모든 열거형이 가지고 있는 것으로 컴파일러가 자동으로 추가해줌
  • ordinal(): 모든 열거형의 조상인 java.lang.Enum 클래스에 정의된 것으로, 열거형 상수가 정의된 순서(0부터 시작)를 정수로 반환

2.3 열거형에 멤버 추가하기

  • ordinal()이 열거형 상수가 정의된 순서를 반환하지만, 이 값을 열거형 상수 값으로 사용하는 것은 좋지 않음

    • 이유: 이 값은 내부적인 용도로만 상용되기 위한 것이기 때문
  • 열거형 상수의 값이 불연속적인 경우에는 열거형 상수의 이름 옆에 원하는 값을 괄호()와 함께 적어주면 됨

    ```java enum Diraction {EAST(1), SOUTH(5), WEST(-1), NORTH(10)}

  • 지정된 값을 저장할 수 있는 인스턴스 변수와 생성자를 새로 추가해 주어야 함

    • 먼저 열거형 상수를 모두 정의한 다음 다른 멤버들을 추가해야 함. 열거형 상수의 마지막에 ‘;’도 붙여야 함

2.4 열거형의 이해

  • 열거형 상수 하나하나가 객체
  • 모든 열거형은 추상 클래스 Enum의 자손
  • 추상 메서드를 새로 추가하면, 클래스 앞에도 abstract를 붙여줘야 하고, 각 static 상수들도 추상 메서드를 구현해주어야 함

3. 애너테이션(annotation)

3.1 애너테이션이란?

  • 소스코드의 주석(/** ~ */)에 소스코드에 대한 정보를 저장하고, 소스코드의 주석으로부터 HTML 문서를 생성해내는 프로그램(javadoc.exe)을 만들어 사용함
    • /**로 시작하는 주석 안에 소스코드에 대한 설명들이 있고, 그 안에 @이 붙은 태그들이 있음
    • 미리 정의된 태그들을 이용해 주석 안에 정보를 저장하고, javadoc.exe라는 프로그램이 이 정보를 읽어 문서를 작성하는데 사용
  • 애너테이션: 프로그램의 소스코드 안에 다른 프로그램을 위한 정보를 미리 약속된 형식으로 포함시킨 것
    • 애너테이션은 프로그래밍 언어에 영향을 미치지 않으면서도 다른 프로그램에게 유용한 정보를 제공할 수 있음
    • JDK에서 제공하는 표준 애너테이션은 주로 컴파일러를 위한 것으로 컴파일러에게 유요안 정보를 제공
    • 새로운 애너테이션을 정의할 때 사용하는 메타 애너테이션을 제공
  • JDK에서 제공하는 애너테이션

3.2 표준 애너테이션

@Override

  • 메서드 앞에만 붙일 수 있는 애너테이션
  • 조상의 메서드를 오버라이딩하는 것이라는걸 컴파일러에게 알려주는 역할을 함
  • 컴파일러가 같은 이름의 메서드가 조상에 있는지 확인하고 없으면, 에러메시지를 출력

@Deprecated

  • 더 이상 사용되지 않는 필드나 메서드에 붙이는 애너테이션
  • 이 애너테이션이 붙은 대상은 다른 것으로 대체되었으니 더 이상 사용하지 않을 것을 권한다는 의미

@FunctionalInterface

  • 함수형 인터페이스를 선언할 때, 이 애너테이션을 붙이면 컴파일러가 ‘함수형 인터페이스’를 올바르게 선언했는지 확인하고, 잘못된 경우 에러를 발생시킴
  • 필수는 아니지만,붙이면 실수를 방지할 수 ㅜ있으므로 함수형 인터페이스를 선언할 때 애너테이션을 붙이는 것이 좋음

@SuppressWarnings

  • 컴파일러가 보여주는 경고메시지가 나타나지 않게 억제해줌
  • 애너테이션이 억제할 수 있는 경고 메시지 종류
    • deprecation: @Deprecated가 붙은 대상을 사용해서 발생하는 경고
    • unchecked: 지네릭스로 타입을 지정하지 않았을 때 발생하는 경고
    • rawtypes: 지네릭스를 사용하지 않아 발생하는 경고
    • varargs: 가변인자의 타입이 지네릭 타입일 때 발생하는 경고

@SafeVarargs

  • 메서드에 선언된 가변인자의 타입이 non-reifiable 타입일 경우, 해당 메서드를 선언하는 부분과 호출하는 부분에서 unchecked 경고가 발생
    • 컴파일 후에도 제거되지 않는 지네릭 타입을 reifiable 타입이라 하고, 제거되는 타입을 non-reifiable 타입이라 함
  • 해당 코드에 문제가 없다면 이 경고를 억제하기 위해 애너테이션을 사용
  • 이 애너테이션은 static이나 final이 붙은 메서드와 생성자에만 붙일 수 있음
    • 오버라이드될 수 있는 메서드에는 사용 불가

3.3 메타 애너테이션

  • 애너테이션에 붙이는 애너테이션으로 애너테이션을 정의할 때 애너텡이션의 적용대상(target)이나 유지기간(retention) 등을 지정하는데 사용

  • @Target: 애너테이션이 적용가능한 대상을 저장하는데 사용
  • @Retention: 애너테이션이 유지되는 기간을 지정하는데 사용
    • SOURCE: 소스 파일에만 존재. 클래스파일에는 존재하지 않음
    • CLASS: 클래스 파일에 존재. 실행시에 사용불가. 기본값
    • RUNTIME: 클래스 파일에 존재. 실행시에 사용가능
  • @Documented: 애너테이션에 대한 정보가 javadoc으로 작성한 문서에 포함되도록 함
    • 자바에서 제공하는 기본 애너테이션 중에 @Overide@SuppressWarnings를 제외하고는 모두 이 메타 애너테이션이 붙어 있음
  • @Inherited: 애너테이션이 자손 클래스에 상속되도록 함
    • 이 애너테이션이 붙은 애너테이션을 조상 클래스에 붙이면, 자손 클래스도 이 애너테이션이 붙은 것과 같이 인식됨
  • @Repeatable: 하나의 대상에 한 종류의 애너테이션을 붙이지만, 이 애너테이션이 붙은 애너테이션은 여러 번 붙일 수 있음
    • 같은 이름의 애너테이션이 여러 개가 하나의 대상에 적용될 수 있기 때문에, 이 애너테이션들을 하나로 묶어서 다룰 수 있는 애너테이션도 추가로 정의해야 함
  • @Native: native method에 의해 참조되는 상수 필드(constant field)에 붙이는 애너테이션
    • 네이티브 메서드는 JVM에 설치된 OS 메서드를 의미. Object 클래스의 메서드들은 대부분 네이티브 메서드

3.4 애너테이션 타입 정의하기

@interface 애너테이션이름	{
    타입 요소이름();	// 애너테이션의 요소를 선언
    ...
}

애너테이션의 요소

  • 애너테이션 내에 선언된 메서드를 의미
  • 반환값이 있고 매개변수는 없는 추상 메서드의 형태를 가지며, 상속을 통해 구현하지 않아도 됨
    • 단, 애너테이션을 적용할 때 이 요소들의 값을 빠짐없이 지정해주어야 함. 요소의 이름도 같이 적어주므로 순서는 상관없음
  • 애너테이션의 각 요소는 기본값을 가질 수 있으며, 기본값이 있는 요소는 애너테이션을 적용할 때 값을 지정하지 않으면 기본값이 사용됨
  • 애너테이션 요소가 하나뿐이고 이름이 value인 경우, 애너테이션을 적용할 때 요소의 이름을 생략하고 값만 적어도 됨
  • 요소의 타입이 배열인 경우, {}를 사영해서 여러 개의 값을 지정할 수 있음

마커 애너테이션 Marker Annotation

  • 값을 지정할 필요가 없는 경우, 애너테이션의 요소를 하나도 정의하지 않을 수 있음
  • Serializable이나 Cloneable 인터페이스처럼, 요소가 하나도 정의되지 않은 애너테이션을 마커 애너테이션이라 함

애너테이션 요소의 규칙

  • 요소의 타입은 기본형, String, enum, 애너테이션, Class만 혀용됨
  • ()안에 매개변수를 선언할 수 없음
  • 예외를 선언할 수 없음
  • 요소를 타입 매개변수로 정의할 수 없음

Chapter 12 끝!!!