배열과 리스트
배열
val myArray1 = arrayOf(1,2,"메롱")
var myArray2 = arrayOf(1,2,"메롱")
myArray1[0] = 10
myArray2[0] = 10
println(myArray1[0])
println(myArray2[0])
배열은 위와같이 초기화를 해줄 수 있고, 인덱스로 접근해서 값도 변경이 가능하다.
값을 변경할때 val,var인지 상관이 없는것은 주소값을 참조하기 때문이다.
다만, 새로 초기화를 하는 경우에 val로 선언한 배열은 에러를 뱉어낸다.
리스트
List & MutableList
var myList1 = listOf<Int>(1,2,3)
val myList2 = listOf(1,2,"메롱")
myList1[0] = 10
myList2[0] = 10
리스트도 배열과 같이 위와 같이 선언과 동시에 초기화가 가능한데,
그냥 List는 var,val 상관없이 인덱스로 접근해서 수정을 하려고하면 에러를 낸다.
왜냐하면 그냥 List는 수정이 불가능한 읽기전용 리스트이기 때문이다.
fun ArrayAndList()
{
var myList1 = arrayListOf<Int>(1,2,3)
val myList2 = arrayListOf(1,2,"메롱")
myList1[0] = 10
myList2[0] = 10
}
그래서 MutableList인 arrayList로 바꿔주면 인덱스로 접근해서 수정이 가능하다.
arrayList도 배열과 같이 var,val인지 상관없이 인덱스로 접근해서 값 변경이 가능하다.(주소를 참조하기 때문)
하지만 val로 설정한 arrayList는 다른 배열을 넣거나, 초기화가 불가능하다.
반복문
for문
val students = arrayListOf("철수","영희","훈이","맹구")
for( name : String in students)
{
println(name)
}
C#의 foreach와 비슷하게 사용이 가능하다.
자료형은 생략이 가능하다. (위의 코드에서 : String)
var sum : Int = 0
// 1부터 10까지 반복
for(i in 1..10) {
sum += i
}
// step- 스텝을 밟아라 -> 2씩 늘어남
for(i in 1..10 step 2) {
sum += i
}
//10부터 1까지
for(i in 10 downTo 1){
sum += i
}
//1부터 99 까지
for(i in 1 until 100){
sum += i
}
위처럼 in, step,downTo,until 을 이용해서 편하게 사용이 가능하다.
fun LoopWithIndex(){
val students = arrayListOf("철수","영희","훈이","맹구")
for((index,name) in students.withIndex())
{
println("${index+1}번째 학생의 이름 $name")
}
}
그리고 때로는 for문의 인덱스가 필요한 경우가 있는데, 위처럼 withIndex 함수를 사용하여 for문을 작성하면 편리하다.
While문
var index = 0
while(index < 10){
println("current index : $index")
index++
}
다른 언어의 While문과 똑같다.
안의 조건이 false가 될때까지 반복한다.
Nullable & NunNull
'?' 연산자
NPE (Null Pointer Exception)은 컴파일 시점이 아닌 런타임때 확인이 가능한데,
코틀린은 ' ? ' 연산자를 이용하여 컴파일시점에 에러를 잡아준다.
var name : String = "철수"
var nullName : String? = null
코틀린에서 일반적인 String 은 NunNull 타입이기 때문에 null을 넣을수가 없다. 넣으면 에러가난다.
하지만 뒤에 ? 만 붙여주면 Nullable 타입이 되어서 null이 가능하다.
var nameInUpperCase = name.uppercase()
//-> 널이면 null반환, 아니면 함수 실행(대문자 변환)
var nullNameInUpperCase = nullName?.uppercase()
name 변수는 NunNull (널이 불가한) 타입이기 때문에 그냥 함수를 실행하면 끝이지만,
Nullable타입인 nullName 변수는 함수를 실행할때도 확인이 가능하다.
그냥 변수 뒤에 ?.함수() 만 하면 끝이다.
' ?: ' 연산자
val lastName : String? = null
//lastname이 잇으면 출력, 없으면 ?: 뒤에있는거 써!
val fullName = name + lastName?: "NO LastName"
'?:' 연산자를 이용해서 변수가 null일 경우를 대비할수가 있다
lastname에 값이 있으면 그 값을 넣고, 아니라면 ?: 연산자 뒤에있는 값을 넣는다.
' !! ' 연산자
fun IgnoreNull(name : String?){
var newName : String = name
}
코드를 이런식으로 작성한다면 에러가 뜬다.
왜냐하면 매개변수인 name이 Nullable 타입이기 때문에 ,
Null을 가질수 없는 newName 변수는 매개변수인 name이 Null일수도 있기 때문에 에러가 나는것이다.
하지만 '!!' 연산자로 우리가 컴파일러에게 확신을 줄 수 있다.
fun IgnoreNull(name : String?){
var newName : String = name!!
}
"내가 name 이라는 변수는 Null이 아니라고 보증 할게!! " 라고 하며 느낌표 두개를 붙여주니 컴파일러가 안심을하며 에러를 없앴다.
이러고보니 궁금해진다..
안심을 시켜놓고 Null 변수를 던진다면?
안심시켜놓고 뻔뻔하게 null을 넣어보았다.
컴파일러는 속상하다..
?.let
fun Studylet(){
var name : String? = "철수"
name?.let {
println(it)
}
}
변수뒤에 ?.let{} 을 붙이면 , 변수가 널이 아니라면 let뒤에오는 람다식 안의 코드를 실행하며, 그 안으로 변수를 넣어준다.
' it ' 이라는 변수명으로도 사용이 가능하고, 원래 변수명으로도 사용이 가능하다.
클래스
기본적으로 코틀린에서의 Class는 아래와 같은 내용도 허용이 된다.
1. 여러 클래스를 한 파일안에 넣을 수 있음
2. 파일명과 클래스 이름이 똑같지 않아도 됨
클래스 기본형식
class TestClass{
var newName : String = "철수"
fun TestFun() {
println("테스트")
}
}
기본적인 프로퍼티와 메소드는 위와 같이 선언이 가능하다.
클래스 생성
//new 필요없음
val test = TestClass()
test.newName = "맹구"
test.TestFun()
그리고 생성할때 자바나 C#처럼 New 키워드를 붙여서 생성하지 않아도 된다.
생성자
class TestClass constructor(name : String){
var name : String = name
fun TestFun() {
println("테스트")
}
}
생성자는 constructor(매개 변수)를 통해 생성이 가능하다.
constructor는 생략이 가능하다.
저렇게 코드를 짜면 위와같은 안내가 뜨는데,
생성자의 매개변수와 클래스가 가진 변수의 이름이 똑같기때문에, 생성자에서도 바로 선언할 수 있다는 뜻이다.
class TestClass (val name : String){
fun TestFun() {
println("테스트")
}
}
그래서 매개변수에 val 나 var를 붙여서 넣어주면, 해당 클래스 소속 프로퍼티가 된다.
따로 클래스 안에 선언하지 않았더라도 이렇게 접근이 가능하다.
class TestClass (val name : String = "철수"){
fun TestFun() {
println("테스트")
}
}
그리고 이런식으로 디폴트 값을 정해줄 수 있는데,
이러면 매개변수가 없는 (name이 철수인) 생성자와, 매개변수 1개가 있는 생성자
이렇게 2개가 생성된다.
주 생성자 , 부 생성자
class TestClass (val name : String = "철수"){
//생성할때 뭘 하고싶은지 정할수있음(주 생성자의 일부)
init {
println("초기화")
}
}
클래스 안에 init 을 넣어주면 생성할때 초기화를 담당하는 코드를 작성할 수 있다.
init은 주 생성자의 일부이다.
class TestClass (val name : String = "철수"){
//부 생성자는 주 생성자의 위임을 받아야함
constructor(name : String, age : Int) : this(name){
}
}
이런식으로 constructor 키워드를 통해 부생성자를 만들어줄 수 있는데,
: this() 로 주 생성자의 위임을 "꼭" 받아야한다.
안받으면 에러가 난다.
부 생성자를 만들었다면, 이런식으로 입력이 가능하다.
말그대로 '부' 이기 때문에, 쓰지 않아도 상관은 없다.
클래스의 상속
class TestClass (val name : String = "철수"){
//부 생성자는 주 생성자의 위임을 받아야함
constructor(name : String, age : Int) : this(name){
}
}
// 이러면 오류!
class SecondTestClass : TestClass()
{
}
상속은 클래스명 뒤에 : 상속받을 클래스명() 을 입력하면 된다.
하지만 기본적으로 class는 private이기 때문에, 부모 클래스의 앞에 'open'을 붙여주어야 한다.
에러가 없어졌다.
부모의 함수 오버라이딩
open class TestClass (val name : String = "철수"){
fun test(){
println("나는 부모")
}
}
class SecondTestClass : TestClass()
{
override fun test() {
println("나는 자식")
}
}
위와같은 코드를 작성하고 함수 오버라이딩을 위해 코드를 작성했더니 오류가 난다.
이유는 함수도 private이기 때문이다.
클래스 상속과 똑같이 오버라이딩할 부모의 함수 앞에 'open'을 입력해주면 에러가 없어진다.
fun main() {
//new 필요없음
val test = SecondTestClass()
test.test()
}
메인 함수에서 위 코드를 실행해보면 오버라이딩이 잘 되었다.
부모에 접근하는법 'super'
open class TestClass (val name : String = "철수"){
open fun test(){
println("나는 부모")
}
}
class SecondTestClass : TestClass()
{
override fun test() {
super.test()
println("나는 자식")
}
}
자식클래스에서 부모 클래스에 접근하려면 super 키워드를 쓰면된다.
오버라이딩한 자식의 함수를 실행해보니 부모의 함수까지 잘 나오는것을 볼 수 있다.
'프로그래밍 공부 > 코틀린' 카테고리의 다른 글
코틀린을 공부해보자 - (1) (0) | 2023.02.14 |
---|
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!