기본 변수 null 허용하지 않음. null pointer exception(NPE) 차단을 위함.
단, nullable 변수 선언해서 사용은 가능함.
fun main() {
var variable: Int = 1
var nullable: Int? = null // nullable. 주의해서 사용할 것
val contants: Int = 2
println(variable)
println(nullable)
println(contants)
}
정수 타입
독특하게도 8진수는 지원하지 않는다. 오로지 2진수, 10진수, 16진수.
fun main() {
var intValue:Int = 1234 // 4byte 10진수
var longValue: Long = 1234L // 8byte 10진수
var hexValue: Int = 0x1af // 16진수 접두사 0x
var binValue: Int = 0b11000101 // 2진수 접두사 0b
}
실수 타입. 유효 자리 수에 유의
fun main() {
var floatValue: Float = 12.4f // 4byte. 유효 7자리 수
var doubleValue: Double = 12.4 // 8byte. 유효 16자리 수
var doubleWithExp: Double = 12.4e5 // 8byte. 유효 16자리 수
}
문자형, 불린형
fun main() {
var charValue: Char = 'A' // 홑따옴표로 감쌀 것
var stringValue: String = "abcdefg" // 문자열은 겹따옴표
var trueValue: Boolean = true
}
명시적 형변환만 가능하며 암시적 형변환 불가능
fun main() {
var a:Int = 1263
var b:Long = a.toLong() // 이렇듯 명시적 형변환만 가능하며 암시적 형변환은 지원하지 않음.
println(b)
}
배열
fun main() {
var intArr = arrayOf(1, 2, 3)
var nullArr = arrayOfNulls<Int>(3) // 크기 3인 null로 채워진 배열
println(intArr[0]) // 1
intArr[2] = 1000
println(intArr[2]) // 1000
}
type infer
typescript에서의 타입 추론이 여기에서도 된다. 선언시에 초기화만 한다면 자료형이 추론된다.
fun main() {
var a = 123 // Int
var b = 12L // Long
var c = 1.2 // double
var d = 1.3f // float
var e = '3' // char
var f = "wowowo" // String
var g = arrayOf(1, 2) // Array<Int>, ArrayInt
}
함수.
독특하게 js의 arrow function과 비슷한 single expression function 존재
fun main() {
println(add(1, 2))
println(singleExpAdd(1, 2))
}
fun add(a: Int, b: Int):Int {
return a + b
}
// single expression function
// return값 type infer됨
fun singleExpAdd(a:Int, b:Int) = a + b
자료형 체크를 위한 is와 !is가 존재한다.
fun main() {
var a = 3
if (a is Int) {
println("a is int type")
} else {
println("a is not int type")
}
}
switch가 없고 when문이 있다.
참고로 when에서 만족하는 첫번째 조건에서 return된다.
fun main() {
doWhen(2) // 이거 정수형이야
doWhen(1.2) // 어디에도 걸리지 않았습니다
}
fun doWhen(a: Any) {
when(a) {
1 -> println("이거 1이야")
"Darren" -> println("다롄")
is Int -> println("이거 정수형이야")
!is Double -> println("이거 더블형 아니야")
else -> println("어디에도 걸리지 않았습니다")
}
}
for문
특이한 표기법을 가지고 있다. .. 으로 범위를 표현하고 step으로 뗀다.
fun main() {
for (i in 0..5 step 2) {
print(i)
}
}
감소하는 케이스는 ..가 아니라 downTo를 붙여야 한다.
fun main() {
for (i in 9 downTo 2 step 2) {
print(i)
}
}
문자도 가능하다.
fun main() {
for (i in 'a'..'e') {
print(i)
}
}
Label 표현식
break, continue에서 Label 표현식을 통해서 특정 loop를 탈출할 수 있다.
아래 코드는 내부에서 break하더라도 myLoop로 지정한 외곽의 반복문까지 탈출하게 된다.
fun main() {
myLoop@for (i in 1..10) {
for (j in 1..10) {
if (i == 1 && j == 1) break@myLoop
print("$i, $j") // print 되면 앙돼요
}
}
}
class
기본 생성자만 활용한 class
fun main() {
// new 할당자가 없다! 그냥 쓰면 된다.
var darren = Person("darren", 27)
}
// method 없는 클래스는 아래와 같이 간단히 선언 가능, age에 기본값으로 100 할당.
class Person(var name: String, val age: Int = 100)
메서드 선언
// method를 선언하려면 아래와 같이 단순히 함수를 내부에서 선언하기만 하면 됨
class Person(var name: String, val age: Int) {
fun greeting() {
println("hello my name is $name and $age years old")
}
}
초기화 영역. 인스턴스가 생성될 때 트리거 된다.
class Person (var name:String, var age: Int) {
// 초기화 영역 -> init: 인스턴스가 생성될 때 호출되는 함수
init {
println("${this.name}, ${this.age}")
}
}
보조 생성자인 constructor를 통해서 생성할 수 있음. 사용시 무조건 this로 바인딩해줘야 함.
class Person (var name:String, var age: Int) {
// 초기화 영역은 보조 생성자인 constructor보다 먼저 실행됨
init {
println("${this.name}, ${this.age}")
}
// 보조 생성자. 사용할 때 반드시 기본 생성자를 통해 속성을 초기화해주어야 함.
// this(...) 꼴로 초기화할 수 있으며, 여기서 기본값을 할당할 수도 있음.
constructor(name: String): this(name, 33) {
println("보조 생성자 실행")
}
}
상속
코틀린은 상속 금지가 기본값이다. 상속하려면 open을 앞에 붙여줘야만 해당 클래스를 부모 클래스로 사용할 수 있다.
fun main() {
var happy = Dog("happy", 6)
happy.bark()
happy.introduce()
}
// open -> 이 클래스는 부모 클래스가 될 수 있다.
open class Animal(var name: String, var age: Int, var type: String) {
fun introduce() {
println("저는 ${type} ${name}이고 ${age}살 입니다")
}
}
// 부모 클래스에 존재하는 속성과 같은 이름의 속성을 가질 수 없음 -> 같은 속성 선언 금지. (var)
class Dog(name: String, age:Int): Animal(name, age, "dog") {
fun bark() {
println("bowwow")
}
}
overriding
부모 클래스와 같은 이름과 형태를 가진 함수를 서브 클래스에서 구현할 수 없다.
아래 예시를 보자.
fun main() {
var a = Warm()
a.eat() // Kotlin: 'eat' hides member of supertype 'Animal' and needs 'override' modifier
}
open class Animal {
fun eat() {
println("eat food, bro")
}
}
class Warm: Animal() {
fun eat() {
println("eat rotten food, bro")
}
}
아래와 같이 부모 메서드에는 open 키워드, 자식 메서드에는 override 키워드를 달아주면 오버라이딩이 가능해서 에러가 나지 않는다.
fun main() {
var a = Warm()
a.eat() // eat rotten food, bro
}
open class Animal {
open fun eat() {
println("eat food, bro")
}
}
class Warm: Animal() {
override fun eat() {
println("eat rotten food, bro")
}
}
추상화 1: abstract class
open대신 abstract 키워드를 써주면 된다.
abstract 메서드는 body가 없어야 한다.
fun main() {
var venom = Venom()
venom.eat()
}
abstract class Animal {
// A function 'eat' with body cannot be abstract 즉, fun에 body가 없어야 함
abstract fun eat()
}
class Venom: Animal() {
override fun eat() {
println("yummy")
}
}
추상화 2: 인터페이스
코틀린에서의 인터페이스는 타 언어와는 조금 다른 점이 있다. 코틀린의 인터페이스는 추상함수 뿐만 아니라 일반함수까지도 가질 수 있다
- 포함된 모든 함수를 자식 클래스에서 구현, 재정의가 가능하다.
- open, abstract 키워드가 별도로 필요 없다.
- 인터페이스에서 구현부가 있는 함수 -> open 함수
- 인터페이스에서 구현부가 없는 함수 -> abstract 함수
fun main() {
var dog = Dog()
dog.eat()
dog.run()
}
interface Runner {
// 구현부 없으므로 abstract 취급
fun run()
}
interface Eater {
// 구현부 있으므로 open 취급
fun eat() {
print("eat")
}
}
class Dog: Runner, Eater {
// 추상 함수의 구체화
override fun run() {
println("run")
}
// 일반 함수 override
override fun eat() {
println("eat eat")
}
}