2023. 12. 6. 21:00ㆍTIL
코틀린 4주차강의 내용이다.
1.<접근제한자>
간단 용어 정리
프로젝트: 최상단 개념 모듈, 패키지, 클래스를 포함
모듈: 프로젝트 아래의 개념 패키지, 클래스를 포함
패키지: 클래스를 포함하는 폴더(우리가 가장 많이 쓰는 곳)
객체를 이용 > 변수나 메소드를 호출할 수 있는지의 여부를 <접근>이라고 함
명령어는 4개
public: 기본적인 형태, 생략도 가능
private: 가장 강한 접근제한, 같은 파일 내에서만 접근가능
internal: 같은 모듈내라면 접근가능
protected: 위 두개의 중간정도?의 접근제한 기본적으로 private과 같음 하지만 상속을 받을경우 접근 가능
설명을 위한 예시)
클래스 파일을 하나 만든다.
class AccessTestClass {
public var a:Int = 1
var b = 2
private var c = 3
internal var d = 4
protected var e = 5 <<여기서 값을 주었고,
public fun publicTest() { <<public: 어디서든 접근가능
println("public 입니다")
}
fun publicTest2() { <<public이 생략된 형태
println("public 입니다")
}
private fun privateTest() { <<이는 이 파일내에서만 접근가능
println("private 입니다")
}
internal fun internalTest() { <<같은 모듈이라면 가능
println("internal 입니다")
}
protected fun protectedTest() { <<상속받은 경우에만 가능
println("protected 입니다")
}
}
제일 특이케이스 protected
이를 상속받는 클래스코드를 짜준다.(위 클래스 코드에서 open 써주기 잊지말기)
class AccessTestChildClass: AccessTestClass() { <<상속받는 명령어
fun protectedTest1() {
println("e의 값은 ${e}")
}
}
이후 파일을 하나 더 만들어서
fun main() {
var accessTestClass = AccessTestClass()
var accessTestChildClass = AccessTestChildClass()
accessTestChildClass.protectedTest1() <<상속받은 클래스를 대신 적어준다.
}
2.<예외 처리>
코틀린은 실행하기전에 알수있는 에러를 컴파일에러라고한다.
실행도중 에러를 런타임에러라고 한다.
실행도중에 런타임에러(예외)가 발생하면 갑자기 종료된다.
이러한 경우를 예방하고자 예외처리 명령어가 있다.
예시1) try-catch 구조
while(true) { <<중요. 원래라면 catch에 있는 명령을 수행하고 끝나지만
try { while(true)명령어를 줘서 무한 반복된다.
var num1 = readLine()!!.toInt()
println("내가 입력한 숫자는 ${num1}입니다") <<예외 발생 가능성이 있는 코드
break <<여기서 명령어를 끊어준다.
} catch(e:java.lang.NumberFormatException) {
println("숫자를 입력하세요") <예외 발생시 대신 나갈 코드
}
}
즉 위 코드에선 숫자를 제외한 다른언어를 쓴다면 숫자를 입력하세요 문구가 뜨면서 반복된다.
예시2)throw 구조
이 명령어는 예외발생시 종료되는것만 방지하는 명령어이다.
fun method1(num1:Int) {
if(num1 > 10) {throw 예외종류}
}
예시3)try-catch-finally
while(true) {
try {
var num1 = readLine()!!.toInt()
println("내가 입력한 숫자는 ${num1}입니다")
break
} catch(e:java.lang.NumberFormatException) {
println("숫자를 입력하세요")
} finally { <<여기가 위 코드와 다른점인데, 예외가 발생하는것과 관계없이 지속적으로 출력된다.
println("키보드와의 연결은 정상적입니다") <<어떤걸 입력하든 이 문구는 지속출력된다.
}
}
3.<지연초기화>
초기화라고 하니까 조금 안와닿았는데 쉽게풀어서 값을 지정하는데, 그걸 지연시켜준다는 의미같다.
명령어로는 변수는 lateinit, 상수는 lazy로 지연초기화한다.
lateinit, isInitialized포함 예시)
fun main(){
var s1 = Student()
s1.age = 10 <<여기서 age에 값을주고
s1.displayInfo() <<아래문구를 불러오지만 name값이 없기에 false문구가 나온다.
s1.name = "참새" <<여기서 네임에 값을 주고,
s1.displayInfo() <<여기서 아래 문구를 다시 불러오면 정상적으로 출력이된다.
}
class Student {
lateinit var name:String <<원래는 var name:String = "" 이런식으로 썼지만 lateinit명령어를 넣음으로써 생략
var age:Int = 0
fun displayInfo() {
if(this::name.isInitialized) { <<isinitialized를 사용해서 값이 초기화 되었는지 확인하는 절차를 가짐
println("이름은: ${name} 입니다.")
println("나이는: ${age} 입니다.") <<값이 초기화 되었다면 이 문구들이 나오고
} else {
println("name변수를 초기화해주세요.") <<초기화되지 않았다면 이문구가 나온다.
}
}
}
lazy 예시)
fun main(){
var s1 = Student()
s1.name = "참새"
s1.displayInfo()
s1.age = 10
s1.displayInfo()
}
class Student {
lateinit var name:String
var age:Int = 0
val address: String by lazy { <<by lazy를 붙여 지연초기화를 하겠다 라는 의미
println("address 초기화") <<처음엔 이 문구가 나온다.
"seoul" <<이후 지연초기화 돼서 address="seoul"이란 의미가 된다.
}
fun displayInfo() {
println("이름은: ${name} 입니다.")
println("나이는: ${age} 입니다.")
println("주소는: ${address} 입니다.")
}
}
이해를 돕기위한 이 코드의 결과물
이름은: 참새 입니다.
나이는: 0 입니다.
address 초기화
주소는: seoul 입니다.
이름은: 참새 입니다.
나이는: 10 입니다.
주소는: seoul 입니다.
4.<널 세이프티>
Null이란 뭔가의 누락을 의미하는 듯 싶다.
이는 프로그램의 가용성을 많이 저하시킨다.
따라서 ? , !! , ?. , ?: 이 4개명령어로 Null예외로부터 대처한다.
이중 !!는 Null이 아니다라는 강조의미기 때문에 Null인 데이터에 !!를 붙여버리면 큰오류가 나는듯 싶다.
때문에 사용에 주의할 것.
이제 예시를 보자
? 예시)
fun main(){
var s = Student()
s.name = "참새"
s.address = "서울"
s.displayInfo()
}
class Student {
lateinit var name:String
var address:String? = null <<여기서 String? 이건 address 부분에 널이 들어갈수도 있다 라는 의미
fun displayInfo() {
println("이름은: ${name} 입니다")
println("주소는: ${address} 입니다")
}
}
!!예시)
fun main(){
// var data = readLine()!!.toInt()
var inputData = readLine()!! <<readLine은 코드 실행 후 직접입력한다는 의미 즉 Null이 들어가지 않는다.
var data = inputData.toInt()
println("Null아닌 값: ${data}")
}
?. (안전호출연산자)예시)
fun main(){
var s = Student()
s.name = "참새"
s.displayAddressLength()
s.address = "서울"
s.displayInfo()
}
class Student {
lateinit var name:String
var address:String? = null
fun displayInfo() {
println("이름은: ${name} 입니다")
println("주소는: ${address} 입니다")
}
fun displayAddressLength() {
println("주소의 길이는: ${address?.length} 입니다") <<여기서 ?를 뺀 address.length를 쓰면 오류가 나서 실행이 안된다.
}
}
?: (엘비스 연산자)예시)
fun main(){
var s = Student()
s.name = "참새"
s.displayAddressLength()
s.address = "서울"
s.displayInfo()
}
class Student {
lateinit var name:String
var address:String? = null
fun displayInfo() {
println("이름은: ${name} 입니다")
println("주소는: ${address} 입니다")
}
fun displayAddressLength() {
println("주소의 길이는: ${address?.length ?: "초기화하세요"} 입니다") <<만약 Null이 뜨면 초기화하세요 문구가 대신 나온다.
}
}
주소의 길이는: 초기화하세요 입니다
이름은: 참새 입니다
주소는: 서울 입니다
5.<배열>
배열은 예시를 먼저 적음
// arrayOf메소드를 호출하면 배열을 리턴해줍니다
// 1,2,3,4,5 각각을 저장한 변수 5개를 배열형태로 arr에 저장합니다
var arr = arrayOf(1,2,3,4,5) <<arr은 변수이름, arrayOf를 써서 배열을 짜줌
// 배열요소를 모두 출력합니다
println(Arrays.toString(arr))
// 배열의 첫번째 요소에 저장된 값을 출력합니다
// var num1 = 1의 num1과 arr[0]은 동일합니다
// arr[0]은 하나의 변수로 취급할 수 있습니다
// arr은 0~4번방(인덱스)까지 접근할 수 있습니다 <<배열은 첫번째가 0번째이다.
println(arr[0])
만약 배열을 쓰지못한다면 위에 1,2,3,4,5는 따로따로 변수이름, 값을 지정해줘야한다.
이로써 좀더 효율적으로 코드를 작성할수있다.
예시하나더)
배열을 불러오고 이름과 값을 이어주는 명령어
fun main() {
var kors = arrayOf(90, 94, 96)
for((idx, kor) in kors.withIndex()) { <<for은 반복명령어 ,idx는 순서, kor은 배열되어있는 값(90, 94, 96)들로 지정해준다는 의미
println("${idx}번째 국어 점수는 ${kor}입니다") <<바로 윗줄이 없으면 제대로 나오지 않는다.
}
}
6.<컬렉션>
코틀린의 컬렉션 사용법 추가지식
array와 대부분 비슷하지만 수정이 용이하다는 특징이 있는듯하다.
첫번째 List
// 읽기전용 리스트입니다
// 0번, 1번, 2번 인덱스에 접근해서 값을 변경할 수 없습니다
var scores1 = listOf(값1, 값2, 값3) <<읽기전용이라 수정이 불가능한 listOf
// 수정가능 리스트입니다
// 0번, 1번, 2번 인덱스에 접근해서 값을 변경할 수 있습니다
var scores2 = mutableListOf(값1, 값2, 값3) <<특정명령어를 써서 수정이 가능함
scores2.set(인덱스, 값) <<리스트이름.set(수정할 순서, 수정할 값) 이런식
// 수정가능 리스트입니다
// 0번, 1번, 2번 인덱스에 접근해서 값을 변경할 수 있습니다
// array로 데이터들을 저장하는 ArrayList도 mutableListOf와 동일하게 사용할 수 있어요
// 저장할 데이터의 자료형을 < > 안에 지정해야 사용할 수 있어요
var scores3 = ArrayList<자료형>(값1, 값2, 값3) <<위와 비슷하지만 자료형을 지정해줘야하나봄(아직 100프로 이해는 안된 상태)
scores3.set(인덱스, 값)
두번째 Map
// 읽기전용 맵입니다
// 변수명[키]로 데이터에 접근할 수 있습니다
var scoreInfo1 = mapOf("kor" to 94, "math" to 90, "eng" to 92) <<각각의 값에 짝을 지어주는 형식
println(scoreInfo1["kor"]) <<kor에 대한 값을 불러오는 형식이라 결과는 90
// 수정가능 맵입니다
// 변수명[키]로 데이터에 접근할 수 있습니다
var scoreInfo2 = mutableMapOf("kor" to 94, "math" to 90) <<위와 같지만 수정이 가능함
scoreInfo2["eng"] = 92 <<윗줄에 "eng" to 92라는 값을 추가한것과 같음
println(scoreInfo2["eng"]) <<eng에 대한 값을 불러오는 형식이라 결과는 92
// 맵의 키와 값을 동시에 추출해서 사용할 수 있습니다
for((k,v) in scoreInfo2) { <<반복문이고 앞의 (k,v)는 idx와 달리 특정 이름이 있는건 아님
println("${k}의 값은 ${v}입니다") <<k는 값의 짝, v는 값을 불러옴
}
위 부분의 결과를 보다면 이런식
94의 값은 kor입니다
90의 값은 math입니다
98의 값은 eng입니다
세번째 Set
Set은 특이하게 순서가 정해져 있지 않음
// 읽기전용 Set입니다.
var birdSet = setOf("닭", "참새", "비둘기")
// 수정가능 Set입니다.
// var mutableBirdSet = mutableSetOf("닭", "참새", "비둘기") <<다른 명령어와 비슷하게 앞에 mutable을 붙임
// mutableBirdSet.add("꿩") <<순서가 없으니 add를 사용해서 쉽게 추가
// mutableBirdSet.remove("꿩") <<remove를 사용해 없앰
println("집합의 크기는 ${birdSet.size} 입니다") <<Set의 갯수를 세어줌
var findBird = readLine()!!
if(birdSet.contains(findBird)) {
println("${findBird} 종류는 존재합니다.")
} else {
println("${findBird}는 존재하지 않습니다.")
}
7. <Single-expression function>
하나의 메소드를 간결하게 표현하는 방법이다.
나중에 배울 내용인지 4주차강의에서는 간결하게 설명하고 넘어갔다
예시)
세개의 숫자 평균을 리턴해주는 함수
var add = {num1: Int, num2: Int, num3: Int -> (num1+num2+num3) / 3}
println("평균값은 ${add(10,20,30)}입니다")
뭐 return, answer 등등 말그대로 좀 늘어질수있는 코드를 간단명료하게 표현한 것.
최적화할때 좋을것 같다.(하지만 아직 최적화를 시도하기에 이른듯)
8.<싱글턴>
이도 4주차에서는 아예 동영상조차 없다. 일단은 넘어가되
5주차 영상을 보고난뒤 만약 이해된다면 다시 정리하러 와야겠다.
예시)
fun main() {
Bird.fly("참새")
}
object Bird {
fun fly(name:String) {
println("${name}가 날아요~")
}
}
예시2)
fun main() {
// trash와 같이 생성자에 매개변수 전달 가능
var singletonObject1 = MySingletonClass.getInstance(trash = 1)
singletonObject1.setNum(5)
println("num값은: ${singletonObject1.getNum()}")
// singletonObject2에서 num을 10으로 대입
var singletonObject2 = MySingletonClass.getInstance(trash = 1)
singletonObject2.setNum(10)
// singletonObject1의 num이 10으로 출력됨
// singletonObject1과 singletonObject2는 같은 객체를 공유하기 때문
println("num값은: ${singletonObject1.getNum()}")
}
class MySingletonClass private constructor() {
private var num:Int = 0
companion object {
@Volatile private var instance: MySingletonClass? = null
private var trash = 0
fun getInstance(trash: Int): MySingletonClass {
this.trash = trash
// 외부에서 요청왔을때 instance가 null인지 검증
if(instance == null) {
// synchronized로 외부 쓰레드의 접근을 막음
// 쓰레드는 다음챕터에서 소개합니다!
// 쓰레드간의 객체상태 혼돈을 막기위해 사용한다고 이해해주세요
synchronized(this) {
instance = MySingletonClass()
}
}
return instance!!
// 엘비스연산자와 뒷장에서배울 scope function을 이용하면
// 아래와같이 더욱 직관적인 코드 작성이 가능합니다
// return instance ?: synchronized(this) {
// // also는 호출한 객체를 it으로 넘김
// // instance가 null이라면 새로 생성하고 아니면 무시함
// instance ?: MySigletonClass().also {
// instance = it
// }
// }
}
}
fun setNum(num: Int) {
this.num = num
}
fun getNum(): Int{
return this.num
}
}
오후 7시) 박찬규 튜터님에게 알고리즘 줌 특강을 들었다.
주된 내용은 알고리즘을 풀어나가는 방법
튜터님의 말로는 한줄한줄 단계별로 풀어나가는게 좋다 하셨다.
그리고선 추가로 알고리즘 학습하기 좋은 사이트를 하나 알려주셨다.
오늘의 한마디 : 음 딱히 적을 말은 없다. 일단 해보자!
'TIL' 카테고리의 다른 글
20231208 (금) 금주 회고 (0) | 2023.12.08 |
---|---|
20231207 (목) Kotlin 5주차 강의 (2) | 2023.12.07 |
20231205 (화) Kotlin 3주차강의 (1) | 2023.12.05 |
20231204 (월) Kotlin 1~2주차 강의 (0) | 2023.12.04 |
20231201 (금) mini project 지코 팀원소개 웹페이지 회고록 (0) | 2023.12.01 |