본문 바로가기
Android/Kotlin App

[Kotlin] 안드로이드를 위한 Kotlin 문법

by 태옹 2021. 3. 22.

Kotlin 사이트(https://play.kotlinlang.org/)에 접속하여 별도의 설치 없이 개발할 수 있음!

 

Kotlin의 특징

Java와 100% 상호 호환되므로 Java 코드를 완전히 대체 가능

② Java보다 문법이 간결함

③ 프로그램의 안정성을 높여줌

var 또는 val 예약어를 통해 데이터 형식을 선언하지 않고 변수를 선언할수 있음

 

 

Kotlin의 변수 선언 방식

2) var(variable) - 변수

• 일반 변수를 선언할 때 사용

• 필요할 때마다 계속 다른 값을 대입 가능

3) val(value) - 상수

• 변수 선언과 동시에 값을 대입하거나, 초기화 없이 선언한 후에 한 번만 값을 대입 가능

• 한 번 값을 대입하고 나면 값을 변경할 수 없음

 

 

Kotlin의 변수 선언 방식

암시적 선언

• 변수의 데이터 형식을 지정하지 않고, 대입되는 값에 따라 자동으로 변수의 데이터 형식이 지정

– 단, 초기화하지 않는 경우에는 데이터 형식을 반드시 명시해야 함

 

 

Kotlin에서 많이 사용되는 기본적인 데이터 형식

- 다른 언어들과 다르게 코틀린에서char타입은 2바이트 (다른 언어는 1바이트)

 

 

데이터 형변환

  • .toInt()
  • .toDouble()
  • .to+데이터타입()

 

null사용

  • Kotlin은 기본적으로 변수에 null 값을 넣지 못함
    • 변수를 선언할 때 데이터 형식 뒤에 ?를 붙여야 null 대입 가능
  • 변수가 null 값이 아니라고 표시해야 하는 경우 !!로 나타냄
    • 컴퓨터에게 null이 아니라는 것을 알려줌
var notNull : Int = null	//오류
var okNull : Int? = null	//정상

okNull = 10
notNull = okNull	//오류
//okNull에는 현재 null이 아닌 값이 들어와 있지만 
//null이 들어올 수도 있는 변수이기 때문에 오류가 남

notNull = okNull!!	//정상
//null이 들어올 수 없도록 !!를 적어줌

오류가 안날 수도 있는데 가급적이면 문법 맞춰주기..

 

 

when문

  • 여러 가지 경우에 따라서 어떤 작업을 할 것인지를 결정
  • 다중 분기라고도 부름
  • switch문과 비슷 (break문이 default로 있음 - 굳이 break처리를 해주어야 할 필요가 없음)
  • 범위로 처리 : in 키워드 사용
fun main() {
    var count:Int = 85
    when(count){
        100 -> println("when문 : 합격 (장학생)")
        in 90..99 -> println("when문 : 합격(장학생)")
        in 60..89 -> println("when문 : 합격")
        50,40,30 -> println("콤마로 or연산자처럼 처리할 수도 있음")
        else -> println("when문 : 불합격")
    }
}

 

 

배열

  • 1차원배열은 두가지 방법으로 선언이 가능
    • var one1 = Array<Int>(4,{0})
          one1 = arrayOf(1,2,3,4)
    • var one2 = IntArray(4)
          one2 = intArrayOf(1,2,3,4)
fun main() {
    //1차원배열
    var one1 = Array<Int>(4,{0})
    one1 = arrayOf(1,2,3,4)
    //one[0] = 10
   	//one[1] = 20
    println(one1[0]+one1[1])	//결과 3
    
    var one2 = IntArray(4)
    one2 = intArrayOf(1,2,3,4)
    //one[0] = 10
   	//one[1] = 20
    println(one2[0]+one2[1])	//결과 3
    
    //2차원배열
    //var two = Array<IntArray>(3,{IntArray(4)})
    var two = Array<Array<Int>>(3, {Array<Int>(4,{0})})
    two[0][0] = 100
    two[2][3] = 200
    println(two[0][0]+two[2][3])    //결과 300

    //3차원배열
    var three = Array<Array<Array<Int>>>(3, {Array<Array<Int>>(3, {Array<Int>(3,{0})})})
	//Array<Array<Int>> : 2차원배열

    //2차원부터는 배열의 선언이 깔끔하지 않기 때문에 잘 사용하지 않음
    //1차원배열을 주로 사용함
}

 

 

ArrayList 

  • Array와 다른점 : Array는 초기에 설정된 크기 이상으로 값을 추가할 수 없지만 ArrayList는 요소를 추가할 수 있음
  • 가변크기
fun main() {
   var one = ArrayList<Int>(1)	//초기 선언에서 크기를 1만큼만 지정
   one.add(10)
   one.add(20)	//지정한 크기를 넘어감
   
   var hap = one.get(0) + one.get(1)
   println(hap) //정상 출력
}

 

for문

  • i(첨자)를 사용하여 배열의 값을 하나씩 처리하는 방법
/**
 * You can edit, run, and share this code. 
 * play.kotlinlang.org 
 */

fun main() {
 	var two : Array<String> = arrayOf("하나", "둘", "셋")
    
	for (i in 0..2 step 1) {	//1. 인덱스 0부터 2까지 1씩 증가
		print(two[i]+" ")
	} 
    	println() 	//한 줄 띄우기
    
	for (i in 0..2) {			//2. 1번과 같은 기능(step 1 생략가능)
		print(two[i]+" ")
	}
    	println() 	//한 줄 띄우기
     
	for (i in two.indices) {	//3. 배열의 크기만큼 : 배열명.indices
		print(two[i]+" ")
	} 
    	println() 	//한 줄 띄우기
     
	for (i in 2 downTo 0 step 1) {	//4. 거꾸로 출력 : downTo (<->'..')
		print(two[i]+" ")
	}   
}
/* 출력결과 : 
하나 둘 셋 
하나 둘 셋 
하나 둘 셋 
셋 둘 하나 */

 

  • i(첨자) 없이 바로 배열의 값을 하나씩 처리하는 방법
fun main() {
  var two:Array<String> = arrayOf("하나","둘","셋")
  
  for(a in two){	//배열의 내용이 하나씩 변수에 대입된 후 for 문 내부 실행
      println(a)
  }
  var k:Int = 0
  while(k<one.size){
      println(one[k])
      k++
  }
}
/* 출력결과 :
하나
둘
셋 
10
20
30
10
20
30
*/

 

메소드와 전역변수, 지역변수

var myVar:Int = 100	//전역변수
fun main() {
	var myVar:Int = 0	//지역변수
   	println(myVar)
    
    var sum:Int = addFunction(10,20)
    println(sum)
}

fun addFunction(num1:Int, num2:Int):Int{
    var hap:Int
    hap = num1+num2+myVar	//전역변수인 myVar의 값을 가져옴
    return hap
}
/* 출력결과 :
0
130
*/

 

연산자

  • 대부분의 언어들에서 볼 수 있는 연산자들과 기능이 유사한 것들이 많음
  • 코틀린에서의 ===연산자 : 동일한 객체인지 확인하게 해줌
    • a=10, b=10일 때 a==b(O), a===b(X)
    • 이런 경우, 표기법은 a==b, a!==b
fun main() {
	var a = Pair('a',65)
    var b = a
    var c = Pair('a',65)
    
    println(String.format("%s, %s",a == b, a == c))
    println("${a===b} ${a===c}")
    //${식} -> 식의 결과를 출력
}
/* 출력결과 :
true, true
true false
*/

 

클래스 정의와 인스턴스 생성, 생성자, 메소드 오버로딩

  • Car 클래스를 구현
    • 오버로딩 : 한 클래스 내에서 메소드의 이름이 같아도 파라미터의 개수나 데이터 형식만 다르면 여러 개를 선언할 수 있음
class Car{
        var color:String = ""
    var speed:Int = 0
    
    constructor(){}	//default생성자
    
    constructor(color:String, speed:Int){	//생성자 추가 (오버로딩)
        this.color = color
        this.speed = speed
    }
    
    fun upSpeed(value:Int){
        if(speed+value >= 200)
        	speed = 200
        else 
        	speed = speed + value
    }
    
    fun downSpeed(value:Int){
        if(speed-value <= 0)
        	speed = 0
        else
        	speed = speed - value
    }
}

 

  • 정의한 Car 클래스를 인스턴스로 생성
fun main(){
    var myCar1:Car = Car()
    
    myCar1.color = "red"
    myCar1.speed = 0
    myCar1.upSpeed(50)
    println(myCar1.speed)
    
    var myCar2:Car = Car("black",0)
    myCar2.upSpeed(250)
    println(myCar2.speed)
}

 

 

  • 정적 필드, 정적 메소드, 상수 필드 (Car클래스에 추가적으로 구현)
    • companion object { } 안에 작성하여 정적 필드, 정적 메소드를 구현할 수 있음

 

 companion object{	
        var carCount:Int = 0			//정적 필드
        const val MAXSPEED:Int = 200		//상수 필드
        const val MINSPEED:Int = 0
        fun currentCarCount():Int{		//정적 메소드
            return carCount
        }
}

 

  • 인스턴스를 생성하지 않고 클래스 자체에서 호출하여 사용 가능
println("생산된 차의 대수(정적 필드) ==> "+Car.carCount)
println("생산된 차의 대수(정적 메소드) ==> "+Car.currentCarCount())
println("차의 최고 제한 속도(상수 필드) ==>"+Car.MAXSPEED)

 

 

 

클래스 상속과 메소드 오버라이딩

  • 오버라이딩: 슈퍼클래스의 메소드를 무시하고 새로 정의하는 것을 의미함
  • 오버라이딩 할 때 자바와의 차이점 : 자바는 자식이 부모에게 오버라이딩을 허락받지 않고 자유롭게 정의할 수 있었지만 코틀린은 부모에 open키워드를 사용하여 오버라이딩을 허락해야함
open class Car{		//open키워드
	...
    open fun upSpeed(value:Int){	//open키워드
    	...
    }
}

class Automobile:Car {
    var seatNum:Int = 0
    
    constructor(){}
    
    fun countSeatNum():Int{
        return seatNum
    }
    
    override fun upSpeed(value:Int){	//override하는 경우 부모클래스의 앞에 open키워드 지정
        if(speed+value >= 300)
        	speed = 300
        else 
        	speed = speed + value
    }
}

 

  • 전체 코드
open class Car{		//부모클래스
    var color:String = ""
    var speed:Int = 0
    companion object{	
        var carCount:Int = 0			//정적 필드
        const val MAXSPEED:Int = 200	//상수 필드
        const val MINSPEED:Int = 0
        fun currentCarCount():Int{		//정적 메소드
            return carCount
        }
    }
    
    constructor(){}	//default생성자
    
    constructor(color:String, speed:Int){	//생성자 추가 (오버로딩)
        this.color = color
        this.speed = speed
        carCount++
    }
    
    open fun upSpeed(value:Int){
        if(speed+value >= 200)
        	speed = 200
        else 
        	speed = speed + value
    }
    
    fun downSpeed(value:Int){
        if(speed-value <= 0)
        	speed = 0
        else
        	speed = speed - value
    }  
}

class Automobile:Car {		//자식클래스
    var seatNum:Int = 0
    
    constructor(){}
    
    fun countSeatNum():Int{
        return seatNum
    }
    
    override fun upSpeed(value:Int){	//override하는 경우 부모클래스의 앞에 open키워드 지정
        if(speed+value >= 300)
        	speed = 300
        else 
        	speed = speed + value
    }
}

fun main(){
    var myCar1:Car = Car()
    
    myCar1.color = "red"
    myCar1.speed = 0
    myCar1.upSpeed(50)
    println(myCar1.speed)
    
    var myCar2:Car = Car("black",0)
    myCar2.upSpeed(250)
    println(myCar2.speed)
    
    println("생산된 차의 대수(정적 필드) ==> "+Car.carCount)
    println("생산된 차의 대수(정적 메소드) ==> "+Car.currentCarCount())
	println("차의 최고 제한 속도(상수 필드) ==>"+Car.MAXSPEED)
    
}

 

추상클래스와 추상메소드

  • 추상클래스 : 인스턴스화를 금지하는 클래스
  • 추상메소드 : 본체가 없는 메소드
  • 추상클래스와 추상메소드는 앞에 abstract 키워드를 붙여서 지정할 수 있음
  • 추상클래스/메소드를 사용하기 위해서는 자식 클래스에서 상속을 받아서 오버라이딩 해주어야함
  • 사용목적 : 공통적으로 사용되는 기능을 추상 메소드로 선언 해놓고, 추상 클래스를 상속받은 후에 추상 메소드를 오버라이딩해서 사용하기 위함
abstract class Animal{	//추상클래스 구현 : abstract
    val name:String=""
    abstract fun move()	//추상메소드 구현 : abstract + 함수내용 포함x
}

class Tiger:Animal(){	//추상클래스를 상속받음
    var age:Int=0
    override fun move(){	//추상메소드를 구현
        println("네 발로 이동")
    }
}

class Eagle:Animal(){
    var home:String=zip()
    override fun move(){
        println("날개로 날아감")
    }
}

fun main(){
    var tiger = Tiger()
    var eagle = Eagle()
    
    tiger.move()
    eagle.move()
}

 

 

다형성

  • 서브클래스에서 생성한 인스턴스를 자신의 클래스 변수에 대입할 수 있는 것을 의미함
  • 하나의 변수에 여러 종류의 인스턴스를 대입할 수 있음
fun main(){
    var animal:Animal	//부모클래스 변수 선언
    
    animal = Tiger()	//자식클래스 객체 생성 후 대입 가능 => 다형성
    animal.move()
    
    animal = Eagle()
    animal.move()
}

 

 

 

인터페이스

  • 추상클래스와 비슷함_그러나..
    • 추상클래스는 필드를 가질 수 있지만 인터페이스는 필드를 가지지 못함
    • 추상클래스는 추상메소드+일반메소드 모두 사용 가능하지만 인터페이스에는 추상메소드만 사용 가능
  • interface 키워드를 사용하여 정의하고 내부에는 추상 메소드를 선언함
  • 인터페이스는 ‘상속받는다’고 하지 않고 ‘구현한다’고 함
interface iAnimal{
    abstract fun eat()
}

class iCat:iAnimal{
    override fun eat(){
        println("생선을 먹음")
    }
}

 

  • 전체코드
abstract class Animal{	//추상클래스 구현 : abstract
    val name:String=""
    abstract fun move()	//추상메소드 구현 : abstract + 함수내용 포함x
}

interface iAnimal{
    abstract fun eat()
}

class iCat:iAnimal{
    override fun eat(){
        println("생선을 먹음")
    }
}
class Tiger:Animal(),iAnimal{	//추상클래스와 인터페이스를 상속받음
    var age:Int=0
    override fun move(){	//추상메소드를 구현
        println("네 발로 이동")
    }
    override fun eat(){
        println("뭐든 먹음")
    }
}

class Eagle:Animal(){
    var home:String=""
    override fun move(){
        println("날개로 날아감")
    }
}

fun main(){
    var animal:Animal
    
    animal = Tiger()
    animal.move()
    animal.eat()
    
    animal = Eagle()
    animal.move()
    
    var cat = iCat()
    cat.eat()
}

 

람다식(lambda expression)

  • 함수를 익명 함수(anonymous function) 형태로 간단히 표현 한 것
  • 코드가 간결해져서 가독성이 좋아짐
  • 람다는 { } 안에 매개변수와 메소드의 모든 내용을 선언
//일반적인 메소드형식
fun addNumber(n1:Int, n2:Int):Int{
    return n1+n2
}

//람다식
val = addNumber = {n1:Int, n2:Int -> n1+n2}

 

람다식의 특징

① 람다식은 { }로 감싸며 fun 예약어를 사용하지 않음

② { } 안 ->의 왼쪽은 파라미터, 오른쪽은 함수의 내용

③ -> 오른쪽 문장이 여러 개라면 세미콜론(;)으로 구분

④ 내용 중 마지막 문장은 반환값(return)임

 

 

* 문자열 비교시 equals()말고도 ==연산자 사용 가능! (코틀린 완전 편하구낭)

 

 

날짜 형식

  • DateFormat 클래스를 상속받은 SimpleDate Format 사용
  • ‘연월일’이나 ‘시분초’와 같은 일반적인 표현이 가능함
import java.text.DateFormat
import java.util.*
import java.text.SimpleDateFormat

fun main(){
	var now = Date()
	var sFormat:SimpleDateFormat
	sFormat = SimpleDateFormat("yyyy-MM-dd")
	println(sFormat.format(now))
	sFormat = SimpleDateFormat("HH:mm:ss")
	println(sFormat.format(now))
}

 

댓글