📖
Kyu9's Repo
  • Library of mameil
  • 이슈 경험
    • 20230220_트랜잭션
    • 20230306_캐싱이슈
    • 20230722_테스트코드에서 @Transactional
    • 20230807_deadlock
  • 인턴 스터디
    • Gradle
    • Stream, Optional, 람다식
    • JVM의 메모리 구조, Garbage Collector
    • RESTful API
    • Microservice Architecture
    • HTTP
    • 웹서버란 무엇인가
    • Git Branch
    • TDD
    • Redis을 이용한 캐시
    • Thymeleaf
    • 정리가 필요한 자료들
    • SpringBoot Management
    • 테스크 코드 분할
  • 동아리 스터디
    • 기본 SQL 공부
      • SQL의 기본 개념
      • SELECT 문장을 이용하여 원하는 데이터 출력하기
        • 집합 연산자 사용하기
        • where절에 비교 연산자를 사용해보기
        • SELECT_EX
        • 산술 연산자 사용해보기
      • 단일 행 함수 사용
        • lower/upper 함수 사용하기
        • length함수 사용하기
        • concat함수 사용
        • substr/mid/substring 함수 사용
        • instr함수 사용하기
        • lpad/rpad 함수 사용하기
        • trim/ltrim/rtrim 함수 사용하기
        • replace 함수 사용하기
        • round 함수 사용하기
        • truncate 함수 사용하기
        • mod함수 사용하기
        • ceil함수 사용하기
        • floor함수 사용하기
        • power 함수 사용하기
        • Date fn(날짜 함수)
        • 형 변환 함수
        • 일반함수란
    • a-ha 실습
    • 혼자서 만들어본 게시판
    • AWS 강의
  • 학교 나머지 공부 자료
    • 웹프레임워크(Spring)
      • Spring이란?
      • Webframework1-1
      • Webframework1-2
      • SpringBoot의 특징
      • SpringBoot 생성 방법
      • Spring Data JPA
      • SpringBoot Security
      • SpringBoot HATEOAS
  • 공부 자료들
  • WS 온라인 자바 스터디
    • Week1(JVM은 무엇이며 자바 코드는 어떻게 실행하는 것인가.)
    • Week2(자바 데이터 타입, 변수 그리고 배열)
    • Week3(연산자)
    • Week4(제어문)
    • Week5(클래스)
    • Week6(상속)
    • Week7(패키지)
    • Week8(인터페이스)
    • Week9(예외처리)
    • Week10(멀티쓰레드 프로그래밍)
    • Week11(Enum)
    • Week12(Annotation)
    • Week13(I/O)
    • Week14(Generic)
    • Week15(람다식)
  • 백준문제
    • 입출력과 사칙연산
      • We love kriii(10718)
      • 고양이(10171)
      • 개(10172)
      • A+B(1000)
      • A-B(1001)
      • AxB(10998)
      • A/B(1008)
      • 사칙연산(10869)
      • 나머지(10430)
      • 곱셈(2588)
    • for문
      • 구구단(2739)
      • A+B - 3(10950)
      • 합(8393)
      • 빠른 A+B(15552)
      • N 찍기(2741)
      • 기찍 N(2742)
      • A+B - 7(11021)
      • A+B - 8(11022)
      • 별 찍기 - 1(2438)
      • 별 찍기 - 2(2439)
      • X보다 작은 수(10871)
    • if문
      • 두 수 비교하기(1330)
      • 시험 성적(9498)
      • 윤년(2753)
      • 사분면 고르기(14681)
      • 알람 시계(2884)
      • 오븐 시계(2525)
      • 주사위 세개(2480)
      • 영수증(25304)
    • While문
      • A+B - 5(10952)
      • A+B - 4(10951)
      • 더하기 사이클(1110)
    • 1차원 배열
      • 최소, 최대(10818)
      • 최댓값(2562)
      • 숫자의 개수(2577)
      • 나머지(3052)
      • 평균(1546)
      • OX퀴즈(8958)
      • 평균은 넘겠지(4344)
    • 함수
      • 정수N개의 합(15596)
      • 셀프 넘버(4673)
      • 한수(1065)
    • 문자열
      • 아스키코드(11654)
      • 숫자의 합(11720)
      • 알파벳 찾기(10809)
      • 문자열 반복(2675)
      • 단어 공부(1157)
      • 단어의 개수(1152)
      • 상수(2908)
      • 다이얼(5622)
      • 크로아티아 알파벳(2941)
      • 그룹 단어 체커(1316)
    • 기본수학-1
      • 손익분기점(1712)
      • 벌집(2292)
      • 분수찾기(1193)
      • 달팽이는 올라가고 싶다(2869)
      • ACM 호텔(10250)
      • 부녀회장이 될테야(2775)
      • 설탕 배달(2839)
      • 큰 수 A+B(10757)
      • Fly me to the Alpha Centauri(1011)
    • 기본수학-2
      • 소수 찾기(1978)
      • 소수(2581)
      • 소인수분해(11653)
      • 소수 구하기(1929)
      • 베르트와 공존(4948)
    • 재귀
      • 하노이 탑 이동 순서(11729)
      • 피보나치 수 5(10870)
      • 별 찍기(2447)
    • 브루트 포스
      • 블랙잭(2798)
      • 분해합(2231)
      • 덩치(7568)
      • 체스판 다시 칠하기(1018)
      • 영화감독 슘(1436)
    • 집합과 맵
      • 숫자 카드(10815)
      • 문자열 집합(14425)
      • 숫자 카드2(10816)
      • 듣보잡(1764)
      • 대칭 차집합(1269)
      • 서로 다른 부분 문자열 갯수(11478)
    • 정렬
      • 수 정렬하기(2750)
      • 수 정렬하기 2(2751)
      • 수 정렬하기 3(10989)
      • 통계학(2108)
      • 소트인사이드(1427)
      • 좌표 정렬하기(11650)
      • 좌표 정렬하기2(11651)
      • 단어 정렬(1181)
      • 나이순 정렬(10814)
      • 커트라인(25305)
      • 좌표압축(18870)
    • 백트래킹
      • N과 M - 1(15649)
      • N과 M - 2(15650)
      • N과 M - 3(15651)
      • N과 M - 4(15652)
      • N-Queen(9663)
      • 스도쿠(2580)
      • 연산자 끼워넣기(14888)
      • 스타트와 링크(14889)
    • 이분 탐색
      • 수 찾기(1920)
    • 동적계획법
      • 피보나치 함수(1003)
      • 신나는 함수 실행(9184)
      • 01타일(1904)
      • 파도반 수열(9461)
      • RGB거리(1149)
      • 정수 삼각형(1932)
      • 계단 오르기(2579)
      • 1로 만들기(1463)
      • 쉬운 계단 수(10844)
      • 포도주 시식(2156)
      • 가장 긴 증가하는 부분 수열(11053)
      • 가장 긴 바이토닉 부분 수열(11504)
      • 전깃줄(2565)
      • LCS(9251)
      • 연속합(1912)
      • 평범한 배낭(12865)
      • 더하기(9095)
    • DFS와 BFS
      • 미로탐색(2178)
      • 바이러스(2606)
      • DFS와 BFS(1260)
      • 단지번호붙이기(2667)
      • 전쟁 - 전투(1303)
      • 숨바꼭질(1697)
      • 데스 나이트(16948)
      • 나이트의 이동(7562)
      • 녹색 옷 입은 애가 젤다지?(4485)
      • 음식물 피하기(1743)
      • A->B (16953)
      • 숨바꼭질 3(13549)
      • 숨바꼭질 2(12851)
    • 구현
      • 치즈(2636)
  • 프로그래머스 문제
    • SQL
      • Animal Table - Oracle
      • Animal Table - MySQL
      • Animal Table2 - Oracle
      • Animal Table 3,4 - Oracle
    • Lv1
      • 두 개 뽑아서 더하기
      • 제일 작은 수 제거하기
      • 문자열 내 p와 y의 개수
      • 예산
      • 자릿수 더하기
      • 두 정수 사이의 합
      • 같은 숫자는 싫어
      • 가운데 글자 가져오기
      • 수박수박수박수박수박수?
      • 나누어 떨어지는 숫자 배열
      • 2016년
      • 폰캣몬
      • 서울에서 김서방 찾기
      • 문자열을 정수로 바꾸기
      • 소수 만들기
      • 문자열 다루기 기본
      • 소수 찾기(에라토스테네스의 체)
      • 숫자 문자열과 영단어
      • 이상한 문자 만들기
      • 없는 숫자 더하기
      • 문자열 내림차순으로 배치하기
      • 문자열 내 마음대로 정렬하기
      • 약수의 개수와 덧셈
      • 콜라츠 추측
      • 자연수 뒤집어 배열로 만들기
      • 신규 아이디 추천
      • 비밀지도
      • 크레인 인형뽑기 게임
      • 실패율
      • 로또의 최고 순위와 최저 순위
      • 키패드 누르기
      • 정수 내림차순으로 배치하기
    • Lv2
      • 행렬의 곱셈
      • 영어 끝말잇기
      • 영어 끝말잇기
      • N개의 최소 공배수
      • 피보나치 수
      • 124 나라의 숫자
      • 짝지어 제거하기
      • 프린터
      • 다음 큰 숫자
      • 최댓값과 최솟값
      • 최소값 만들기
      • 숫자의 표현
      • JadenCase 문자열 만들기
      • 오픈채팅방
      • 영어 끝말잇기
      • 멀쩡한 사각형
      • 올바른 괄호
      • 위장
      • 기능개발
      • 더 맵게
      • 스킬트리
    • 완전탐색
      • 모의고사(Lv1)
      • 카펫(Lv2)
      • 소수 찾기(Lv2)
    • 정렬(Sorting)
      • K번째 수(Lv1)
      • 가장 큰 수(Lv2)
      • H-Index(Lv2)
    • 해시(Hash)
      • 완주하지 못한 선수(Lv1)
      • 전화번호 목록(Lv2)
    • 탐욕법(Greedy)
      • 체육복(Lv1)
      • 큰 수 만들기(Lv2)
      • 구명보트(Lv2)
    • 동적계획법(DP)
      • 정수 삼각형(Lv3)
    • 깊이/너비 우선 탐색(DFS/BFS)
      • 타겟 넘버(Lv2)
      • 네트워크(Lv3)
      • 단어 변환(Lv3)
  • 스프링부트 책
    • Day 1
    • Day 2
    • Day 3
    • Day 4
    • Day 5
    • Day 6
    • Day 7
    • Day 8
    • Day 9
    • Day 10
    • Day 11
    • Day 12
    • Day 13
    • Day 14
    • Day 15
    • Day 16
    • Day 17
  • JPA 책
    • 프로젝트 세팅 및 기본설정
    • 영속성 관리 개념
    • 엔티티 매핑
      • 실습 예제
    • 연관관계 매핑 기초
      • 실습 예제
    • 다양한 연관관계 매핑
      • 다대일, 일대다 관계
      • 일대일, 다대다 관계
      • 실습 예제
    • 고급 매핑
      • 상속 관계 매핑
      • @MappedSuperclass
      • 복합 키와 식별 관계 매핑
      • 조인 테이블
    • 프록시와 연관관계 관리
      • 프록시
      • 즉시 로딩과 지연 로딩
      • 영속성 전이, 고아 객체
    • 값 타입
      • 임베디드 타입
      • 값 타입과 불변 객체
      • 값 타입의 비교, 컬렉션
    • 객체지향 쿼리 언어
      • JPQL part1
      • JPQL part2
      • JPQL part3
      • QueryDSL
      • NativeSQL
      • 객체지향 쿼리 심화
    • 응용 애플리케이션
      • 엔티티 설정
    • 스프링 데이터 JPA
      • 공통 인터페이스
  • Kotlin In Action
    • 코틀린의 특징
    • 코틀린의 기초
    • 함수 정의와 호출
    • 클래스, 객체, 인터페이스
    • 람다 방식
    • 코틀린 타입 시스템
    • 연산자 오버로딩과 기타 관례
    • 고차함수
    • 제네릭스
    • 애노테이션과 리플렉션
    • 코루틴
  • Oracle
    • Oracle 기본
    • Oracle 심화
  • SQL_연습
    • Revising the Select Query
    • Basic Select
    • Advanced Select
    • Basic Select 2
  • SQL 첫걸음(책)
    • Day 1
    • Day 2
    • Day 3
    • Day 4
    • Day 5
    • Day 6
    • Day 7
    • Day 8
    • Day 9
    • Day 10
    • Day 11
    • Day 12
    • Day 13
    • Day 14
    • Day 15
    • Day 16
    • Day 17
    • Day 18
    • Day 19
    • Day 20
    • Day 21
    • Day 22
    • Day 23
    • Day 24
    • Day 25
    • Day 26
    • Day 27
    • Day 28
    • Day 29
    • Day 30
  • 더 자바 코드를 조작하는 다양한 방법
    • JVM 이해하기
    • 바이트코드 조작
    • 리플렉션
    • 다이나믹 프록시
    • 애노테이션 프로세서
  • 더 자바, 애플리케이션을 테스트하는 다양한 방법
    • JUnit5
    • Mockito
    • 도커와 테스트
    • 성능, 운영이슈, 아키텍처 테스트
  • 이펙티브 자바
    • item1 - 생성자 대신 정적 팩토리 메소드를 고려하라
    • item2 - 생성자에 매개변수가 많다면 빌더를 고려하라
    • item3 - 생성자나 열거타입으로 싱글턴임을 보증하라
    • item4 - 인스턴스화를 막기 위해선 private 생성자를 사용하라
    • item5 - 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라
    • item6 - 불필요한 객체 생성을 피하라
    • item7 - 다 쓴 객체 참조를 해제하라
    • item8 - finalizer와 cleaner 사용을 피하라
    • item9 - try-finally 보다 try-with-resources을 사용하라
    • item10 - equals는 일반 규약을 지켜 재정의하라
    • item11 - equals을 재정의하려면 hashCode도 재정의하라
    • item12 - toString을 항상 재정의하라
    • item13 - clone 재정의는 주의해서 진행하라
    • item14 - Comparable을 구현할지 고민하라
  • Elastic Search
    • 강의 Summary
    • Elastic Summary 개념 정리
    • Elastic Summary 적용 정리
  • 토비의 스프링 강의
    • 스프링부트 살펴보기
    • 독립 실행형 서블릿 애플리케이션
  • k8s
    • minikube 설치
    • jenkins 추가
  • Article
    • Choosing the Right MessageBroker
Powered by GitBook
On this page
  • 제네릭 타입 파라미터
  • 제네릭스 동작
  • 변성: 제네릭과 하위 타입

Was this helpful?

  1. Kotlin In Action

제네릭스

애노테이션을 통해서는 라이브러리가 요구하는 의미를 클래스에서 부여하는 것이 가능하고시점에 컴파일러 내부 구조를 분석하는 것이 가능하다 ㅈㅈ

실체화한 타입 파라미터를 사용하면 인라인 함수 호출에서 타입인자로 쓰인 구체적인 타입을 실행한 시점에 알 수 있다 선언 지점 변성을 사용하면 기저 타입은 같지만 타입 인자가 다른 두 제네릭 타입 A, B가 있다고 가정할 때 타입 인자 A, B의 상위/하위 타입 관계에 따라 두 제네릭 타입의 상위/하위 타입 관계가 어떠한 관계인지 지정하는 것이 가능하다

제네릭 타입 파라미터

제네릭스를 사용하면 타입 파라미터를 받는 타입을 정의하는 것이 가능하다 제네릭 타입의 인스턴스를 만들기 위해서는 타입 파라미터를 구체적인 타입 인자로 치환해주어야 한다 자바에서오 동일하다 그냥 Map<K, V> 이러한 제네릭 타입을 가진 자료구조 타입이 있는데, Map<Int, String> 이런 식으로 구체적인 타입을 넘김으로써 타입을 인스턴스화할 수 있다

제네릭 함수를 사용할 때는 반드시 구체적 타입으로 타입 인자를 넘겨야만 사용하는 것이 가능하다 하지만 특정 리스트에 대한 함수를 사용하는 경우에는 컴파일러로 하여금 추론하게 할 수 있다

val letters = ('a'...'z').toList()
println(letters.slice<Char>(0..2))
println(letters.slice(10..13))

char 리스트인 letters에 대한 slice 함수를 사용하면서 첫 번째로 사용할 때는 타입을 명시해줌으로써 리턴타입을 명시해주었지만 두 번째로 해당 리스트의 한 번 추론해주었던 타입을 명시하는 순간 컴파일러는 slice함수의 타입을 Char라고 인식하고 작동하게 된다

제네릭 클래스의 선언은 자바에서 사용하는 것과 동일하게 <> 을 통해서 선언을 해주고 사용할 때는 특정 타입임을 명시해주면서 사용한다 제네릭 클래스를 확장하는 클래스를 정의할 때는 기반 타입의 제네릭 파라미터에 에 대한 타입 인자를 지정해주어야 함 - 하위 클래스도 제네릭이면 그 클래스의 구체적 타입을 주는 것도 가능하고 그냥 받은 파라미터로 받은 타입을 넘길 수도 있다

코틀린만의 특정 제네릭 특성에 대해서 보자

타입 파라미터 제약 타입 파라미터 제약이라은 클래스나 함수에 사용할 수 있는 타입 인자를 제한하는 기능이다 sum이라는 함수가 있을 때 -> Int,Double은 허용가능한 타입이고 String은 허용불가능한 타입으로 지정하고 허용불가능한 타입은 사용할 수 없게하는 것이 가능한 것이다 그 방법으로는 제네릭 타입을 선언하는데 있어서 허용하고 싶은 파라미터의 상한으로 지정하면 > 제네릭 타입을 인스턴스화할 때 사용하는 타입 인자를 반드시 그 상한 타입이거나 그 상한타입의 하위 타입이어야만 사용할 수 있다 그래서 위의 sum 예시에 적용하면 sum 제네릭 함수를 사용시 Int, Double 을 허용할 것이기 때문에 Number 타입을 상한타입으로 정해주면 가능하다 fun List.sum(): T 이렇게 사용하고 리턴하는 값인 T 같은 경우에는 그 상한 타입으로 값으로 취급하는 것도 가능하다는 점

이렇게 일차원적으로 넣어서 사용하는 값 말구 Compare함수와 같은 제네릭 함수를 생각해보자 compare 같은 경우에는 값을 2개 받아야하고, 그 2개의 값의 타입을 비교하고 그 타입을 비교하는 과정을 거치게 되는데, 그냥 아무런 타입이나 선언해두고 부등호(>,<) 이렇게 비교하는 걸 넣어두고 String 과 같은 타입을 넣어버리면 max을 비교할 수 없는 값 사이에 호출하면 컴파일 오류가 난다 파라미터에 2가지 이상의 타입 제약을 거는 것은 함수 내부에서 where T: Number, String 뭐 이렇게 타입 파라미터를 선언하는 곳에서 :을 통해서 선언해주는 것이 아닌 함수 내부에서 where 문을 통해서 특정 타입을 지정해준다

다음은 Nullable 이다 제네릭을 인스턴스화할 때 nullable 타입을 포함하는 어떤 타입으로 타입인자를 지정해도 타입 파라미터를 치환하는 것이 가능하다 만약에 아무런 상한을 정하지 않은 타입 파라미터는 결과적으로 Any? 을 상한으로 정한 파라미터와 같다 그래서 따로 ?으로 타입 파라미터를 지정해주지 않아도 기본적으로 Any? 으로부터 만들어지기 때문에 내부에서 해당 파라미터를 사용할때 nullable의 체크인 ?. 을 통해서 사용하는 것이 가능하다 그래서 만약에 non-nullable 타입을 제네릭 타입으로 받고 싶다면, T 와 같이 무명으로 타입 파라미터를 지정해주는 것이 아니라 Any 이 타입을 선언해주자 그러면 내부에서 non-nullable 방식으로 컴파일을 진행해준다 근데 any로 해두면 문제가 있는게 non-nullable 을 위해서 any를 설정한 것이지만, 모든 객체는 any로부터 파생되었기 떄문에 결국은 nullable 객체를 제네릭에 넣어주어도 들어간다는 것이고 그 들어간다는 의미는 즉 npe를 내뱉는다는 의미이다 그러니까 그냥 any가 아닌 그냥 non-nullable 타입을 사용해서 상한을 지정해주자

제네릭스 동작

JVM 에서 제네릭스는 타입 소거를 사용해서 구현되는데, 이건 실행 시점에 제네릭 클래스의 인스턴스에 타입 인자 정보가 들어있지 않다는 의미이다 코틀린에서도 그럼 결국 제네릭 클래스의 타입 인자 정보는 런타임시에는 지워진다는 의미인데, 예시로 2개의 List가 있다고 보자 List, List 이렇게 2개가 있는데 컴파일러는 두 리스트를 서로 다른 타입으로 인식하고 각각의 리스트에는 지정해준 타입의 객체만 채워질 수 있도록 보장해준다 근데 타입 소거로부터 발생하는 한계가 있는데, 타입인자를 따로 저장하지 않기 때문에 실행 시점에 타입 인자를 검사하는 것이 불가능하다 즉, is 검사와 같은 타입 인자로 지정한 타입을 검사할 수는 없다 실행 시점에는 List인지 아닌지에 대한 여부는 체크할 수 있지만, 그 List의 타입이 String 인지 Int 인지 이러한 여부는 알 수가 없다는 것이다 물론 이렇게 타입 소거에 의해서 메모리 사용량과 같은 이점을 볼 수 있다는 점도 있다 as, as? 에도 제네릭은 여전하게 사용하는 것이 가능하지만 기저 클래스는 같지만 타입 인자가 다른 타입으로 캐스팅해도 여전히 캐스팅이 가능하다는 점을 조심해주어야 한다 실행 시점에는 모르고 무조건 성공하기 때문에 컴파일러에서 unchecked cast으로 경고를 해주기 때문에 문제 없다고 생각해도 그정도는 고려할 수 있어야 한다 그냥 실행은 되어버리지만 만약에 잘못된 타입의 원소가 들어가면 ClassCastException이 발생 그래서 as 을 사용해서 바로 타입 캐스팅하는 것이 아니라 is 을 통해서 검사를 해주면 좋게 사용할 수 있다

제네릭 함수가 호출되어도 그 함수의 본문에서는 호출 시 쓰인 타입 인자를 알 수 없지만 이러한 제약을 피할 수 있는 경우가 있는데, 그 방법은 인라인이다 인라인 함수의 타입 파라미터는 실체화되기 때문에 실행 시점에 인라인 함수의 타입 인자를 알 수 있다 inline 이라는 키워드는 컴파일러는 그 함수를 호출한 식을 모두 함수 본문으로 변경해주는 키워드이다 함수가 람다를 인자로 사용하는 경우 그 함수를 인라인 함수로 만들면 람다 코드도 함께 인라이닝되고 그에 따라 무명 클래스와 객체가 생성되지 않아서 성능이 더 좋아질 수도 있다 인라인 키워드는 본문에 구현한 바이트 코드를 그 함수가 호출되는 모든 지점에 삽입하는 키워드이기 때문에 컴파일러는 실체화한 타입 인자를 사용해서 인라인 함수를 호출하는 각 부분의 타입을 실행시점에 보는 것이 가능하기 때문이다 인라인의 특징은 이전에도 인라인을 보면서 봤지만, 인라인 함수의 크기에 항상 주의를 해야한다는 점을 기억

실체화한 타입 파라미터로 클래스 참조 대신 java.lang.Class 타입 인자를 파라미터로 받는 api에 대한 코트린 어뎁터를 구축하는 경우에는 실체화한 타입 파라미털르 사용한다 JDK의 ServiceLoader는 어떤 추상 클래스나 인터페이스를 표현하는 java.lang.Class를 받아서 그 클래스나 인스턴스를 구현한 인스턴스를 반환한다 ServiceLoader으로 부터 서비스를 가져오는 방식은 이러하다 var service = ServiceLoader.load(Service::class.java) ::class.java 는 코틀린에서 java.lang.Class 참조를 사용하는 방법이다 근데 이걸 var service = loadService() 이렇게 줄여서도 쓸 수 있다 이렇게 말고도 클래스를 타입 인자로 지정하면 ::class.java 라고쓰는거보다 간단하게 아래와 같이 사용할 수 있다

inline fun<reified T> loadService(){
 return ServiceLoader.load(T::class.java)
}

실체화한 타입 파라미터를 사용할 수 있는 경우는 이러하다

  • 타입 검사와 캐스팅(is, !is, as, as?)

  • 코틀린 리플렉션(::class)

  • 코클린 타입에 대응하는 java.lang.Class (::class.java)

  • 다른 함수를 호출할 때 타입 인자로 사용

하지만 다음과 같은 일을 할 수 없다

  • 타입 파라미터 클래스의 인스턴스 생성하기

  • 타입 파라미터 클래스의 동반 객체 메소드 호출하기

  • 실체화한 타입 파라미터를 요구하는 함수를 호출하면서 실체화하지 않은 타입 파라미터로 받은 타입을 타입 인자로 넘기기

  • 클래스, 프로퍼티, 인라인 함수가 아닌 함수의 타입 파라미터를 reified로 지정하기

변성: 제네릭과 하위 타입

변성이라는 개념은 List 이나 List와 같이 기저 타입이 같고 (List), 타입인자가 다른 여러 타입이 서로 어떠한 관계가 있는지를 설명하는 개념이다 직접 제네릭 클래스나 함수를 정의하는 경우 변성을 이해하고 사용하는게 좋다고 하네요 변성의 이유는 인자를 함수에 넘길 수 있어서이다 List 을 파라미터로 받는 놈에다가 List 을 보냈을 떄 문제가 없이 안전하게 작동하는 것과 같다 근데 mutableListOf 같은걸로 해서 any로 선언해두고 String 을 집어넣으려고하면 컴파일러에서 일단은 막는걸로 보인다 리스트의 원소를 바꾸거나 넣거나 할때는 타입에 대해서 불일치가 생길 수 있기 때문에 Any 대신에 String 을 넘기는 것은 불가능하지만 그렇게 수정이나 추가가 없는 경우에는 그냥 상위 타입인 Any를 사용하는 것도 가능하다

여기서 타입과 클래스의 차이는 무엇인걸까? 제네릭이 아닌 경우에는 클래스 이름을 바로 타입으로 사용하는 것이 가능하다 var x: String 이렇게 사용하면 String 클래스의 인스턴스를 저장하는 변수를 저장한 것이다 근데 var x: String? 이렇게 클래스 이름을 널이 될 수 있는 타입에도 사용할 수 있다는 점을 기억하자 = 모든 코틀린 클래스가 적어도 둘 이상의 타입을 구성할 수 있다는 의미 제네릭에서 올바른 타입을 얻기 위해서는 제네릭 타입의 파입 파라미터를 구체적인 타입 인자로 바꿔주어야 한다 그렇게 다양한 클래스 타입을 만들다 보면 타입간의 관계를 고려해봐야 한다 타입 사이의 관계를 정의하기 위해서 하위 타입이라는 개념도 알아야하는데 개념은 이러하다 타입 A, B 가 있는데, A의 값에 필요한 모든 장소에 B을 넣을 수 있다면, 즉 A을 대신해서 B를 사용해도 문제가 없다면 그것을 보고 B가 A의 하위 타입이다 위 상황의 반대는 A같은 경우에는 상위 타입이다. 더 구체적인 예시로 들면서 Number로 짜여져있는 곳에다가 Number 대신 Int 을 넣어도 정상 동작을 하게 되는데 그래서 Number 가 상위타입, Int가 하위 타입 이렇게 생각하면 된다 이렇게 타입에 대한 다른 점을 확인 하는 이유는 컴파일러가 변수를 대입하거나 함수 인자를 전달하는 시점에 하위 타입 검사를 진행하기 때문이다 그래서 예를 들어서 함수의 파라미터로 Int 클래스를 받았는데, 내부에서 구현할 때 Number 변수에 넣어도 문제가 없다는 것이다 대신 String 같은 변수에 넣으면 문제가 생기지? 추가로 nullable 요것도 봐야 한다 코틀린에서 기본적으로 모든 타입은 non-nullable 이고, ? 을 붙힘을 통해서 nullable 하게 돌아간다 여기서도 하위 타입 개념이 들어가는데 non-nullable 이 상위타입, nullable이 하위타입이다 Int가 상위타입, Int? 이 하위 타입 이렇게 상위하위를 왜 따지냐 -> 제네릭에서는 아주 중요하다. 제네릭 타입을 인스턴스화할 때 타입 인자로 서로 다른 타입이 들어가면 인스턴스 타입 사이의 하위 타입 관계가 성립하지 않으면 그 제네릭 타입을 무공변이라고 한다

그럼 또 반대로 A가 B의 하위 타입이라면 List는 List의 하위 타입이게 되고 이러한 클래스나 인터페이스르 보고 공변적이라고 한다 그래서 공변적이라는 의미는 하위 타입 관계를 유지하는 것이다 예를 들어서 Animal 하위에 Cat 이 있다고 했을 때 Producer에 넣어서 사용한다고 가정하자 그럼 Producer은 Producer 의 하위 타입이게 된다 그리고 코틀린에서 제네릭 클래스가 타입 파라미터에 대해 공변적임을 표시하기 위해서는 타입 파라미터 이름 앞에 out 을 넣어주어야 한다

Previous고차함수Next애노테이션과 리플렉션

Last updated 2 years ago

Was this helpful?