til(11_20)

김태은·2024년 11월 20일

오늘은 몸상태 어제보다는 조금 더 낫은 상태? 이다.

오늘은 어제 강의에 이어 Enum 2회차 + 컬렉션 1회차 진행 예정이다.
내용 정리 또한, Enum 만 진행할 예정이고, 컬렉션은 키워드 정도만 적을 생각이다.

오전엔 어제 gpt성님에게 얻은 코드를 내것으로 만들기 위해 계속 연습해 볼 생각이다.


Enum

타입 안전 열거형 패턴

특징

나열한 항목(상수)들만 사용할 수 있다. - > 그외는 사용 불가
우리가 아는 Enum과 다르게 기본 클래스에 작성한다. - > 인스턴스를 만들 수 있다.
- > 각 상수는 모두 서로 다른 인스턴스를 참조하기 때문에 참조 값이 다르다.

public class ClassGrade {
 	public static final ClassGrade BASIC = new ClassGrade();
 	public static final ClassGrade GOLD = new ClassGrade();
 	public static final ClassGrade DIAMOND = new ClassGrade();
}

장점

타입안정성 향상
정해진 객체만 사용할 수 있기 때문에, 잘못된 값을 입력하는 문제를 근본적으로 방지할 수 있다.
데이터 일관성
정해진 객체만 사용하므로 데이터의 일관성이 보장된다.

단점

public class ClassGradeEx2_2 {
 	public static void main(String[] args) {
 	int price = 10000;
 	DiscountService discountService = new DiscountService();
 	ClassGrade newClassGrade = new ClassGrade(); //생성자 private으로 막	아야 함
 	int result = discountService.discount(newClassGrade, price);
 	System.out.println("newClassGrade 등급의 할인 금액: " + result);
	}
}

위 처럼 main에 객체를 생성하면 빈 깡통 객체가 생성되어, 위 discount 메서드에 들어가도 원하는 값을 얻을 수 없다.

- > 위 문제 해결을 위해 class에 생성자를 private로 두어 main에서 인스턴스 생성 못하게 막을 수 있긴하다.


Enum 타입

타입 안전 열거형 패턴을 쉽게 한것이 Enum 이다. + 타입 안전 열거형 패턴의 장점은 가지고오고, 단점은 보완!

해당 enum클래스는 객체를 생성 못한다! 그래서 특히나 static의 개념을 잘 알아야한다. < - 물론 개인전인 생각이다.

구조

밑에 코드와 같이 상수 옆에 변수를 달 수 있다.
물론 선택 사항이다. 인스턴스 변수를 달라고 할적엔,
생성자에 매개변수와 실행문에 넣으려는 변수를 입력해야하고, enum 필드에도 마찬가지로 변수를 선언해준다.

ps
상수만 정의 할 적엔 상수정의 부분 끝에 ;(세미콘론)이 필요 없지만, 상수의 변수가 필요해서 정의를 할 적엔, 상수 정의 부분 끝에 세미콘론이 필요하다.

public enum Grade {
 BASIC(10), GOLD(20), DIAMOND(30); // < - 인스턴스 변수를 선언하여 ';' 이 필요하다!
 private final int discountPercent; // < -
 Grade(int discountPercent) { // < -
 this.discountPercent = discountPercent;
 }

특징

  1. 열거형은 java.lang.Enum을 자동(강제)으로 상속 받기 때문에, 다른 클래스를 추가로 상속받을 수 없다.
  2. 열거형은 인터페이스를 구현할 수 있다.
  3. 열거형은 추상 메서드를 선언하고, 구현할 수 있다.
    <- 이경우 익명 클래스와 같은 방식을 사용해야한다. 물론 난 아직 해당 내용을 배우진 않았다.

메서드

  • values() : 모든 enum 반환한다.
  • name() : enum 상수의 이름을 문자열로 변환한다.
  • ordinal() : 순서를 얻을 수 있다. - 0번부터 시작되고, 정의한 상수 순서 그대로 0부터 순서가 매겨진다.
  • valueOf(String input) : String 변수 input 을 열거형 타입의 변수에 할당시킬 수 있다.
  • toString() : enum 상수의 이름을 문자열로 변환한다. 직접 오버라이드 할 수 있다.
/모든 ENUM 반환
 Grade[] values = Grade.values(); // < - 값들이 배열로 나와서 Grade[] 
 System.out.println("values = " + Arrays.toString(values)); 
 // < - 이렇게해야 enum의 상수들이 출력된다.
 // System.out.println("values = " + values); - > 하면 참조값이 출력됨
 for (Grade value : values) { // < - 밑에 코드 리뷰? 에서 공부한 내용이랑 같은 내용
 System.out.println("name=" + value.name() + ", ordinal=" + value.ordinal());
// value.name()은 상수 값 = 상수의 이름 
// ex PLU 가 상수로 enum클래스에 객체라면 , 해당 매세드로 출력 결과 - > PLU

String input = "GOLD"; // < - String - > enum(열거혈)
 Grade gold = Grade.valueOf(input); 
 // < - Grade 는 타입(열거형)! , gold 는 변수명
 System.out.println("gold = " + gold);


컬렉션

배열

키워드

배열 ,인덱스 ,빅오 ,순차 검색 ,조회 ,변경 ,알고리즘 ,성능 ,상수 시간 ,선형 시간 ,제곱 시간 ,로그 시간 ,선형 로그 ,데이터 추가 ,배열의 생성시점 ,정적


리스트

키워드

동적,리스트,순서,중복 허용,copyof(),데이터 추가,삭제



코드 연습


Enum 과 HashMap

  1. gpt가 도와준 코드

코드를 보기에 앞서 해당 코드의 큰 흐름은
fromId(int id) 에서 나온 상수 를, calculate 메서드의 매개변수인 OperationEnum operation 자리에 매개인자로써 들어가면서 산술 기능을 수행하는 흐름입니다.

package PracticeEnum;

public enum OperationEnum {
    PLU("+", 1),
    MIN("-", 2),
    MUL("*", 3),
    DIV("/", 4);

    private final String symbol;
    private final int id;

    OperationEnum(String symbol, int id) {
        this.symbol = symbol;
        this.id = id;
    }

    // ID로 enum 찾기
    public static OperationEnum fromId(int id) {
        for (OperationEnum op : values()) {
            if (op.id == id) {
                return op;
            }
        }
        throw new IllegalArgumentException("Invalid operation ID: " + id);
    }

    public String getSymbol() {
        return symbol;
    }

    public int getId() {
        return id;
    }

    // 연산 메서드
    public static int calculate(int num1, int num2, OperationEnum operation) {
        switch (operation) {
            case PLU:
                return num1 + num2;
            case MIN:
                return num1 - num2;
            case MUL:
                return num1 * num2;
            case DIV:
                return num1 / num2;
            default:
                throw new IllegalArgumentException("Invalid operation: " + operation);
        }
    }

}

위 코드를 3번 정도 연습한 나의 코드

package Practice.P1;

public enum P3 {

    PLU(1,"+"),MIN(2,"-"),MUL(3,"*"),DIV(4,"/");

    private final int sign;
    private final String signal;

    P3 (int sign,String signal){ 
        this.sign=sign;  // 후에 필드를 지정
        this.signal=signal;
    }

    public static P3 hashMapEnumSign(int sign){ 
        for(P3 p3 : values()){
            if(p3.sign == sign){
                return p3;
            }else{
                throw new IllegalArgumentException("exception : " + sign);
            }
        }
    }

    public int getSign(){
        return sign;
    }
    public String getSignal(){
        return signal;
    }

    public static int operation(int num1,int num2,P3 p3,int sign){
        switch (p3.hashMapEnumSign(sign)){
            case PLU ->  return num1 + num2;
            case MIN ->  return num1 - num2;
            case MUL ->  return num1 * num2;
            case DIV ->  return num1 / num2;
                default -> throw new IllegalArgumentException();

        }
    }
}

내가 연습하며 쓴 코드에서 모르는 부분을 다시 질문!

질문 외에 틀린 부분은 숙지했고, 위 코드는 3회차 연습한 결과이니 오해 ㄴㄴ

  • switch 문 틀
  • hashMapEnumSign 메서드에 예외처리 위치

질문

  1. P3 p3 : values() 에서 values() 메서드를 따로 혼자 쓰는 것이 어떻게 되는가?
    public static P3 hashMapEnumSign(int sign){ 
        for(P3 p3 : values()){
            if(p3.sign == sign){
                return p3;
            }else{
                throw new IllegalArgumentException("exception : " + sign);
            }
        }
    }
  1. static and non-static
// ID로 enum 찾기
    public static OperationEnum fromId(int id) {
        for (OperationEnum op : values()) {
            if (op.id == id) {
                return op;
            }
        }
        throw new IllegalArgumentException("Invalid operation ID: " + id);
    }

답변

  1. P3 p3 : values() 에서 values() 메서드를 따로 혼자 쓰는 것이 어떻게 되는가?

values()는 enum 클래스에 포함된 모든 상수를 배열로 반환하는 메서드이고, 호출 방식은 두가지이다.

  • 클래스 이름을 붙여서 호출 - 클래스명.메서드
  • 메서드 단독 호출 가능 - 컴파일러가 자동으로 클래스명.메서드 로 처리하기 때문

🚩 ps

  • P3 p3 부분은 P3가 열거형(enum) 타입 p3는 변수명을 의미한다.
  • 각각의 상수(PLU, MIN, 등)는 OperationEnum(P3) 클래스의 정적(static) 객체로, 미리 생성된 하나의 고정된 인스턴스이다.
  1. 🚩 static and non-static

fromId() 메서드 < - > getSymbol()

우선 static 클래스 내에 객체없이 이용할 때 쓰고, heap영역이 아닌 메서드 영역에 저장된다. 를 가지고 질문을 했다.

fromId()에서 static을 쓴 이유 :
객체를 생성할 필요가 없기( = 클래스 내에 객체없이 이용 ) 때문에 사용했다.
getSymbol()은 그럼 왜 static을 쓰지 않을까?

getSymbol()에서 static을 쓴 이유 :
getSymbol()은 특정 상수 객체(PLU, MIN, ...)가 존재할 때만 의미가 있는 동작이다.
객체(상수)의 인스턴스 변수(symbol)에 접근해서 ( - > 표현이 헛갈리지만 상수의 변수 symbol을 말하는 것 ) 그 값을 반환하는 메서드이기 때문에, 특정 객체 없이 호출하는 건 불가능해하다( 코드에서 a = 상수 이다.를 표현할 때, 무조건 상수를 가지고 와야한다. ).
각 상수는 고유한 symbol 값을 가지며, 이를 반환하기 위해선 해당 객체에 접근하는 인스턴스 메서드이다.

메서드static or instance이유
fromId()static클래스 전체 데이터를 다루고, 특정 객체와 무관하게 동작하기 때문에
getSymbol()instance특정 객체의 상태(symbol)에 의존 하며, 객체가 있어야 의미 있는 동작이기 때문에

ps
static and non-static

static
static 메서드는 클래스 단위로 동작한다.
- > static 메서드에서 사용하는 데이터는 메서드 영역의 클래스 정보와 정적 변수를 기반으로 작동한다.

정적 변수란?
정적 변수(static variable)는 static 키워드가 붙은 클래스의 필드이다.
이 변수는 클래스 수준에서 하나만 생성할 수 있고, 모든 객체가 하나의 동일한 정적 변수를 공유해 사용하게 된다.

객체와 독립적
정적 변수는 특정 객체가 아닌 클래스 자체에 속해. 따라서 객체를 생성하지 않고도 사용할 수 있다.

예시 코드

class Counter {
    // 정적 변수: 모든 객체가 공유
    static int count = 0;
    // 인스턴스 변수: 객체마다 독립적으로 존재
    int instanceCount = 0;
	// 정적 변수 증가
    static void incrementStatic() {
        count++;
    }
	// 인스턴스 변수 증가
    void incrementInstance() {
        instanceCount++;
    }
}
public class Main {
    public static void main(String[] args) {
        // 정적 변수 접근 (객체 생성 없이도 가능)
        Counter.incrementStatic(); // count: 1
        System.out.println("Static count: " + Counter.count);
		// 인스턴스 변수는 객체를 통해 접근
        Counter obj1 = new Counter();
        obj1.incrementInstance(); // obj1의 instanceCount: 1
        System.out.println("Instance count (obj1): " + obj1.instanceCount);
		// 또 다른 객체 생성
        Counter obj2 = new Counter();
        obj2.incrementInstance(); // obj2의 instanceCount: 1 (독립적)
		// 정적 변수는 두 객체에서 공유
        System.out.println("Static count: " + Counter.count);
        System.out.println("Instance count (obj1): " + obj1.instanceCount);
        System.out.println("Instance count (obj2): " + obj2.instanceCount);
    }
}

non-static

non-static 메서드는 객체의 속성(필드)에 접근하며, 메서드 영역의 클래스 정보를 참조해 동작한다.

특성static (fromId)non-static (getSymbol)
호출 방식클래스명으로 호출 (객체 불필요)객체(인스턴스)를 통해 호출
객체와의 관계특정 객체와 독립적으로 동작특정 객체(인스턴스)에 의존
데이터 접근 범위클래스 수준 데이터 (values() 등)인스턴스 수준 데이터 (symbol, id 등)

Enum 과 HashMap 하면서...

  1. Enum은 기본적인 클래스와 필드, 생성자 만드는 것이 조금 다르다
 PLU(1,"+"),MIN(2,"-"),MUL(3,"*"),DIV(4,"/");  // < -

    private final int sign;
    private final String signal;

    P3 (int sign,String signal){ // < -
        this.sign=sign;  // 후에 필드를 지정
        this.signal=signal;
    }
  1. this를 적재적소에 잘 쓰자
public int getSign(){
        return sign;
    }
    public String getSignal(){
        return signal;
    }

해당 부분 메서드의 매개변수에 필드인 sign 혹은 signal과 같은 변수를 쓰지 않기에 굳이 this를 붙일 필요가 없다.

자! 여기까지가 Enum 코드 분석 및 연습



회고

오늘은 이상하게 느낀 것이 많은 날이다. 공부를 잘 했다는 뜻인가.. 시간을 잘 쓰지 못했다는 것인가...

클래스를 많이 나눈다고 좋은 것이 아니다!
- > 나눌수록 클래스안에 메서드와 필드를 공유하게 되어 캡슐화에 맞지 않다.
- > 그래서 웬만하면 해당 클래스 안에 피연산자나 매서드에 필요한 것들이 다있다면, 연산을 마치고, 외부에 그 값만 전달( getter 로)하는 것이 좋다.
그리고 클래스를 나누었던 안 나누었던, 외부에 보여질 필요없는 것은 private 를 생활화하자(매서드도 마찬가지!)

알고 있다고 넘어가지말고, 다른 예제로 연습해보자

gpt를 이용하려거든, 위에 작성한 것처럼 제대로 분석하고 연습해보자

... 등

째든 그래도 gpt가 작성한 코드의 리뷰 그리고 Enum 강의 마지막으로 배열 강의 (일부)의 키워드 정리...

내일은 더 열심히 하게 빨리 자야겠다...

ps
커맨드
ctrl + alt +shift + -> 는 코드가 좌우로 이동

profile
Spring_4

0개의 댓글