til(11_19)

김태은·2024년 11월 19일

오늘의 몸상태는 어제보다는 좋은 최악단계 중 중간이다.
어제는 몸상태가 많이 안좋아서, 공부를 하는데 있어서 많은 어려움이 있었다...
til 작성하려 했는데...ㅠㅠ

오늘은 어제 공부하던것들 다시 공부하며 정리할 예정이다.

제네릭 강의, static 강의

+ 오늘의 학습 강의 enum 되시겠다.
n회차 공부를 할 것이므로, 간단 키워드 정도로 정리 할 것으로 보인다.

+ lv3 계산기 다시 도전 - > 실패 ㅠ

+ 회고

제네릭



코드의 재사용을 위해 매개 변수 자리에 오브젝트를 넣어보자! - > 이러면 어떤걸 넣어도 되니까!


제네릭의 필요

제네릭 없이! 오브젝트를 쓰면 어떨까?

오브젝트를 쓴다면? 몇가지 문제가 있다.

강의 듣지 않은 상태에서도 알듯이... 값들을 모두 다운 케스팅(다운 그레이) - 매번 형변환을 해야하는 문제가 있다.

또 object는 최상위에 위치하여 의도한 것 외에 다른 인자를 써도 프로그램이 돌아가는 문제가 발생함을 알 수 있다.( ex Integer를 넣어야 할 자리에 String이 들어가도 프로그램이 돌아감! )


그리하여

제네릭을 쓰는 것이다~

재사용성과 타입 안전성이 높기 때문에!



제네릭

제네릭은 보통 새로운 클래스를 하나 파서 코드를 작성한다.

코드 작성

public class 제네릭이름 <T>{} // < - T가 아니어도 된다!

<> 가 있으므로 제네릭임을 알 수 있다. <> 안에는 타입 매개변수가 들어간다.
{} 안에는 필드와 각 메서드를 정의해 놓을 수 있다.

//선언과 생성
제네릭명<타입 매개변수> 변수명 = new 제네릭명<타입 매개변수>();
// 보통 인스턴스를 만드는 과정과 비슷하다
// 생성시점에서 타입 매개변수를 결정한다 < - 제네릭 클래스에서

위 설명과 같이 제네릭은 생성시점에서 타입 매개변수를 정해주기 때문에 기존에 클래스 인스턴스화랑은 다른 점이 있다.

ps 제네릭은 생성자가 필요없다 - > 애초에 <> 안에 타입 매개변수를 정해주고 선언 및 생성을 진행하기 때문에! < - 내가 강의를 들으면서 생각한 내용이므로... 혹시 참고하려면 이 부분은 혼자서 더 공부하시길...

타입 매개변수는 많이 써도 된다! ( ex <t,q,y,....> < -이 처럼 )

로 타입 '<>' 를 안쓰는 것 < - 안써도 되지만, 모두 object로 되고, 제네릭을 쓰면서 얻은 타입 안정성이 사라질 수 있다...( < - 는 제네릭을 지원하지안는 하위 버전에서만 어쩔 수 없이 쓰는 것 )



제네릭 상속

제네릭 없이! 상속을 쓴다면?!

- > 이것또한 부모를 기준으로 자식 이 들어올 수 있어, 원하는 값을 도출할 때, 문제가 생길 수 있다.( < - 물론 컴파일 오류는 나지않는다. 타입 안정성이 떨어진다. )


제네릭 상속

이때, 주위해야할 점이 있다. 제네릭을 보통 만드는 것 처럼 만든다면,

public 부모 제네릭명<타입 매개변수>{}

부모 클래스의 메서드를 사용할 적에 문제가 발생한다. 타입 매개변수에 부모클래스를 넣고 제네릭을 돌린다고 해서 모든 코드가 돌아가는건 아니다! - > 타입이 설정되는건 생성시점이므로 타입 매개변수 자리는 생성시점 전에는 미정상태라는 것이다.

쉽게 말해서 제네릭에 메서드의 실행문을 작성할 때, 적용하려는 부모 클래스의 메서드를 적는 다한들, 타입 매개변수에 뭐가 들어올지는 미정 상태라 컴파일 오류가 발생한다 는 뜻이다.

위 문제를 해결하고자 ( - 메서드를 해당 클래스의 사용 문제) 최상위인 object를 쓰는 것을 고려할 수 있다.

이것 또한, 모든 object가 사용가능한 메서드인 toString 정도? 만 쓸 수 있기에 (같은 문제 - 어떤 것이 들어올지 모르므로 다 사용가능한 메서드만 사용가능한 문제...) object를 사용하면, 필요로하는 메서드를 사용이 안되는 것을 느낄 것이다.

그러면 어떻게 해야할까?

제네릭 클래스명부터 고쳐보면,

public class 제네릭명 < T extend 부모 클래스 >{}

위 내용을 해서해보자면 생성시점에서 넣어줄 타입은 부모클래의 자식이 되는 것만 받겠다! 이다. 그러니 해당 제네릭 클래스에서 사용가능한 메서드는 모두를 포함한 부모클래스의 메서드가 된다!

이해를 했을까?

타입 매개변수를 제한 함으로써, 부모클래스의 메서드를 제네릭 메서드에 쓸 수 있게 된것이다!

이해를 못했다면 더 찾아보실길... 코드 예시는 귀찮아서 생략할 예정이므로...

---🚩
내가 빼먹은 부분 ps
void는 리턴 타입이 없어 제네릭 클래스 타입에 영향을 받지만,
밑에서 다루겠지만 return 타입이 있는 제네릭 메서드는 다르다!

⛔ ---


제네릭 메서드

제너릭 메서드 말그대로 제네릭 클래스 안에 잇는 메서드이다. 위에 내용에서 조금씩 제네릭 메서드의 내용이 가미되었는데, 사실 코드의 형태는 클래스에서 메서드를 만들때와 비슷한 구조를 가진다.


제네릭 메서드 구조

우선 구조를 알아보기 전에 클래스에 메서드 구조를 생각해보자!

접근제어자 리턴타입 메서드명(매개 변수){ 실행문 ... return 리턴값; }

요 부분에서 제네릭 메서드를 도입하면 리턴 타입과 매개변수만 고쳐주면 끝!

리턴타입에는 <T - ex U, 혹은 원하는 T 등등 >를 이용하여 리턴타입을 지정해주고, T ( - 앞에 것과 동일하게! ) 를 바로 뒤에 적어준다. 후에 메서드명을 기입 , 매개변수는 소괄호에 ( T t - ex U, 혹은 원하는 T 등등 ) 이런식으로 입력해준다!

---🚩
내가 빼먹은 부분 ps

밑에서 해당 내용을 적을테지만,
제네릭 클레스의 타입과 (static 뿐만아니라) 메서드의 타입이 같을 필요는 없다.

제네릭 클레스와 제네릭 메서드에서 타입은 별개!!

⛔ ---

public <T> T 메서드명(T t){... return 리턴값; }

이때 해당 메서드 안에서 타입의 제한을 걸 수도 있다.

public <T extend 부모 > T 메서드명(T t){... return 리턴값; }

제네릭 메서드의 기본형은 클래스의 메서드 정의 형식과 매우 유사함으로 리턴 타입을 지정해주는 부분을 잘 숙지하면, 금방 코드를... 아마 작성할 수 있을 것이다. (물론 본인은 그렇게 금방 안되긴 했음..)


제네릭 메서드 호출

클래스 인스턴스의 메서드 호출은 인스턴스.(dot) 메서드명(); 의 형식이였다.

제네릭 메서드의 호출도 이와 비슷하다.
호출시에 인스턴스 앞에 <>를 이용하여 원하는 리턴타입을 명시해주면 된다. + 메스드 매개변수도 이와 같다.

ps 타입 추론 가능하다!
물론 메서드의 매개변수와 메서드의 타입이 서로 같을 때, 혹은 본인이 여러 코드를 작성해 보지는 않았지만, 매개변수는 순서가 중요하니 여러 매개 변수가 있다고 가정하고, 매개변수의 순서와 맞는 메서드의 타입이 같다면 생략이 가능할 지 싶다. ( - > '호출시에 인스턴스 앞에 <>를 이용하여 원하는 리턴타입을 명시' 요부분을 생략 할수있는뜻이다. )

이때 타입 인자의 전달은 메서드를 호출한 시점이다.

곧, main 클래스에서 메서드를 호출할 적에(직전) 코드에 적힌 타입 인자를 가지고, 제네릭 메서드에 가서 실행! 이라 생각하자 ( - 내가 이해한 것으로 그렇다... )

---🚩
생각보자 중한 ps

제네릭 메서드는 static 메서드로도 가능한다. 위는 인스턴스 메서드 이다.

다만, static은 인스턴스 생성과 무관하다.(static은 메서드 영역에 저장 , 인스턴스는 heap 영역에 저장 - static은 클래스 단위로 작동 )
제네릭 타입은 인스턴스 생성시점에서 타입을 전달하여( 제네릭 클래스를 생성할 적에 지정한 타입 ( - > '<>' ) .. 서로 타입 전달 시점이 다름 ) 무관하다는 뜻으로 생각된다.

그래서 static 메서드를 만들적엔 제네릭 클래스를 만들지 않고, 제네릭 메스드를 만들수있다. 로 생각하면 되겠다. static 메서드에 제네릭을 도입하려면 제네릭 메서드를 사용해야한다. ( - 해당 개념은 static을 공부 해보면 조금 도움이 될 듯하다. 물론 본인은 공부를 했지만, 위 내용을 다 이해하고 정리하는 것은 아니다... 너무 슬프다...)

⛔ ---



제네릭 타입과 제네릭 메서드

제네릭 타입과 제네릭 메서드

제네릭 타입과 제네릭 메서드가 동시에 있다면? 결론은 제네릭 메서드가 우선이다! 쉽게 [{()}] 이런식으로 되어있으면 가장 안쪽부터 푸는 것과 같다.

위 말을 보면 알 수 있듯이 타입의 제한 또한, 서로 다르게 적용됨을 알 수 있다. 제네릭 메서드에서 리턴 타입의 제한을 두고 싶다면 \에서 \<T extends 부모> 를 적어주어야한다.

ENUM

정확한 상수를 입력해야 할 때, - > 다른 값들을 넣으면, 적용해야하는 기능이 먹지 않을 때 쓴다!

생성자에서 private를 쓰는 특별한 ENUM ! 생성 못하게 막는



코드

계산기 lv3

Enum - 산술 연산자

Generic을 넣은 Interface에 매개변수 함수를 넣어 람다식 활용하기 - 조회 기능

Generic - 타입 변환

해당 기능을 구현하려 했지만 번번이 실패했다... 마지막으로 강의를 들은 직후, 다시 도전해 봤지만 같은 곳에서 막혔다.. ㅜ

잘 이해했다고 생각했는데 아니였나보다...

그래서 실패한 코드를 가지고 원하는 결과를 뽑기위해 어떤 것이 빠졌나를 알기위해, gpt에게 도움을 청했다.

해당 코드들을 velog에 적을지 아니면, git에 따로 모아 정리할지 고민이다...



회고

오늘은 어제에 비해 많은 것을 했다고 생각한다. 강의 내용을 정리하면서 아는 내용은 다시 한번 공부할 수 있었고, 다른 개념과 연결되는 부분까지 체크 할 수 있어 공부 만족도가 높았다. 또, 헛갈리는 내용은 튜터님에게 물어봄으로써 잘 이해했음을 느꼈다.( lv3 계산기와는 별개로.. )

내일은 해보고 싶은 것이 있다. 과제 제출은 당연한 거고, 오늘 gpt에게 받은 코드를 오전 내내 치면서 어느정도 내것으로 만들것이다. 또, 해당 코드를 조금씩 바꿔가면서 내것으로 더 고착화 할 것이다

내일이 기대된다! 화이팅이다! 다들 ㅠ

ps 코드에 회색으로 표시되면 생략가능!(다는 아니고...) - ex 타입 추론

profile
Spring_4

0개의 댓글