728x90

하나의 클래스를 다른 클래스에 위임하도록 선언하여 위임된 클래스가 가지는 인터페이스 메소드를 참조 없이 호출할 수 있도록 생성해주는 기능이다.

 

interface Fruit를 구현하고 있는 class Apple이 있다면, 
Fruit에서 정의하고 있는 Apple의 모든 메소드를 클래스 GreenApple로 위임할 수 있다. 
즉, GreenApple은 Apple이 가지는 모든 Fruit의 메소드를 가지며, 이를 클래스 위임(Class delegation)이라고 한다.

interface Fruit {
    val name: String
    val color: String
    fun bite()
}
 
class Apple: Fruit {
    override val name: String
        get() = "사과"
    override val color: String
        get() = "빨간색"
 
    override fun bite() {
        print("사과 아삭~ 아삭~")
    }
}
 
// 클래스 위임 : 사용할 기능은 그대로 사용하고 새로 정의할 것만 override로 재정의
class GreenApple(
    private val apple: Apple
) : Fruit by apple {
    override val color: String
        get() = "초록색"
}

 

fun main() {
    val greenApple = GreenApple(Apple())
    println(greenApple.color)
    println(greenApple.name)
    greenApple.bite()
}

 

 

'안드로이드 > Kotlin 문법' 카테고리의 다른 글

코틀린 제네릭 공변, 반공변  (0) 2024.02.04
[코틀린] ArrayList, mutableListOf  (0) 2021.06.14
[코틀린] Inner Class  (0) 2020.08.21
[코틀린] Nested Class (중첩 클래스)  (0) 2020.08.21
코틀린 클래스  (0) 2020.05.06
블로그 이미지

Link2Me

,
728x90

Kotlin에서 List, Map, Set같은 Collections 클래스를 사용할 때, List<String> 이런식으로 제네릭 타입을 지정할 수 있다.

제네릭을 사용하면 타입 안정성을 얻을 수 있다.
하지만 제네릭으로 지정한 타입 이외에도 좀 더 유연하게 사용하고 싶을 수 있다.
그래서 코틀린에서는 공변성과 반공변성을 허용한다.

 

package generic
 
abstract class Animal(val name: String)
 
abstract class Fish(name: String) : Animal(name)
 
// 금붕어
class GoldFish(name: String) : Fish(name)
 
// 잉어
class Carp(name: String) : Fish(name)

 

package generic
 
fun main(){
    val goldFishCage = Cage<GoldFish>()
    goldFishCage.put(GoldFish("금붕어")) // Type Casting없이 바로 금붕어를 가져올 수 있다.
 
    val cage = Cage<Fish>()
    cage.moveFrom(goldFishCage)
 
    val fish: Fish = cage.getFirst()
    // Cage<Fish>에서 데이터를 가져오면 GoldFish인지 Carp인지 모른다.
}
 
/***
 * 제네릭 클래스 : 타입 파라미터를 사용한 클래스
 * 코틀린에서는 Raw 타입 사용이 불가능하다.
 * Raw Type : 제네릭 클래스에서 타입 매개변수를 사용하지 않고 인스턴스화 하는 것
 * in-variant(무공변) : 타입 파라미터끼리는 상속관계이더라도, 제네릭 클래스 간에는 상속관계가 없다는 의미
 * co-variant(공변) : 타입 파라미터간의 상속관계가 제네릭 클래스에도 동일하게 유지된다는 의미
 * 코틀린에서는 타입 파리미터 앞에 out 변성 어노테이션을 사용한다.
 * contra_variant(반공변) : 타입 파라미터간의 상속관계가 제네릭 클래스에서는 반대로 유지된다는 의미
 * 코틀린에서는 타입 파리미터 앞에 in 변성 어노테이션을 사용한다.
 */
class Cage<T : Animal> {
    private val animals: MutableList<T> = mutableListOf()
 
    fun getFirst() : T {
        return animals.first();
    }
 
    fun getAll() : List<T> {
        return  this.animals
    }
 
    fun put(animal: T) {
        this.animals.add(animal)
    }
 
    fun moveFrom(otherCage: Cage<out T>) {
        // out을 붙이면 moveFrom 함수를 호출할 때 Cage는 공변하게 된다.
        // out을 붙이면, otherCage로부터 데이터를 꺼낼 수만 있다.
        this.animals.addAll(otherCage.animals)
    }
 
    fun moveTo(otherCage: Cage<in T>){
        // in을 붙인 otherCage는 데이터를 받을 수만 있다.
        otherCage.animals.addAll(this.animals)
    }
}

 

 

 

'안드로이드 > Kotlin 문법' 카테고리의 다른 글

코틀린 클래스 위임(Class Delegation)  (1) 2024.02.06
[코틀린] ArrayList, mutableListOf  (0) 2021.06.14
[코틀린] Inner Class  (0) 2020.08.21
[코틀린] Nested Class (중첩 클래스)  (0) 2020.08.21
코틀린 클래스  (0) 2020.05.06
블로그 이미지

Link2Me

,
728x90

List 인터페이스는 특정한 순서가 있는 선형 자료구조 를 구현할 때 필요한 함수들을 정의하고 있다. 
ArrayList는 선형 자료구조이고, index로 각 원소 접근이 가능하다.
언제든지 새로운 원소를 추가하고, 변경하고, 삭제할 수 있다.

fun main(agrgs: Array<String>){
 
    val lableList = mutableListOf<String>()
    lableList.add("1월")
    lableList.add("2월")
    lableList.add("3월")
    lableList.add("4월")
    lableList.add("5월")
 
    for (item in lableList) // element
        println(item)
 
    for(i in 0..lableList.size-1// index
        println(lableList[i])
 
}

 

Custom ArrayList 데이터를 추가하고 출력하는 예제

데이터 추가할 때 주의하지 않으면 원하지 않는 결과가 나온다.

data class ChartData(
    var lableData: String = "",
    var lineData: Double = 0.0
)
 
fun main(agrgs: Array<String>){
    val chartData = mutableListOf<ChartData>()
    chartData.clear()
 
    // 데이터 추가
    var item = ChartData()
    item.lableData = "1월"
    item.lineData = 7.9
    chartData.add(item)
 
    item = ChartData()
    item.lableData = "2월"
    item.lineData = 8.2
    chartData.add(item)
 
    item = ChartData()
    item.lableData = "3월"
    item.lineData = 8.2
    chartData.add(item)
 
    item = ChartData()
    item.lableData = "4월"
    item.lineData = 8.5
    chartData.add(item)
 
    item = ChartData()
    item.lableData = "5월"
    item.lineData = 7.3
    chartData.add(item)
 
    // 데이터 출력 (index)
    for (i in 0..chartData.size-1)
        println("${chartData[i].lableData} , ${chartData[i].lineData} ")
 
    // 데이터 출력(element)
    for (item in chartData)
        println("${item.lableData}, ${item.lineData}")
 
}
 

 

데이터 추가 함수화한 예제

 
val chartData = mutableListOf<ChartData>()
 
data class ChartData(
    var lableData: String = "",
    var lineData: Double = 0.0
)
 
private fun addChartItem(lableitem: String, dataitem: Double) {
    val item = ChartData()
    item.lableData = lableitem
    item.lineData = dataitem
    chartData.add(item)
}
 
fun main(agrgs: Array<String>){
 
    chartData.clear()
 
    // 데이터 추가
    chartData.clear()
    addChartItem("1월"7.9)
    addChartItem("2월"8.2)
    addChartItem("3월"8.3)
    addChartItem("4월"8.5)
    addChartItem("5월"7.3)
 
    // 데이터 출력 (index)
    for (i in 0..chartData.size-1)
        println("${chartData[i].lableData} , ${chartData[i].lineData} ")
 
    // 데이터 출력(element)
    for (item in chartData)
        println("${item.lableData.replace(("[^\\d.]").toRegex(), "")}, ${item.lineData}")
        // 숫자만 추출하는 정규표현식 적용 : str.replace(("[^\\d.]").toRegex(), "")
 
}

 

 

블로그 이미지

Link2Me

,
728x90

class OuterClass{
  //중첩클래스
  class NestedClass
}

중첩클래스의 class 키워드 앞에 inner 키워드를 붙이면 내부클래스가 된다.
class OuterClass{
  //내부클래스
  inner class InnerClass
}

이 두 클래스에 대해서 공통점과 차이점을 간단히 요약해보면 다음과 같다.
공통점 : 클래스 내부에 다른 클래스로 정의된다.
외형적 차이점 : inner 키워드를 쓰면 내부클래스, 안쓰면 중첩클래스이다.
기능적 차이점 : 중첩클래스는 OuterClass의 참조를 가지지 않지만 내부클래스는 OuterClass의 인스턴스를 참조를 가진다.
중첩클래스는 외형적으로는 OuterClass 소속인 것 같지만 사실은 연관성이 거의 없다.


class OuterClass {
    val outerValue = 10
    val a = "Outside Nested class."

    inner class Inner {
        private val innerValue = 20
        fun callMe() = a // OutClass 의 변수 할당 가능
        fun printItems() {
            println("inner: $innerValue, outer: $outerValue")
        }
    }

    fun printItems() {
        val inner = Inner() // 객체 생성
        inner.printItems()
    }
}

fun main(args: Array<String>) {
    val outer = OuterClass() // 객체 생성
    outer.printItems()

    println("Using outer object: ${outer.Inner().callMe()}")

    val inner = OuterClass().Inner()
    println("Using inner object: ${inner.callMe()}")
}
 


https://www.bsidesoft.com/8218 에 더 많은 내용이 있으니 참조하면 도움된다.

블로그 이미지

Link2Me

,
728x90

자바에서는 A 클래스 안에 B 클래스를 정의하면 B 클래스는 자동으로 내부 클래스가 되었다.
하지만 코틀린에서는 반대다.
한 클래스안에 다른 클래스를 정의하면 기본적으로는 중첩 클래스가 되고,
내부 클래스로 만들고 싶다면 inner 키워드로 클래스를 선언해야 한다.
내부 클래스는 기본적으로 외부 클래스를 참조하게 되지만 중첩 클래스는 그렇지 않다.


class Outer {
    val a = "Outside Nested class."

    class Nested {
        // Outer Class 변수에 접근할 수 없다.
        val b = "Inside Nested class."
        fun callMe() = "Function call from inside Nested class."
    }
}

fun main(args: Array<String>) {
    // accessing member of Nested class
    println(Outer.Nested().b) // 자바의 static 중첩 클래스와 동일하다.

    // creating object of Nested class
    val nested = Outer.Nested()
    println(nested.callMe())
}
 




'안드로이드 > Kotlin 문법' 카테고리의 다른 글

[코틀린] ArrayList, mutableListOf  (0) 2021.06.14
[코틀린] Inner Class  (0) 2020.08.21
코틀린 클래스  (0) 2020.05.06
코틀린 null 안전과 예외  (0) 2020.05.05
코틀린 object  (0) 2020.01.19
블로그 이미지

Link2Me

,
728x90

프로퍼티(Property)

Java에서는 데이터르 필드에 저장하며, 멤버 필드의 가시성은 보통 비공개(private)이다.

클래스는 자신을 사용하는 클라이언트가 그 데이터에 접근하는 통로로 쓸 수 있는 접근자 메소드를 제공한다.

보통은 필드를 읽기 위한 getter를 제공하고 필드를 변경 허용해야 할 경우 setter를 추가 제공할 수 있다.

Java에서는 필드와 접근자를 묶어 property 라구 부르며, 프로퍼티를 활용하는 프레임워크가 많다.

코틀린은 프로퍼티를 언어 기본 기능으로 제공한다.

class Person(val name: String, val age: Int)

코틀린에서는 Java 클래스의 getter를 val 프로퍼티처럼 사용할 수 있고, getter/setter 쌍이 있는 경우에는 var 프로퍼티처럼 사용할 수 있다.


코틀린에서는 하나의 파일에 하나 또는 여러개의 클래스를 정의할 수 있다.

하지만 유지보수의 용이성을 고려하여 가급적 하나의 클래스를 하나의 파일에 정의하는 것이 좋다.


가시성 제한자(visibility modifier)

 public

 함수나 속성이 클래스 외부에서 사용될 수 없다.

 코틀린의 기본 가시성은 public 이므로 가시성 제한자를 생략해도 된다.

 private

 함수나 속성이 정의된 클래스 내부에서만 사용될 수 있다.

 protected

 함수나 속성이 정의된 클래스 내부 또는 클래스의 서브 클래스에서만 사용될 수 있다.

 internal

 함수나 속성이 정의된 클래스가 포함된 모듈에서 사용될 수 있다.


코틀린에서는 자바에 없는 internal 가시성을 지원한다. 이것은 같은 모듈에 있는 클래스, 함수, 속성까리 상호 사용할 수 있다는 것을 뜻한다.


Java는 기본적으로 패키지 가시성을 지원한다. 즉, 가시성 제한자가 없는 메서드, 필드, 클래스는 같은 package에 있는 클래스에서만 사용 가능하다는 뜻이다.

코틀린에서는 package 가시성이 없다. 같은 package에 있는 클래스, 함수, 속성 등은 기본적으로 상호 사용할 수 있어서 굳이 별도의 가시성을 가질 필요가 없기 때문이다.


서로 다른 패키지에 있는 같은 이름의 클래스나 함수 등을 함께 사용할 경우에는 as 키워드로 별칭을 지정하여 이름 충돌이 생기지 않게 할 수 있다.


클래스 속성에서 var 는 변경 가능한 속성을, val 은 읽기 전용 속성을 지정하는 데 사용된다.


코틀린 생성자

코틀린은 자바, C++과는 달리 클래스 선언 부분에서 이름에 괄호를 붙여 생성자를 만들 수 있다.

이를 주 생성자(primary constructor)라고 한다.

코틀린 몸체에 보조 생성자가(secondary constructor)를 추가할 수 있다. 보조 생성자는 매개변수를 다르게 하여 여러 개를 정의할 수 있다.

    // (name: String, surname: String)  default constructor signature
    class Person(_name: String, surname: String) {
        // 맨 앞에 밑줄(_)이 있는 변수는 임시 변수를 나타낸다.
        // 매개변수를 포함해서 임시 변수는 한번 이상 참조될 필요가 없는 변수이며,
        // 1회용이라는 것을 나타내기 위해 이름 앞에 밑줄을 붙인다.

        val name: String

        // init block , represents the body of default constructor
        init { //주생성자와 함께 초기화 블럭
            // init 초기화 블록은 여러개 가능하다.
            name = _name
            Log.d("primary", name)
        }

        // secondary constructor
        // this(name,"") call to default constructor
        // constructor 로부터 생성된 생성자는 '기본 생성자'를 '상속' 받아야 한다.
        constructor(name : String):this(name,""){
            Log.d("secondary", "Hello")
        }
    }


class Person constructor(name: String) { /*...*/ }

primary constructor가 어노테이션이나 접근 제한자(public, private 등)을 갖고 있지 않다면 constructor 키워드를 생략할 수 있다.
class Person(name: String) { /*...*/ }

자바의 경우 생성자로부터 받아온 변수를 해당 클래스에서 사용할 때 생성자 내에서 [this.클래스변수 = 매개변수] 같은 형식으로 받아올 수 있다. 하지만 코틀린은 생성자 영역이 없어서 이렇게 할 수가 없다. 

코틀린에서는 기본과 보조 생성자에 추가하여 클래스 초기화 블럭도 정의할 수 있다.

초기화 블럭은 init 키워드로 정의한다. init 초기화 블록은 여러개 가능하다.

this.name = name

만일 속성을 초기화하는 코드가 복잡하다면 초기화 코드를 함수나 초기화 블럭에 넣은 것을 고려하자.


코틀린의 클래스는 secondary constructor도 가질 수 있다.
이때 사용되는 키워드는 constructor이고, primary constructor와 다르게 생략할 수 없다.
class Person {
    var children: MutableList<Person> = mutableListOf<Person>();
    constructor(parent: Person) {
        parent.children.add(this)
    }
}



블로그 이미지

Link2Me

,
728x90

null 은 var 나 val 변수의 값이 없다는 것을 나타내는 특별한 값이다. 자바를 포함해서 많은 프로그래밍 언어에서 null 은 흔히 크래시(crash)를 유발하는 원인이 된다.


코틀린은 컴파일 언어다. 즉 실행에 앞서 컴파일러라는 특별한 프로그램에 의해 기계어로 변환된다는 걸 뜻한다.

컴파일러는 null 가능 타입에 null 값이 지정되는지 검사한다.


런타임 에러는 컴파일러가 발견할 수 없어서 프로그램이 컴파일된 후 실행 중에 발생하는 오류다.

자바는 null 가능 타입과 불가능 타입을 구분하지 않으므로 자바 컴파일러는 null 값으로 인한 문제가 있는지 사전에 알려줄 수가 없다. 따라서 정상적으로 컴파일 된 코드가 런타임 시에 크래시될 수 있다.


null 가능 타입이 컴파일이나 런타임 에러 없이 항상 실행되도록 해야 할 경우가 있다.

이런 경우에 null 가능 타입을 안전하게 처리하기 위해 안전 호출 연산자(safe call operator)인 ?.을 사용한다.

non-null 단언 연산자(assertion operator)인 !!도 null 가능 타입에 대해 함수를 호출하기 위해 사용할 수 있다.

non-null 단언 연산자는 컴파일러가 null 발생을 미리 알 수 없는 상황이 생길 수 있을 때 사용된다.

nul 가능 변수에 !! 연산자를 사용하는 것은 위험하다.


예기치 않는 값으로 인해 우리가 의도치 않는 방향으로 프로그램이 실행될 수 있다. 따라서 코드 값을 작성할 때는 입력 값을 검사하는데 많은 노력을 기울여야 한다.


null 불가능 매개변수가 null 인자를 받지 않게 해주는 매커니즘에는 @NotNull 애노테이션이 있다.

@NotNull 애노테이션은 많은 자바 프로젝트 코드에서만 볼 수 있지만, 코틀린에서 자바 메서드를 호출할 때 특히 유용하다.


출처 : 빅너드랜치의 코틀린 프로그래밍에서 발췌 요약

http://www.kyobobook.co.kr/product/detailViewKor.laf?ejkGb=KOR&mallGb=KOR&barcode=9791188621538&orderClick=LAG&Kc=







블로그 이미지

Link2Me

,
728x90

객체 지향 언어에서는 객체를 생성하기 위해서는 먼저 클래스를 선언하고, 해당 클래스로부터 객체를 생성한다.

하지만, 코틀린에서는 이런 과정을 거치지 않고도 객체를 생성할 수 있다.

object 키워드를 사용하면, 클래스가 아니라 객체 그 자체, 인스턴스가 된다.


코틀린에는 static keyword가 없고 object 키워드를 통해
- 싱글턴을 정의하는 방법
- 동반객체 companion object를 이용한 팩토리 메서드 구현
- 무명클래스(익명 클래스)의 선언
를 한다.


예제1

fun main(){
    // Person은 그 자체로 하나의 객체이기 때문에, 바로 .을 찍어 프로퍼티나 메서드에 접근할 수 있다.
    Person.name = "홍길동"
    Person.age = 30
    println("이름 : ${Person.name}, 나이 : ${Person.age}")
}

// object 는 subclass 의 명시적인 선언 없이 객체 생성
object Person {
    var name: String = ""
    var age: Int = 0
}
 


예제2

- object 키워드는 어떤 객체가 반드시 하나만 존재해야 할 때, 즉 싱글톤 패턴에 활용할 수 있다.

// 예제 2.1

fun main(args: Array<String>) {
    Singleton.printVarName()

    println("\nSingleton variableName Change")
    Singleton.variableName = "강감찬"
    var a = A()
}

object Singleton{
    init {
        println("Singleton class invoked.")
    }
    var variableName = "홍길동"
    fun printVarName(){
        println("변수명 : $variableName ")
    }
}

class A {
    init {
        println("Class init method. Singleton variableName property : ${Singleton.variableName}")
        Singleton.printVarName()
    }
}

// 예제 2.2
fun main(args: Array<String>) {
    var a = A()
    a.printVarName()

    println()
    Singleton.printVarName()
}

open class A {
    open fun printVarName() {
        println("I am in class printVarName")
    }

    init {
        println("I am in init of A")
    }
}

object Singleton : A() {
    init {
        println("Singleton class invoked.")
    }

    var variableName = "홍길동"
    override fun printVarName() {
        println(variableName)
    }
}



예제3. companion object

- 동반 객체(companion object)는 최상위 수준에서는 사용할 수 없고 클래스 내부에 정의하여 사용한다.

  동반 객체는 클래스 내부에 정의된 객체 선언이라고 할 수 있다.

  하나의 클래스에는 하나의 동반 객체만 포함될 수 있다.

- 동반 객체를 포함하는 클래스 내부에서는 동반 객체의 속성이나 함수를 자신의 속성이나 함수인 것처럼 인식하므로 동반 객체의 이름을 지정하지 않고 사용할 수 있다.

- 카 판매 버튼을 클릭할 때마다 카운트가 증가되는 함수라고 생각하면 된다.

- companion object 가 서로 다른 객체(아우디, 그랜저)의 카운트 총합을 자동 합산한다.

fun main(args: Array<String>){
    var car1 = CarSales("아우디")
    var car2 = CarSales("그랜저")

    car1.sales()
    car1.sales()
    car1.sales()
    car1.sales()

    car2.sales()
    car2.sales()

    println("${car1.name} : ${car1.count}")
    println("${car2.name} : ${car2.count}")
    println("총계 : ${CarSales.total}")
 
}

// object 는 subclass 의 명시적인 선언 없이 객체 생성

class CarSales(val name: String){
    // 코틀린에는 정적(static) 변수 혹은 메소드가 없고,
    // 대신 패키지 내에 함수를 선언하여 사용할 수 있다.
    companion object {
        var total = 0
    }

    var count = 0
    fun sales(){
        total++
        count++
    }
}


예제4

fun main(args: Array<String>){
    var first = MyHtml.generateHtmlObject("first")
    first.setColumnHeaders("1열","2열","3열")
    //println("first.generateTable() => ${first.generateTable()}")

    val second = MyHtml.generateHtmlObject("second", 5,5)
    second.setColumnHeaders("월","화","수","목","금")

    val third = MyHtml.generateHtmlObject("third",2,7)
    third.setColumnHeaders("적색","녹색","노랑","파랑","보라","검정","주황")

    println("생성된 table 개수 : ${MyRecord.showTableCount()}")
    for( (k,v) in MyRecord.showTable()) {
        println("\n <!-- $k table (${v.row} x ${v.col}) -->")
        println(v.generateTable())
        println()
    }
}

object MyRecord {
    private var _count = 0
    private val _tables = hashMapOf<String,MyHtml>()

    fun gen(table: MyHtml){
        _tables.put(table.name, table)
        _count++
    }
    fun showTableCount() = _count
    fun showTable(): Map<String,MyHtml> = _tables
}

class MyHtml {
    val name: String
    val row: Int
    val col: Int
    private val HEAD = "<table border='1' cell-spacing='10' cell-padding='10'"
    private val TAIL = "</table>"
    private lateinit var comment: String
    private val tableColumns = ArrayList<String>()

    private constructor(_name: String, row: Int, col: Int ){
        this.name = _name
        this.row = row
        this.col = col
    }

    fun setColumnHeaders(vararg colNames: String){
        this.tableColumns.addAll(colNames)
    }

    companion object _GenTable {
        fun generateHtmlObject(_name: String, row: Int = 2, col: Int = 3): MyHtml {
            val mh = MyHtml(_name, row, col)
            mh.comment = "<!-- auto-generator table by MyHtml v1.0 -->"
            // 테이블 객체 기록
            MyRecord.gen(mh)
            return mh
        }
    }

    private inner class Tr {
        val rowHtml = """
            |<tr>
            | ${"<td>-</td>".repeat(col)}
            |</tr>${"\n"}
        """.trimMargin()
    }

    private inner class Th(val wpx: Int = 40) {
        val headers = tableColumns.map {
            "<th width='$wpx'> $it </th>"
        }
        val rowHtml = """
            |<tr>
            | ${headers.joinToString("")}
            |</tr>
        """.trimMargin()
    }

    // 테이블 생성 실행
    fun generateTable() =
        """
                |$comment
                |$HEAD
                |${Th().rowHtml}
                |${Tr().rowHtml.repeat(row)}
                |$TAIL
            """.trimMargin()
}


블로그 이미지

Link2Me

,
728x90

코틀린에서는 접근자와 설정자를 따로 만들어줄 필요가 없다.
자바에서는 private 접근제한자로 은닉화된 멤버 변수에 getter/setter 메소드를 사용해서 접근한다.
코틀린은 property가 getter/setter를 대체한다.

코틀린에서 속성(property)란
최상위 변수(함수나 클래스 외부에 정의된 변수)나 클래스의 멤버 변수로 선언하면 속성으로 간주된다.
클래스의 멤버 변수는 모두 private 제한자로 지정된다.
따라서 해당 클래스의 내부의 getter와 setter를 통해서만 속성을 참조할 수 있다.

 

package com.myapp.funcargs

fun main(){
    val a = Person("홍길동", 30)
    println(a)
    //println(a.toString())

    val b = a.copy("이순신")
    println(b)

    val c = b.copy(age=40)
    println(c)

    val(name, age) = Person("강감찬",35)
    println("성명 : $name, 나이 : $age")



    val obj = Pair<String, Int>("김좌진", 50)
    println(obj)
}

// 데이터는 보유하지만 아무것도 안하는 클래스
data class Person(val name: String, val age: Int)
// 파라미터 없는 기본 생성자가 필요한 경우에는
// data class Person(val name: String = "", val age: Int = 0)
// 모든 프로퍼티에 기본 값을 설정해 주면 된다.
// 기본 생성자에서 선언된 속성을 통해, 컴파일러가 자동으로 toString(), hashCode(), equals() 등을 생성한다.
// 명시적으로 정의해준 경우에는 컴파일러가 자동으로 생성하지 않는다.

 

실행결과

 

 

import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
 
@Parcelize
data class Memo_AddEdit (
    var idx: String,
    var memo: String,
    var userID: String,
    var userNM: String,
): Parcelable
 
 
class MainActivity : AppCompatActivity() {
    private lateinit var mContext: Context
    private lateinit var mMemo_AddEdit: Memo_AddEdit
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mContext = this@MainActivity
 
        initView()
    }
 
    private fun initView() {
        // 값을 할당함으로써 초기화가 된다.
       mMemo_AddEdit = Memo_AddEdit("0","",userid,userNM)
 
    }
}

 

'안드로이드 > Kotlin 문법' 카테고리의 다른 글

코틀린 null 안전과 예외  (0) 2020.05.05
코틀린 object  (0) 2020.01.19
코틀린 고차함수와 람다 그리고 확장함수  (0) 2020.01.14
코틀린 접근 제한자  (0) 2020.01.14
코틀린 패키지  (0) 2020.01.13
블로그 이미지

Link2Me

,
728x90
코틀린 함수의 기본형태
fun 함수명(매개변수):Unit {
}

fun 함수명(매개변수):리턴타입 {
   return 값
}


고차함수

고차함수(High-order Function)란 다른 함수를 인자로 사용하거나, 함수를 결과값으로 반환하는 함수를 말한다.


package com.myapp.funcargs

fun main(){
    val a = sum(4,5) // 일반 인자
    println("a result : $a")
    val b = mul(sum(1,2), 3) // 인자에 함수 사용
    println("b result : $b")

    println("ResultFun : ${resultFun()}")
}

fun sum(a: Int, b: Int) = a + b
fun mul(a: Int, b: Int) = a * b
fun resultFun(): Int {
    return mul(3,5) // 함수의 반환 값으로 함수 사용
}


람다 표현식

람다식, 또는 람다 함수는 프로그래밍 언어에서 사용되는 개념으로 익명 함수 (Anonymous Functions)를 지칭하는 용어이다.


val lambdaName: Type = {argument -> codeBody}


람다 함수는 { } 안에 매개변수와 함수 내용을 선언하는 함수로 다음 규칙에 따라 정의한다.
- 람다 함수는 항상 { }으로 감싸서 표현해야 한다.
- { } 안에 -> 표시가 있으며 -> 왼쪽은 매개변수, 오른쪽은 함수 내용이다.
- 매개변수 타입을 선언해야 하며, 추론할 수 있을 때는 생략할 수 있다.
- 함수의 반환값은 함수 내용의 마지막 표현식이다.


fun sum(a: Int, b: Int) = a + b
를 람다 함수로 정의하면
val sum ={a: Int, b: Int -> a + b}

또는

val sum: (Int, Int) -> Int ={a, b -> a + b}


package com.myapp.funcargs

fun main(){
    val a = sum(4,5) // 일반 인자
    println("a result : $a")
    val b = mul(sum(1,2), 3) // 인자에 함수 사용
    println("b result : $b")

    println("ResultFun : ${resultFun()}")

    val c:(String)->Unit ={str -> println("$str 람다함수")}
    b(c)
}

val sum ={a: Int, b: Int -> a + b}
// val sum: (Int, Int) -> Int ={a, b -> a + b}
// val mul ={a: Int, b: Int -> a * b}
val mul: (Int, Int) -> Int ={a, b -> a * b}
fun resultFun(): Int {
    return mul(3,5) // 함수의 반환 값으로 함수 사용
}
fun b (function: (String)->Unit){
    function("b가 호출한")
}




확장함수(Extension Function)
Extension functions(확장 함수)는 기존에 정의된 클래스에 함수를 추가하는 기능이다.
자신이 만든 클래스는 새로운 함수가 필요할 때 쉽게 추가할 수 있다.
하지만 Standard Library 또는 다른 사람이 만든 라이브러리를 사용할 때 함수를 추가하기가 매우 어렵다.

확장 함수는 fun 클래스이름.함수이름(매개변수): 리턴타입 { 구현부 }으로 정의할 수 있다.

또는
fun 확장하려는대상.함수명(매개변수):Unit {
}

fun 확장하려는대상.함수명(매개변수):리턴타입 {
   return 값
}


확장함수 예제1

class Car {
    fun getPrice() : Int {
        return 8000
    }
}

fun Car.getBrandName() : String {
    return "BMW"
}

fun Car.getBrandName2() : Car {
    println("KIA")
    return this
}

fun main(args: Array<String>) {
    val car = Car()
    println(car.getBrandName())
    println(car.getBrandName2().getPrice())


확장함수 예제2

fun String.getLonggerLength(x: Int) : Int {
    return if(this.length > x) this.length else x
}

fun main(args: Array<String>) {
    println("Hello".getLonggerLength(3))
} 

fun main(args: Array<String>) {
    println("Hello".getLonggerStringLength("Have a nice day!"))
}

fun String.getLonggerStringLength(x: String) : Int {
    return if(this.length > x.length) this.length else x.length
}


확장함수 예제3

class Car {
    fun getPrice(): Int {
        return 10000
    }
}

fun Car.getPrice(): Int {
    return 20000
}

fun Car.getPrice(price: Int): Int {
    return 30000
}

fun main(args: Array<String>) {
    val car = Car()
    println(car.getPrice())
    println(car.getPrice(0))


확장함수 예제4

fun List<Int>.getHigherThan(num: Int): List<Int> {
    val result = arrayListOf<Int>()
    for (item in this) {
        if (item > num) {
            result.add(item)
        }
    }
    return result
}

fun main() {
    val numbers: List<Int> = listOf(1, 2, 3, 4, 5, 6, 7)
    val filtered = numbers.getHigherThan(3).toString()
    System.out.println(filtered)
}
 


'안드로이드 > Kotlin 문법' 카테고리의 다른 글

코틀린 object  (0) 2020.01.19
코틀린 data class (자바의 getter 와 setter 클래스)  (0) 2020.01.16
코틀린 접근 제한자  (0) 2020.01.14
코틀린 패키지  (0) 2020.01.13
코틀린 인터페이스  (0) 2020.01.13
블로그 이미지

Link2Me

,
728x90

접근 제한자란 객체가 공개되어야 하는 범위를 정해주는 역할을 한다.

자바에서는 접근 제한자(Access Modifier)라는 용어를 사용하고
코틀린에서는 Visibility Modifier로 사용하는데, 우리말로는 접근 제한자 또는 가시성 제한자라고 할 수 있다.
코틀린에서 접근 제한자를 가질 수 있는 요소로는 class, object, interface, constructor, function, property 등이 있다.

자바에서는 접근 제한자를 붙이지 않으면 default(package-private) 이다.
그러나 코틀린에서는 아무것도 붙이지 않으면 public 이다.

코틀린에서 말하는 모듈은 자바와는 약간의 차이가 있는데, 자바에서의 모듈은 기능이 비슷한 클래스들의 집합체를, 코틀린에서는 동일한 컴파일의 집단을 의미한다.


패키지 레벨의 접근제한자

프로퍼티와 함수를 최상위(Top) 레벨에 작성한다는 것은 클래스에 멤버로 포함하지 않고 코틀린 파일에 정의하는 것을 의미한다.
package foo

public val myData: Int = 10
public fun myFun() {}
public class myClass() {}

- 접근 제한자를 표기하지 않으면 public이 기본으로 설정되며, 누구나 접근이 가능하다.

- private : 해당 .kt 파일 내부에서만 사용 가능하다.
- internal : 프로젝트의 모듈 안에서 누구나 접근이 가능하다.
- protected : 최상위(top-level)에서는 사용할 수 없다.
 

클래스와 인터페이스의 접근제한자
클래스 또는 인터페이스 안에 선언되는 멤버들에 사용되는 접근 제한자는 다음과 같은 의미를 갖는다.
- public : 누구나 접근이 가능하다.
- private : 클래스 내부에서만 접근이 가능하다.
- protected : 클래스 내부와 상속받는 객체에서 접근이 가능하다.
- internal : 프로젝트의 모듈 안의 누구나 접근이 가능하다.


private, protected, public 과 Java와 코틀린에서 같은 의미로 사용된다.
하지만 internal은
자바에서는 default(같은 패키지내에서만 접근가능)
코틀린에서는 같은 모듈안에서만 사용 가능하다. 


생성자의 접근제한자
생성자에도 접근제한자를 붙일 수 있다. 기본은 public 생성자로 누구나 접근이 가능하다.
internal을 붙이면 모듈 내에서만 접근 가능하다.
private을 붙이면 클래스 내에서만 접근이 가능하여 외부에서 생성할 수 없다.

// private 생성자
class Person private constructor(a: Int) {
}



Java 접근 제한자

접근 제한자는 Class 의 멤버에 대해 다른 Class 에서 접근하는 것을 제한하는 용도로 사용된다.

- public : 모든 Class 에서 접근 가능

- protected : 같은 패키지에 있는 Class, 그리고 다른 패키지의 자손 클래스에서 접근이 가능

- default : 같은 패키지에 있는 Class 들만 접근 가능

  String name; // 아무런 접근 제한자도 지정되지 않았으므로 default 접근 제한자다.

- private : 같은 Class 내에서만 접근 가능


지시자

클래스 내부

동일 패키지

상속받은 클래스

이외의 영역

 private


 X

X

X

 default


X

X

 protected


X

 public




블로그 이미지

Link2Me

,
728x90

코틀린에서 프로젝트(Project)는 모듈(Module), 패키지(Package), 파일(File)로 구성되어 있다.
프로젝트에는 모듈이 있고 모듈은 다시 패키지로 구성되어 있다.


Java는 파일명과 클래스명이 동일해야 하지만, 코틀린은 public 클래스는 하나만 사용 해야하는 규칙이 없다.


코틀린 파일은 .kt 확장자를 가지며 맨 위에는 이 파일이 어떤 패키지에 포함된 것인지 코틀린 컴파일러가 알 수 있도록 패키지 이름을 선언해야 한다.
만약 패키지 이름을 선언하지 않으면 그 파일은 자동으로 default 패키지에 포함된다.
파일이 패키지 안에 들어있어도 패키지 이름을 선언하지 않으면 default 패키지에 포함된 것으로 인식한다.
파일에 클래스가 여러 개 정의되어 있다면 파일은 단순히 클래스를 묶는 역할을 하고 kt 확장자가 붙게 된다.
코틀린에서는 파일명과 클래스의 선언 개수에 큰 의미를 두지는 않는다.
같은 파일에 있는 여러 개의 클래스는 모두 그 파일에서 지정한 패키지로 인식한다.


패키지 이름은 파일 맨 위에 적는다. 이때 패키지 이름 앞에 package라는 키워드를 함께 입력해야 패키지 이름으로 인식한다. 단, 패키지의 이름은 특수 문자나 숫자로 시작하면 안된다.

블로그 이미지

Link2Me

,
728x90

코틀린 인터페이스

- Java 와 달리 인터페이스 함수가 구현부를 가질 수도 있고, 추상함수만 가질 수도 있다.

- 상속과 마찬가지로 콜론(:)을 사용해서 인터페이스를 구현한다.

  자바에서는 클래스의 상속과 인터페이스의 구현을 extends 와 implements로 구분한다.

- 상속은 한번에 하나밖에 안되지만, 인터페이스는 콤마(,)로 여러개를 구현 가능하다.

   또한, 콜론(:)으로 동시에 클래스 상속과 인터페이스 구현이 가능하다.

- 코틀린에서는 상속받거나 구현한 함수의 앞에 무조건 override 키워드를 붙이도록 강제한다.

  Java에서는 부모 클래스의 메서드를 재정의하거나 인터페이스 구현 메서드를 @Override 어노테이션으로 구분한다.
  하지만 @Override 어노테이션 추가는 선택 사항이라 메서드의 유형을 코드만으로 구분하기는 어렵다.


fun main(args: Array<String>) {
    var a = Person()
    a.click()
    a.run()
}

interface Clickable {
    // 일반함수 : open 함수로 간주한다.
    fun click() {
        println("버튼을 눌렀어요.")
    }
}

interface Runner {
    // 추상함수 : abstract 함수로 간주한다.
    fun run()
}

class Person : Runner, Clickable {
    override fun run(){
        println("성큼 성큼 달린다.")
    }
} 


만약, 인터페이스 내의 함수들이 구현체가 있고, 중복된 함수가 있을 경우에는 어떻게 할까?

필요한 클래스의 super 를 호출할 수 있다. 둘 다 혹은 필요한 클래스의 함수만 호출해도 된다.


fun main(args: Array<String>) {
    var a = Person()
    a.click()
    a.run()
}

interface Clickable {
    // 일반함수 : open 함수로 간주한다.
    fun click() {
        println("클릭 버튼을 눌렀어요.")
    }
}

interface Runner {
    // 추상함수 : abstract 함수로 간주한다.
    fun run()
    fun click() {
        println("Runner 버튼을 눌렀어요.")
    }
}

class Person : Runner, Clickable {
    override fun run(){
        println("성큼 성큼 달린다.")
    }

    override fun click() {
        super<Clickable>.click()
        super<Runner>.click()
    }
}
 


'안드로이드 > Kotlin 문법' 카테고리의 다른 글

코틀린 접근 제한자  (0) 2020.01.14
코틀린 패키지  (0) 2020.01.13
코틀린 추상클래스  (0) 2020.01.12
코틀린(Kotlin) 클래스 선언 및 상속  (0) 2020.01.12
코틀린 기본 이해  (0) 2020.01.07
블로그 이미지

Link2Me

,
728x90

추상 클래스

- 클래스와 일부 멤버가 abstract로 선언되면 추상 클래스이다.
- abstract 키워드 사용시 클래스나 함수에 open 키워드를 따로 명시하지 않아도 된다.
- abstract 함수는 선언부분만 있고 구현부가 없다.

  추상 함수는 상속받은 서브 클래스에서 override하여 재정의해야 한다.
- 다중 상속은 불가능하다.


※ 추상 클래스는 미완성 설계도이다. 미완성 설계도로는 제품을 만들 수가 없다.

   클래스는 직접 객체 생성(인스턴스화)을 할 수 있지만, 추상 클래스는 직접 객체 생성을 할 수가 없다.

   반드시 상속을 받는 자식 클래스가 있어야만 비로소 객체를 생성할 수 있다.


fun main() {
    var b = Dog("불독", 5)

    b.introduce()
    b.bark()
    b.sniff()

    var c = Cat("나갈래", 4)
    c.introduce()
    c.meow()
    c.sniff()
}

abstract class Animal(var name:String, var age:Int, var type:String){
    abstract fun introduce()
    fun sniff(){
        println("냄새를 맡느라고 끙끙거린다.")
    }
}

class Dog(name:String, age:Int) : Animal (name,age,"개"){
    override fun introduce(){
        println("타입 : Dog, ${name}, ${age}살 입니다.")
    }

    fun bark(){
        println("멍멍멍")
    }
}

class Cat(name: String, age: Int) : Animal(name, age, "고양이"){
    override fun introduce(){
        println("타입 : Cat, ${name}, ${age}살 입니다.")
    }

    fun meow() {
        println("야옹야옹")
    }
}


예제2) 출처 : https://www.programiz.com/kotlin-programming/abstract-class

abstract class Person(name: String) {
init {
println("My name is $name.")
}
// 일반 메서드
fun displaySSN(ssn: Int) {
println("My SSN is $ssn.")
}
// 추상 메서드
abstract fun displayJob(description: String)
}

class Teacher(name: String): Person(name) {
// 추상 메서드 구현
override fun displayJob(description: String) {
println(description)
}
}

fun main(args: Array<String>) {
val jack = Teacher("Jack Smith")
jack.displayJob("I'm a mathematics teacher.")
jack.displaySSN(23123)
}


'안드로이드 > Kotlin 문법' 카테고리의 다른 글

코틀린 접근 제한자  (0) 2020.01.14
코틀린 패키지  (0) 2020.01.13
코틀린 인터페이스  (0) 2020.01.13
코틀린(Kotlin) 클래스 선언 및 상속  (0) 2020.01.12
코틀린 기본 이해  (0) 2020.01.07
블로그 이미지

Link2Me

,
728x90

코틀린 클래스 선언

class 클래스명(변수) { }

코틀린은 자바, C++과는 달리 클래스 선언 부분에서 이름에 괄호를 붙여 생성자를 만들 수 있다.

자바는 멤버변수를 field라고 하지만 코틀린 프로퍼티라는 용어를 사용한다.
자바는 생성자를 꼭 정의해야 하지만 코틀린은 생략이 가능하다.

코틀린에는 주 생성자와(primary constructor)와 부생성자가(secondary constructor)가 존재한다.
주 생성자는 class 선언과 함께 선언하고, 부 생성자는 추가적인 생성자가 필요할 때 사용한다.
생성자를 정의하지 않으면 기본으로 인자가 없는 생성자를 만들어 준다.

부모의 생성자는 super(), 클래스 내 생성자중 다른 생성자는 this()로 호출이 가능하다.


fun main() {
    var a = Person("박보영", 1990)  // 코틀린은 new 키워드 없이 인스턴스를 생성한다.
    var b = Person("전영경",1997)
    var c = Person("차은우")
    var d = Person("홍길동")

    //println("안녕하세요, ${a.birthYear}년생 ${a.name} 입니다.")
}

class Person (var name:String, val birthYear:Int) {

    // var name:String, val birthYear:Int

    // 프로퍼티 선언 및 초기화는 기본 생성자에서 간결한 구분으로 사용 가능


    // 초기화
    init {

        // init 안에서는 자바처럼 [this.클래스변수= 매개변수]를 얼마든지 할 수 있다.

        println("안녕하세요, ${this.birthYear}년생 ${this.name}입니다.")
    }

    // 보조 생성자 : constructor 키워드로 선언하고 클래스 body에 구현한다.

    // 클래스 별로 여러 개를 가질 수 있다.
    constructor(name: String) : this(name,1997)
}
 

기본 생성자

- 기본 생성자에 어노테이션 접근지정자 등이 있을 경우 constructor 키워드가 필요하다.

  class Person public @Inject constructor(name: String) { .... }



클래스 상속
- 코틀린의 최상위 클래스는 Any 이다.
  Any는 java.lang.Object 와는 다른 클래스이며, equals(), hasCode(), toString()만 있다.
- 상속 키워드는 콜론(:)이다. Java 의 상속 키워드는 extends
- 코틀린의 클래스와 메소드는 기본적으로 final이다. 즉 기본적으로 상속을 못하게끔 되어 있다.
  Java에서는 클래스나 메서드에 final 키워드를 붙여 클래스를 더 이상 상속받지 못하게 하거나,
  메서드를 재정의하지 못하게 할 수 있다.
- 클래스의 상속을 허용하려면 클래스 앞에 open 변경자를 붙여야 한다.
  open 변경자가 없으면 final 로 자식 클래스에서 override가 불가능하다. 
  오버라이드를 허용하고 싶은 메소드나 프로퍼티 앞에도 open 변경자를 붙여야 한다.
- 부모 클래스의 메소드를 자식 클래스에서 오버라이드 한 경우, 해당 메소드는 기본적으로 열려있다.

fun main() {
    var a = Animal("불독", 3,"개")
    var b = Dog("불독", 5)

    a.introduce()
    b.introduce()
    b.bark()

    var c = Cat("나갈래", 4)
    c.introduce()
    c.meow()
}

open class Animal(var name:String, var age:Int, var type:String){
        open fun introduce(){ // open 이므로 override 가능하다.
        println("저는 ${type} : ${name}, ${age} 살 입니다.")
    }
}

class Dog(name:String, age:Int) : Animal (name,age,"개"){
    // 상위 클래스 함수에 open 키워드를 명시하고, 서브 클래스 함수에 override 를 붙인다.
    override fun introduce(){
        println("타입 : 개, ${name}, ${age} 살 입니다.")
    }

    fun bark(){
        println("멍멍멍")
    }
}

class Cat(name: String, age: Int) : Animal(name, age, "고양이"){
    fun meow() {
        println("야옹야옹")
    }
}
 


'안드로이드 > Kotlin 문법' 카테고리의 다른 글

코틀린 접근 제한자  (0) 2020.01.14
코틀린 패키지  (0) 2020.01.13
코틀린 인터페이스  (0) 2020.01.13
코틀린 추상클래스  (0) 2020.01.12
코틀린 기본 이해  (0) 2020.01.07
블로그 이미지

Link2Me

,
728x90

구글이 2017년 5월 18일 안드로이드 공식 언어로 코틀린(Kotlin) 추가해서 현재 Android 개발은 Java 와 Kotlin 이 공존하고 있는데, 향후에는 Kotlin 으로 전부 대체될 것으로 보인다.

GitHub 에서 source code를 검색하면 Kotlin 으로 된 코드들이 검색되어 안배우면 안되겠다 싶어서 동영상 보면서 정리를 할 생각이다. 배울 언어가 너무 많아서 안배우고 싶은데, 안배우면 도태될 거 같아서....

 

Kotlin 은 IntelliJ IDE를 만든 JetBrains 에서 개발한 오픈소스 언이이다.

JVM에서 동작하고 Java 와 100% 호환된다.

Java 코드를 Kotlin 에서 인식하고, Kotlin 코드를 Java 에서 인식한다.

 

- Java 플랫폼에서 숫자형은 JVM primitive type 으로 저장됨
- Nullable 이나 제네릭의 경우에는 박싱됨
- 박싱된 경우 identity를 유지하지 않음.
- 문자(Characters) : 숫자로 취급하지 않음.

 

개발툴 다운로드

- 온라인에서 코드 테스트 : https://play.kotlinlang.org/ 에서 간단한 코드를 연습할 수 있다.

- Intellij IDEA 개발툴 : https://www.jetbrains.com/ko-kr/idea/ 에서 community 무료버전을 다운로드 받아서 설치한다.

  Java 에 대한 코드 연습을 Eclipse 툴에서 하듯이

  유투브 강좌 등에서 설명하는 코틀린 문법 연습은 IntelliJ IDEA 툴에서 하면 된다.

  Android Studio 에서 코틀린 선택하여 코드 연습하는 것은 적절하지 않다.

TOOL을 Jetbrains 에서 만들어서 Android Studio 와 메뉴구조는 비슷하다.

잠깐 써본 PHPStorm 도 메뉴구조가 거의 동일하더라.

 

변수의 선언

Java에서는 변수를 선언할 때 타입이 맨 앞에 온다. 코틀린에서는 타입 지정을 생략하는 경우가 흔하다.
Java에서는 두가지종류의 변수가 있는데 하나는 primitive type(기본형)이 있고 다른 하나는 레퍼런스 타입(객체형)이 있다. 코틀린에서는 이 모든게 통합됬다. 즉 레퍼런스 타입밖에 없다.
 

Java에서는 기본이 변수고 상수는 final을 붙혀서 표시했다.

코틀린에서는 변수는 var로 상수는 val로 표시한다.

val // 변경 불가능한 참조를 저장하는 변수다. 선언시에만 초기화 가능. 중간에 값을 변경할 수 없다.

var // 변경 가능한 참조다. 일반적으로 통용되는 변수, 언제든지 읽기쓰기가 가능하다.

 

 

기본적으로 모든 변수를 val 키워드를 사용해 불변 변수로 선언하고, 나중에 꼭 필요할 때에만 var 변수로 변경하라.

코틀린은 문장의 끝에 세미콜론을 사용하지 않는다.

코틀린은 기본변수에서 null 을 허용하지 않으며, 또한 변수에 값을 할당하지 않은채로 사용하게 되면 문법 에러를 표시하고 컴파일을 막아주므로 의도치 않은 동작이나 null pointer exception 등을 원천적으로 차단해 준다는 장점이 있다.

 

var a = 100

또는

var a: Int  // 초기화 식을 사용하지 않고 변수를 선언하려면 변수 타입을 반드시 명시해야 한다.

a = 100

 

※ var 키워드를 사용하면 변수의 값은 변경할 수 있지만 변수의 타입은 고정돼 바뀌지 않는다.

var a = 100

a = "kotlin"  ← 컴파일 오류 발생

컴파일러는 변수 선언 시점의 초기화 식으로부터 변수의 타입을 추론한다.

 

var a: Int? // 코틀린에서 null 값을 허용하려면 자료형의 오른쪽에 ? 기호를 붙여준다.

 

문자형 : 코틀린은 문자열을 유니코드 인코딩 방식 UTF-16 BE 을 사용하여 글자 하나당 2bytes 메모리 공간을 차지한다.

var a = '가' // 문자 한글자는 작은 따옴표 사용한다.

var a ="홍길동" // 문자열을 쌍따옴표를 사용한다.

var a ="""여러줄의

   문자열"""  // 쌍따옴표 3개를 사용하면 여러줄 문자열 가능하며, 줄바꿈이나 특수문자까지 사용 가능하다.

 

lateinit 키워드를 사용한 늦은 초기화

코틀린에서는 클래스의 변수(var, val로 선언되는 변수들)를 프로퍼티(Property)라 부른다.

코틀린에서는 일반적으로 생성자에서 모든 프로퍼티를 초기화해야 한다.

프로퍼티 타입이 null이 될 수 없는 타입이라면 반드시 null이 아닌 값으로 그 프로퍼티를 초기화해야 한다.

private var backPressHandler: BackPressHandler? = null

// null로 초기화하기 위해 null이 될 수 있는 타입인 프로퍼티를 선언한다.

backPressHandler!!.onBackPressed()

// 반드시 null 가능성을 신경써야 한다. non-null 단언 연산자 !!를 꼭 써야 한다.

 

lateinit 변경자를 붙이면 프로퍼티를 나중에 초기화할 수 있다.

private lateinit var backPressHandler: BackPressHandler

// 초기화를 하지 않고 null이될 수 없는 프로퍼티를 선언한다.

backPressHandler.onBackPressed()

 

lateinit은 다음 조건에서만 사용할 수 있다.
1) var 변수에서만 사용한다.
2) null 값으로 초기화 할 수 없다.
3) 초기화 전에는 변수를 사용할 수 없다.
4) Int, Long, Double, Float에는 사용할 수 없다.

5) lateinit은 custom getter/setter를 사용하지 않은 프로퍼티에만 사용할 수 있다.

문자열 접합 연산

println("안녕하세요, $name!")  → Java의 문자열 접합 연산 ("안녕하세요," + name + "!")와 동일한 기능

 

존재하지 않는 변수를 문자열 템플릿 안에서 사용하면 컴파일 오류가 발생한다.

$ 문자를 문자열에 넣고 싶다면 println("\$x")와 같이 \를 사용해 $를 이스케이프 시켜야 한다.

${name} 처럼 중괄호로 변수명을 감싸는 습관을 들이면 좋다. 이렇게 하면 복잡한 식도 중괄호로 둘러싸서 문자열 템플릿안에 넣을 수 있다.

 

 

형변환

코틀린에서는 Int 형과 Long 형 타입이 다른 경우 자동으로 형변환이 되지 않는다

즉, 다른 언어들이 지원하는 암시적 형변환은 지원하지 않고, 명시적 형변환만 지원한다.
  var a: Int = 100
  var b: Long = a.toLong()

 

배열

배열은 Array 클래스로 표현된다.

var intArr = arrayOf(1, 2, 3, 4, 5)  // arrayOf 함수를 통해 배열에 저장할 값들을 나열한다.

var intArr = arrayOfNulls<Int>(10) // null 로 채워진 배열. 

 

함수

function 의 약자인 fun 을 사용한다.

 

리턴과 파라미터가 없는 함수
fun 함수이름() { }

값을 리턴 하는 함수
fun 함수이름() : 리턴타입 { return 값 }

리턴과 파라미터가 있는 함수
fun 함수이름( 변수이름: 변수타입) : 리턴타입 { return 값 }

fun add(a: Int, b: Int, c: Int): Int {   // 반환형

    return a + b + c

}

단일표현식 함수 

fun add(a: Int, b: Int, c: Int) = a + b + c  // 반환형의 타입 추론이 가능하여 반환형을 생략할 수 있다.

 

IF문

- val max = if( a > b) a else b

  Java 의 삼항연산자(ternary)가 코틀린에는 없음.

  int max = (a > b) > a : b; // Java 의 삼항연산자

- If식의 branches 들이 블록을 가질 수 있음 {  }

  val max = if(a > b) {

    print("Choose a")

    a

  } else {

    print("Choose b")

    b

  }

 

다중 조건문 when

- when문은 C계열 언어의 switch 문에 대응한다.

- when문은 각각의 branches의 조건문이 만족할 때까지 위헤서부터 순차적으로 인자를 비교함.

  when (x) {

     1 -> print("x == 1")

     2 -> print("x == 2")

     else ->{

           print("x is neither 1 nor 2)

     }

  }

- when이 식으로 사용된 경우 else 문이 필수임

- when 이 식으로 사용된 경우 컴파일러가 else문이 없어도 된다고 입증할 수 있는 경우에는 else 생략 가능

  var res = when (x) {

    true -> "맞다"

    false -> "틀리다"

  }

- branch의 조건문에 콤마를 사용하여 여러조건들을 처리할 수 있다.

  when (x) {

     0, 1 -> print("x == o or x == 1")

     else -> print("기타")

  }

- branch의 조건문에 함수나 식을 사용할 수 있다.

  when (x) {

     parseInt(x) -> print("s encodes x ")

     1 + 5 -> print("6")

     else -> print("s does not encode x")

  }

- range 나 collection 에 in 이나 !in 으로 범위 등을 검사할 수 있다.

  val validNumbers = listOf(3, 6, 9)

  when (x) {

    in validNumbers -> print("x is valid")

    in 1..10 -> print("x is in the range")

    !in 10..20 -> print("x is outside the range")

    else -> print("none of the above")

  }

- is 나 !is 를 이용하여 타입도 검사할 수 있다.

  fun hasPrefix(x: Any) = when (x) {

is String -> x.startsWith("prefix")

else -> false

  }

- when 에 인자가 없으면, 논리연산으로 처리된다.

  when {

x.isOdd() -> print("x is odd")

x.isEven() -> print("x is even")

  }

 

For Loops

- for문은 iterator 를 제공하는 모든 것을 반복할 수 있다

  for(item in collection)

      print(item)

- for문의 body가 블록이 올 수도 있다.

  for(item in collection) {

     print(item.id)

     print(item.name)

  }

- for(i in 0..9)  // 0부터 1씩 증가시키며 반복한다.

  for(i in 0..9 step 3)  

     print(i)    // 3씩 증가되어 0, 3, 6, 9 를 출력한다.

- 감소 반복

  for(i in 9 downTo 0)   // 9부터 1씩 감소하여 반복한다.

  for(i in 9 downTo 0 step 3)  // 9부터 3씩 감소하여 반복한다.

- 문자 for 문

  for(i in 'a'..'f')   // a부터 f까지 반복한다.

- 반복문 탈출 break

  loop@for(i in 1..10){

     for(j in 1..5) {

         if(i == 1 && j == 2) break@loop

         println("i : $i, j : $j")

     }

  }

 

fun main(args: Array<String>) {
    var a = listOf(1, 2, 3, 4, 5)
    println(a)
    println(sum(3 , 4))
    foo()
    println("")
    foo2()
    println("")
    foo3()
    println("")
    foo4()
    println("")
    println(foo5())
}

fun sum(a: Int, b: Int):Int {
    return a + b
}

fun foo() {
    var ints = listOf(0, 1, 2, 3)
    ints.forEach(
            fun(value: Int){
                if(value == 1) return
                print(value)
            })
    print("End")
}

fun foo2() {
    var ints = listOf(0, 1, 2, 3)
    // 람다식에서 return 시 nearest enclosing 함수가 return 된다.
    ints.forEach {
                if(it == 1) return
                print(it)
            }
    print("End")
}

fun foo3() {
    var ints = listOf(0, 1, 2, 3)
    // 람다식에 대해서만 return 하려면 label 을 이용해야 한다.
    ints.forEach loop@{
                if(it == 1) return@loop
                print(it)
            }
    print("End")
}

fun foo4() {
    var ints = listOf(0, 1, 2, 3)
    // 람다식에 대해서만 return 하려면 label 을 이용해야 한다.
    // 암시적 레이블은 람다가 사용된 함수의 이름과 동일하다.
    ints.forEach {
        if(it == 1) return@forEach
        print(it)
    }
    print("End")
}

fun foo5(): List<String> {
    var ints = listOf(0, 1, 2, 3)
    val result = ints.map{
        if(it == 0){
            return@map "zero"
        }
        "No $it"
    }
    return result
}
 

 

 

'안드로이드 > Kotlin 문법' 카테고리의 다른 글

코틀린 접근 제한자  (0) 2020.01.14
코틀린 패키지  (0) 2020.01.13
코틀린 인터페이스  (0) 2020.01.13
코틀린 추상클래스  (0) 2020.01.12
코틀린(Kotlin) 클래스 선언 및 상속  (0) 2020.01.12
블로그 이미지

Link2Me

,