본문 바로가기

우테코/level1(코틀린)

[우테코]로또 미션 코드리뷰 관련 학습내용

멧돼지 피드백 관련 학습


질문과 답변

1. 원시값 포장의 이점과 기준?

원시값들을 모두 포장하려고하니 로또구입금액, 로또구입개수 같은것들은 거의 같은 역할을 하는것들이라 검증 하는 내용들이 같아 중복이 일어났습니다. 게다가 로또구입 금액을 가지고 로또구입 개수를 구하는 형태이다보니 연속으로 같은 검사를 중복해서 해야할 필요가 있나 느껴졌습니다.
하지만 원시값들을 포장해놓고 아무런 검증이나 내부 함수가 없다면 원시값을 포장하는 의미가 있나? 라는 생각이 들어 모든 원시값을 포장하는것이 옳은가라는 생각이 들었고 원시값을 모두 포장한다해도 중복되는 검사또한 해야하는지 궁금합니다.

원시값을 무조건 포장하라는 의미를 그대로 해석하시지 말고,
근본적으로 포장하는 것에 대한 이유를 설명할 수 있어야 합니다.

Int 값을 단순히 포장하는 것외에도, 맡은 역할이 무언가는 있을 거에요.

 

원시값의 이점을 설명한 테코블이다

https://tecoble.techcourse.co.kr/post/2020-05-29-wrap-primitive-type/

 

원시 타입을 포장해야 하는 이유

변수를 선언하는 방법에는 두 가지가 있다. 원시 타입의 변수를 선언하는 방법과, 원시 타입의 변수를 객체로 포장한 변수를 선언하는 방법이 있다. (Collection…

tecoble.techcourse.co.kr

원시값 포장의 장점

1.자신의 상태를 객체 스스로 관리할 수 있다 (값 검증이 포장된곳으로 옯겨간다는뜻

2.코드의 유지보수에 도움이 된다. -> 위의 이점과 함께 로직이 각각 분리됨으로써 유지보수도 쉬워질수 있다는것을 말한다.

3.자료형에 구애받지 않는다. (여러 타입의 지원이 가능하다.) -> 멤버변수와 생성자 오버라이딩으로 여러가지 변형이 가능하여 유지보수성이 올라간다.


인스턴스화는 왜 필요한 것인가?

상태값을 가지지않는 class들은 object로 변경해보라는 피드백을 보고 적용해 보았습니다.인스턴스 없이 많은 함수들을 프로젝트 전체에서 접근할수 있도록 object로 변환하는것이 좋은건가 라는 의문이 들어서 리뷰어님의 생각이 듣고싶습니다.

인스턴스화 라는 것이 왜 필요한지를 스스로 찾아서 답해보셨으면 좋겠습니다.
object가 제공하는 함수와 class가 제공하는 메소드는 어떤 차이를 가질까

 

인스턴스는 추상화 개념 또는 클래스 객체, 컴퓨터 프로세스 등과 같은 템플릿이 실제 구현된 것이다. 인스턴스화는 클래스 내의 객체에 대해 특정한 변형을 정의하고, 이름을 붙인 다음, 그것을 물리적인 어떤 장소에 위치시키는 등의 작업을 통해, 인스턴스를 만드는 것을 의미한다.

 

인스턴스 화라는것은 컴퓨터내에서 실행시킬수있는 실행파일을 만드는것이다. 즉 jvm 이 관리하는 메모리에 적재된 것이다.

-> object는 항상 메모리에 올라간형태로 만드는것이고

-> 인스턴스화 시켜서 사용하는것은 메모리에 필요할때 객체를 만들어서 사용하는것이므로 메모리 관리를 위해서 인스턴스화 시킬 수 있는것들은 인스턴스화 시키는것이 더 올바른 사용법이라고 판단된다.

 


3.데이터 클래스

자 막연하게 항상 어쩌구 클래스 하면 두려움부터 생긴다 하지만 클래스 시리즈중 최약체 데이터 클래스는 두려워할 필요가 전혀없다.

그냥 단순히 자주 쓰는것들을 코틀린에서 자동으로 미리 완성해놨을 뿐 그이상도 이하도 아니다 그냥 적절할때 맞춰서 데이터 클래스를 잘사용해보자.

 

구글링해서 자료를 찾아보고 이차검증으로 코틀린 인 액션을 살펴보았다 사랑해요 코틀린 인 액션

일단 구글링에서 빠르게 참고할만한 글을하나 링크로 남기겠다.

https://readystory.tistory.com/85

 

코틀린(Kotlin)의 데이터 클래스(data class)

자바와 코틀린의 data class 이름, 나이, 성별을 갖는 데이터 클래스가 있다고 하자. 자바로 구현한다면 아래와 같을 것이다. public class Person { String name; int age; String gender; public Person(String name, int age,

readystory.tistory.com

이제 데이터 클래스가 자동으로 해주는 일을 공부하기전 사전지식을 좀 가지고 가자

 

이 이야기를 하려면 길어질것 같다 궁금해서 구글링하고 챗gpt한테도 물어보고 킹갓제너럴 제이슨 코치님한테도 물어봐서 풀린 의문이다.

 

- 동등성 동일성 

동등성: 값이 같은지 비교 라고 알고있었지만 사실상 언어마다 비교하는 기본적인 방법이있고 그것은 사용자가 정의해서 사용하는 방법이 옳다.

동일성: 주소값이 같은지 비교 

 

- 자바는 어떻게 동등성과 동일성이 돌아갈까?

동등성비교: equals

동일성 비교: ==

즉 자바의 == 은 뭐가 오던간에 주소값을 비교하는 형태이다.

equals 을 통해 동등성을 비교할수 있도록 해놨는데 값을 비교하고싶거나 어떤 사용자 관점의 기준을 가지고 비교하는것이다.

이런 사용자 기준을 가지고 비교할수있게 동등성을 내멋대로 정의할수 있도록 해놓은것이다.

 

equals는 기본적으로는 주소값을 비교하게 설정되어있다.(사용자에 맞게 오버라이딩 하도록 되어있다.)

그래서 내가 각 객체 혹은 값을 기준에 맞춰서 (ex: 값의 크기) 비교할수 있도록 열어둔것이다.

 

근데 이상한일들이 있다. 같은String 값들을 오버라이딩 하지 않은 equals로 비교하면 true 가 나온다.

String str1 = "madplay";
String str2 = "madplay";

str1.equals(str2);
//true

주소값을 비교하면 분명 다른 변수에 담았으니 false가 나와야하는데이게 뭔일이람?

제이슨의 설명으로 (나의 정리에의해서 왜곡될수있음)

자바의 리터럴을 이용해서 값을 만들경우 String Constant Pool이라는 곳에 캐싱되어서 같은 문자열값으로 되어있을경우 거기있는 값을 꺼내오기 때문에 값이 같다면 주소값도 같은 형태로 찍힌다  -> 그래서 주소값을 디폴트로 비교하고있는 equals 에서도 true가 나오는것이다. 

그래서 new를 통해서 새로운 객체주소에 같은 값으로 문자열을 담아서 비교하면 같은 값이 담겨있어도 당연히 주소값이 다르니 false 가 나온다.

제이슨의 설명을 정리되어있는 블로그를 첨부한다.

https://madplay.github.io/post/java-string-literal-vs-string-object

 

자바의 String 객체와 String 리터럴

자바에서 문자열을 선언하는 방법은 두 가지가 있다. String과 new String()은 어떤 차이가 있을까?

madplay.github.io

 

 

https://kotlinlang.org/docs/equality.html

 

Equality | Kotlin

 

kotlinlang.org

 

- 이제 코틀린의 동등성 동일성.

동등성: == 이거 부르면 자동으로 equals 호출함 그러므로 커스텀하고싶으면 equals 오버라이딩 해주면됨

동일성: ===

 

자바위에서 돌아가므로 동등성 동일성을 똑같이 생각하면된다.

그리고 == 은 그냥 동등성을 비교하기위한 추상자료형이라고 생각하면되고 기본적으로 동등성을 비교하기 위해서는 equals를 중점적으로 보면 된다.

동등성을 비교하는 equals 는 자바와같이 정의안한다면 주소값을 비교하고

원하는 조건에 따라서 equals를 재정의하면 된다.(참고로 인텔리제이에서 equals 자동완성 기능을 이용하면 기깔나게 값비교하는 것은 만들어준다.)

 

-equals()

equals 가 무엇인지는 대충 위에서 다 설명됐을 것이라 생각한다. 기본적으로 equals는 주소값을 비교하도록 설정되어있고 값비교 같은것을 하고싶을때 재정의하는것이다.

기본적으로 자동완성 해주는 기능을 이용해서 만들어보자

 

- hashCode()

해시가 뭔지는 자료구조 cs같은데서 다시 공부해야하겠다 하지만 우테코에 쫓기는 나는 현재 시간을 많이 낭비했기 때문에 간단하게 정리하고 언젠가 다시 돌아올것이다.

https://100100e.tistory.com/352

 

객체의 해시코드(hashCode())란

객체 해시 코드란 객체를 식별할 하나의 정수 값을 말한다. Object는 클래스의 최상위 타입이고 Object의 hashCode() 메서드는 객체의 메모리 번지를 이용해서 해시 코드를 만들어 리턴하기 때문에 객

100100e.tistory.com

일단 대충정리하자면 해시코드는 객체를 구분하기위한 하나의 정수값인데

객체의 메모리 번지수를 기반으로 계산해서 나오는 수이다.

 

이런 해시값을 통해서 hashMap,hashSet 이런거 사용하면 원래 탐색하고 이런거 시간복잡도 높은데 해시값을 이용해서 마법같이 빠르게 탐색한다고한다.

 

근데 저런 해시어쩌구 자료형은 동등성으로 값을비교(equals를 호출) 하기전에 해시값부터 비교해서 해시값이 다르다면 애초에 실제값을 비교하지 않는다. 

 

그래서 equals를 잘 오버라이딩 했어도 제대로 안움직일수 있다.

해시 어쩌구 자료구조가 잘 동작할수있도록 해시값을 구해내는 hashCode()함수도 equals를 오버라이딩할때 자바에서는 필수로 코틀린에서는 뭐세트세트긴 하지만 강제는 아니게 되어있다.

 

 

그래서 데이터 클래스가 뭔데 씹덕아!!!!

위에 블로그글을 참고하면 자세히 나와있고

키워드만 보자면 Any에 정의되어있는 함수인 Canonical Methods(equals,hashcode,toString)을 자동으로 값비교하고 문자열로 바꿔주게 오버라이딩 해주고

단 이것은 주생성자에 들어있는 프로퍼티에만 해당한다 즉 안에 프로퍼티 그냥 만든것들은 이 함수의 혜택을 못받는다.(코틀린 공식문서 보면 나옴)

그리고 copy 메서드 편하게 쓸수있고

디스터쳐링이라는것을 지원해서 파이썬처럼 분해해서 각각 변수에 선언할수 있다.


4.오류처리

이렇게 일반화된 오류처리가 오히려 악영향을 미칠수 있다고 생각해본적이 없었다.

생각해보니 IlligalArgumentException이라고 죄다 재입력을 받고 이러는건 지금 상황에서나 맞지 절대 일반적이라고 생각이 들지않아 주의해야함을 느꼈다.


5.팩토리 메서드 진심모드

https://blog.kotlin-academy.com/item-30-consider-factory-functions-instead-of-constructors-e1c747fc475

 

Item 30: Consider factory functions instead of constructors

Yes, primary constructor, but how about other ways to create a class? Part of Effective Kotlin by Marcin Moskała.

blog.kotlin-academy.com

이펙티브 코틀린이지만 어쨋든 이글은 팩토리함수에 진심이다. 읽어보고 테스트에서 private 팩토리함수로 테스트할때 불편함을 줄였다.

 

글 자체는 내용이 워낙 방대하기 때문에 간단하게 필요한 내용만 요약하도록 하겠다.

팩토리 메서드를 사용하면서 얻을수 있는 이점

1.생성자와 다르게 이름을 가질수 있다.

→ 이름은 인스턴스가 어떻게 생성되는지 그리고 어떤 인자를 통해 성성되는지 설명할수있다.

2.생성자와는 다르게 팩토리 함수는 subtype의 인스턴스를 반환할 수 있다.

→ 인터페이스 뒤에 구현체를 숨기는 용도로 사용될수 있다. ListOf함수가 예시이다. 이 함수는 List인터페이스 자료형을 리턴하는것으로 되어있지만 실제로는 각 플렛폼(자바,자스,어떤 네이티브) 의 리스트구현체를 반환한다.

3.생성자와 다르게 항상 새로운 인스턴스를 만들필요는없다

→ 캐싱이 가능해진다.

4.팩토리 함수는 아직 만들어지지않은 객체또한 조달할수있다.

→ 라이브러리를 만드는 사람들이 이용할수 있다는데 이런건 필요할때 봐야할듯하다.

5.객체 밖에다 만들어 놓으면 접근을 원하는데로 제어할 수 있다.

→ 같은 모듈내 접근 아님 같은 파일내 접근 이런식으로

6.팩토리 함수는 생성자로 만들기 복잡한 객체를 만들수 있다.

  1. 생성자는 슈퍼클래스 또는 기본 생성자를 호출해야 하지만 팩토리 함수를 사용하면 원하는 때에 생성자를 호출할 수 있다.

팩토리 함수를 사용하는 5가지 방법

  1. Companion object factory function
  2. Extension factory function
  3. Top-level factory functions
  4. Fake constructors
  5. Methods on a factory class

1.Companion object factory function

주로 사용되는 방법이고 네이밍 컨벤션이있으니 고려해서 이름을 지어주자

2.Extension factory fuction

사용하고싶은 인터페이스,클래스를 건들 수 없을때 companion object 에다가 확장함수를 박을수 있다.

하지만 companion object가 꼭 있어야만 가능하다.

3.Top-level factory functions

최상위함수로 만들수 있다 주로 많이 사용되는 것들인 list map android의 Intent 같은것들이 이런것들로 만들어져 있는데 어디서든 호출할수 있음에 IDE자동완성을 이상하게 더럽힐수 있고 함수 메서드와 이름이 같아지면 또 문제를 일으키므로 현명하게 사용해야한다.

주로 작은단위로 많이 사용되는것들을 만들기위해 사용하면 좋다.

4.Fake constructors

원래 함수는 소문자시작 생성자는 대문자 시작이 컨벤션이지만 그것을 어긴 방법이다.

코틀린 표준 라이브러리에서 List 만드는 함수를 찾아보면

List(4) { "User$it" } // [User0, User1, User2, User3]

이런식으로 쓰는데

이거 리스트는 인터페이스 이기 때문에 이런식으로 인스턴스도 못만들거니와 생성자가 없다.

그래서 그 대용으로 사용하도록 만들어진게 이것이고 Facke constructors의 예시이다.

생성자 대신 가짜 생성자를 만드는 이유는 다음과 같다.

  • 인터페이스를 위한 생성자를 만들고 싶을 때
  • reified 타입 아규먼트를 갖게 하고 싶을 때

이런것들이 필요할 때 다짜 생성자를 만든다고한다.

5.Methods on a factory class

클래스는 상태를 가질수 있기 때문에 이런것들로 함수에비해 객체를 생성하는데 이점이 있을수있다.

이런것들을 이용하면된다.