자바와 크게 다른점
1. 세미콜론(;)을 쓰지 않는다.
2. 변수 선언 및 함수의 머릿줄이 다르다.
-> int a = 10; == var a : Int = 10
-> void swap() || int add()가 아닌, fun swap() : Unit {} || fun add() : Int {}
3. 타입 추론이 가능하다. var a = 10 가능
4. 조건식에 if뿐만이 아닌, when이 추가되었다는 점
5. Nullable, 자바의 NPE가 개선이 되었다는 점
..
나머지는 코드를 보며 알아보도록 하자
Hello World
fun helloworld() : Unit {
println("Hello World")
}
이렇게 fun을 쓰고 함수 명()를 함.
콜론을 붙이고 Unit을 쓰면, 리턴 값이 없다는 의미가 됨.
즉, 이 함수는 void helloworld()와 같다.
마찬가지로 : Unit은 생략 가능
자바처럼 System.out.println()을 안 해도 됨
함수선언 함수명 (변수명 : 변수타입, 변수명 : 변수타입) : 반환타입{
return a+b
}
fun add(a : Int, b : Int) : Int{
return a + b
}
자료형의 앞 글자는 대문자여야 함
변수의 선언 ( val vs var )
val = value ( 변할 수 없는 수 = const = 상수)
var = variable (변할 수 있는 수 = 변수)
fun hi(){
val a : Int = 10
var b : Int = 10
a = 100 // 에러 상수를 바꾸는 건 ㅂㄱㄴ
b = 100
var c = 1000 // 이렇게 : Int를 안 써도 알아서 int로 인식함
var d = 1000 // 마찬가지.
var e // 이건 에러임. 자료형이 타입 추론에 의해 정해지지 않았음
var e = 100 //을 하던,
var e : Int //를 하던 변수의 자료형을 정해줘야 함.
}
파이썬은 동적 타이핑 언어이다. 코틀린은 정적 타이핑 언어이고, 근데 코틀린은 var a = 100;이 가능하다.
이러한 이유는 ‘타입 추론’ 기능을 포함하기 때문인데, 이는 컴파일 시점에 이루어진다.
R_value의 타입을 보고 변수의 자료형을 추론한다는 의미이다.
파이썬은 변수의 타입이 프로그램이 실행되는 동안에도 변화될 수 있으나,
코틀린은 컴파일 때 타입 추론에 의해 정해진 자료형이 실행 도중에는 바뀌지 않는다.
문자열 (String Template)
fun main(args: Array<String>){
val name = "Kyxxn"
val lastName = "Park"
println("내 이름은 ${name + lastName}입니다.") => 내 이름은 KyxxnPark입니다.
// 변수명을 문장 내에 넣고 싶으면 ${} 달러 사용
// 지금 KyxxnPark가 붙어있는데, Kyxxn Park 할거면
println("내 이름은 ${name + " " + lastName}입니다.") // ${}안에 문장 드가도 됨
}
조건식 (if와 when)
fun maxBy(a : Int, b : Int) : Int{
if(a>b) {
return a;
}else{
return b;
}
}
fun maxBy2(a : Int, b : Int) = if(a>b) a else b
// 위 두 함수는 같음
// 함수의 본문이 단일 표현식인 경우, 중괄호와 return 없이 '='을 사용하여
// 바로 작성이 가능함. 이를 "표현식 본문"이라고 함
// a와 b가 Int니까 알아서 반환 타입도 Int로 타입추론 됨
// if가 코틀린에선 표현식이므로, 값을 가질 수 있음. a와 b 중에 값을 가짐.
fun main(args: Array<String>){
checkNum(3)
}
fun checkNum(score : Int){
when(score){
0 -> println("0 입니다") // score 변수가 0 이면 0입니다
1 -> println("1 입니다") // 1이면 1입니다
2, 3 -> println("2 or 3 입니다") // 2나 3이면 2 or 3입니다
else -> println("0 1 2 3이 아닙니다") // 다른 수면 이렇게.
}
var b = when(score){
3 -> 10 // score가 3이면 b = 10
else -> 1 // 필수적인 문장. 3이 아니면 1로
}
println("b : ${b}") => 10 나옴.
when(score){
in 1..2-> println("1, 2등급이면 공부 잘하네") // 1 <= .. <= 2
in 3..4 -> println("3, 4등급은 놀진 않았네") // 3 <= .. <= 4
else -> println("공부 안 했네")
}
}
Expression (표현식) vs Statement (문장)
Expression : 값을 만들어 낸다
Statement : 값을 만들어내지 않음. 동작만 하는 느낌?
코틀린의 모든 함수는 Expression이다.
fun a() {
~~
}
이것 마저도 : Unit을 리턴하므로, Expression임.
Unit은 코틀린에서 반환 타입이 없음을 나타내는 타입인데, 이는 객체이며 표현식 ㄱㄴ.
자바, C, C++처럼 void는 Statement임. 그래서 값을 갖지 않음.
아래 두 문장은 Expression 임.
fun maxBy2(a : Int, b : Int) = if(a>b) a else b
var b = when(score) 처럼 변수에 선언을 해줄 때는 항상 else문을 적어야 함.
나머지는 else 안 적어도 됨
Array와 List (Collection)
Array : 고정된 사이즈 (클래스 임. 정적)
List : 읽기 전용, 수정 불가능한 리스트 (인터페이스, get은 있으나 set은 없음)
MutableList : 수정 가능한 리스트, 대표적으로 ArrayList가 있음 (인터페이스)
→ ArrayList : MutableList 인터페이스를 정의하여 만든 클래스
fun array() {
val array = arrayOf(1, 2, 3) // 정수형 배열
val list = listOf(1, 2, 3) // 정수형 리스트
array[0] = 1 // ㅆㄱㄴ
list[0] = 10 // 불가능. list는 수정 불가능
var result = list.get(0) // list 0번쨰 인덱스가 변수에 저장됨
val array2 = arrayOf(1, 2.0, "삼") // Any 타입 배열
val list2 = listOf(1, 2.0, "삼") // Any 타입 리스트
array2[0] = "일" // 0번째 인덱스는 정수지만, Any 타입이므로 문자도 ㄱㄴ
var arraylist = arrayListOf<Int>()
// ArrayList는 MutableList 인터페이스를 구현하는 클래스
// arrayListOf 함수는 ArrayList의 객체를 생성하고 초기화 하는 문장
arraylist.add(10)
arraylist.add(20)
val arraylist2 = arrayListOf<Int>()
arraylist2.add(100) // val이어도 요소 추가는 가능.
// 동일한 객체(100)을 참조함.
arraylist2 = arrayListOf() // 이건 오류
}
반복문 (For / While)
step, downTo, until
fun main(args: Array<String>){
// for문
val actor = arrayListOf("A", "B", "C", "D")
for(i in actor){ // 변수명 in List or Array
print("${i} ")
}
var sum = 0;
for(i in 1..10){ // 1<= .. <= 10
sum += i
}
println(sum)
for(i in 1..10 step 2){ // 1<= .. <= 10, 2칸씩
sum += i
}
println(sum) // 1 3 5 7 9 => 25
for(i in 10 downTo 1){ // 10 >= downTo >= 1
sum += i
}
println(sum) // 10 9 8 7 6 ... => 55
// 1부터 99까지
for(i in 1 until 10){ // 1 <= until < 100
sum += i
}
println(sum) // 1 2 3 .. 9 => 46
//While
var index = 0
while(index < 10){
print("${index} ")
index++;
}
val student = arrayListOf("박효준", "강민기", "이상진")
for((i, name) in student.withIndex()){
println("${i+1}번째 학생의 이름 : ${name}")
}
}
Nullable / NonNull (엘비스 연산자)
NPE : Null Pointer Exception
⇒ 자바에서 런타임 에러로 잡아주는 예외인데, 돌려봐야 이 코드가 오류가 있는 지 암.
코틀린에선 컴파일 때 오류를 잡아줌
fun main(args: Array<String>){
var name : String = "Kyxxn"
var nullName : String = null // 에러
var nullName2 : String ? = null
// 자료형을 지정했을 경우, null을 쓰려면 ? 를 붙여야함
// NULL을 넣고 싶으면 ? 를 넣어줘야 함 + 타입 추론때매 자료형 생략 X
// ? 를 붙인 걸 Nullable 타입.
var nameCheck = name.toUpperCase() // 가능
var nullNameCheck = nullName2.toUpperCase() // 에러
var nullNameCheck2 = nullName2?.toUpperCase()
// nullName2가 null인지 아닌지 모르기에 nullName2?. ~~()를 한다.
// null이 아니라면 대문자로 바꾸고, 널이면 null을 넣어줌
// 즉, nullNameCheck2 : String ?이 된 거임.
// 엘비스 연산자 ?:
// ?는 null일 경우 null을 반환
// ?:는 null일 경우 디폴트 값을 반환
val lastName : String ? = null
val fullName = name + (lastName?: "No lastName")
println(fullName)
// lastName이 널이 아니면 그대로 출력
// 널이면 ?: 연산자 뒤에 문구가 출력됨
}
fun ignoreNull(str : String?){ // Nullable 타입으로 들어올 때,
// str에 절대 null이 안 들어온다면, 개발자가 컴파일에게 null이 아님을 명시 가능
val mNotNull : String = str // 에러 Nullable 타입이니 ?가 붙어야함
val mNotNUll : String = str!! // !!를 String? 객체에 붙이면 사용 가능, 잘 안씀
val Upper = mNotNUll.toUpperCase() // mNotNUll이 String이므로 이렇게 가능.
}
코틀린에서 모든 타입은 기본적으로 null 값을 가질 수 없다.
var nonNullableString: String = "Hello World" // 이 변수는 null을 가질 수 없습니다.
var nullableString: String? = "Hello World" // 이 변수는 null을 가질 수 있습니다.
let 함수
fun main(args: Array<String>) {
ignoreNulls("gywns626")
}
fun ignoreNulls(str : String?){
val email : String? = str
// let 함수는 객체가 null이 아닐 경우, it으로 객체 값을 대체할 수 있음
email?.let{ // null이 아닌 경우에만 람다 식 진행
println("my email is : ${it}")
}
}
만약 위 코드에서 email.let을 했다면, null이 email에 할당 됐을 때, NPE가 발생함
Nullable 타입이 아닌 Non-Nullable 타입에 null이 들어가서 let을 호출하려 했기 때문.
그래서 email?.let을 써야함
Class 클래스
코틀린의 클래스는 public 클래스가 파일 명과 같을 필요가 없음
+) 코틀린은 클래스 및 메소드는 모두 final 수식어를 갖고 있음. (그냥 상속이 안됨)
생성자에 대하여
class Human constructor(str : String){
val str = str
fun eating(){
println("가나다")
}
}
이렇게 해도 되지만,
class Human constructor(val str : String){} // 이렇게도 가능
class Human(val str : String){} // construct 생략도 가능
// construct 키워드 생략
// 디폴트 초기화해두기 = main에서 Human()도 되고 ("~~")도 됨
class Human(val str : String = "디폴트값 주기"){}
class Human (val str : String = "디폴트 값"){
init{
println("init 함수는 생성자가 동작할 때, 동작하는 함수")
}
}
// 오버로딩 하고 싶으면 부생성자 construct를 클래스 내부에 명시
class Human (val str : String = "디폴트 값"){
// 부 생성자는 주 생성자를 받아와야 함 : this(str)
constructor(str : String, age : Int) : this(str){
println("이건 부생성자, 오버로딩 발생될 때 호출됨")
}
init{ // 주 생성자의 일부
println("init 함수는 생성자가 동작할 때, 동작하는 함수")
}
}
상속
- 코틀린의 클래스는 모두 final 이므로, 부모 클래스 앞에 “open” 키워드를 붙인다.
- 코틀린의 클래스 내부의 메소드는 모두 final 이므로 오버라이딩 하려면 각각의 메소드 머릿말에 “open”을 붙인다.
- extends 키워드가 아닌 변수 선언하듯 콜론 클래스이름() 을 사용한다
open class Human (val str : String = "부모 생성자 변수"){
var abc = "부모 클래스 지역변수"
init{
println("부모 생성자")
}
open fun singAsong() {
println("hahaha")
}
}
// Human()로 부모 생성자 호출 함.
class derived(val str2 : String? = "자식 생성자 변수") : Human() {
init{
println("자식 생성자")
}
override fun singAsong(){
super.singAsong() // 부모 클래스의 singAsong 함수 호출
println("하하하")
println("부모 클래스 변수도 가져올 수 있다 : ${str}")
}
}
fun main(args: Array<String>) {
val obj = derived()
println("${obj.abc}") // 자식에서 부모 클래스 지역변수 호출 가능
println("${obj.str2}")
obj.singAsong()
}
'🎸 기타 > AOS, Kotlin' 카테고리의 다른 글
앱 개발 프로젝트에서 배운 점(4) : 프래그먼트 간의 데이터 전달 (1) | 2023.08.15 |
---|---|
앱 개발 프로젝트에서 배운 점(3) : TextView 배열의 원소 증감 처리 및 메인 액티비티로 넘겨주기 (0) | 2023.08.15 |
앱 개발 프로젝트에서 배운 점(2) : Spinner 아이템 글자크기 및 폰트, 정렬 다루는 방법 (0) | 2023.08.14 |
앱 개발 프로젝트에서 배운 점(1) : 레이아웃, 프래그먼트, 뷰 (0) | 2023.08.14 |
[코틀린] 코틀린의 '람다'(Lamda) 살펴보기 (0) | 2023.07.29 |