세상은 모험을 두려워 하지않은 자의 것이다~~!

Java 개발자가 Kotlin 을 공부하면서.. 끄적인 노트입니다.

 

상속 (Inheritance)

/**
 * 코틀린에도 Java 처럼 상속 개념이 있다.
 * 코틀린은 클래스와 함수가 기본적으로 final 이다, 클래스를 상속받고, 재정의 하려면 open 키워드를 사용해야한다.
 */
open class Car {

    var wheelSize: Int = 20;
    open var color: String = "White"
    open var doorCount: Int = 4;

    open fun carInfoPrint() {
        println("내차는 ${color}이다.")
    }

    fun upgradeWheelSize() {
        wheelSize += 1
    }
}

class SportsCar : Car() { // 클래스 명뒤에 콜론 뒤에 Car 생성자를 호출함으로써 상속받았음을 의미한다.
    override var color = "Silver"
    //    override wheelSize = 30 // wheelSize는 open 속성이 아니라 재정의 불가
    override var doorCount = 2

    override fun carInfoPrint() {
        println("SportsCar는 문짝이 ${doorCount} 이고 ${color} 색상이다. 많은 차들은 문짝이 ${super.doorCount} 이고, ${super.color} 색상이다.")
    }

//    override fun upgradeWheelSize() {} // open 함수가 아니라 재정의 불가
}

fun testOverride() {
    SportsCar().carInfoPrint()
    // SportsCar는 문짝이 2 이고 Silver 색상이다. 많은 차들은 문짝이 4 이고, White 색상이다.
}


open class Car1 {
    val color: String
    val doorCnt: Int

    constructor(color: String, doorCnt: Int) {
        this.color = color
        this.doorCnt = doorCnt
    }

    open fun carInfoPrint() {
        println(" 문짝이 ${doorCnt}개 이고, ${color} 색상이다.")
    }
}

/**
 * Primary Constructor (기본생성자) 로 정의 할 수 있고
 */
class SportUtilityVehicle(color: String, doorCnt: Int) : Car1(color, doorCnt) {
    override fun carInfoPrint() {
        println("SUV 문짝이 ${doorCnt}개 이고, ${color} 색상이다.")
    }
}

/**
 * Secondary Constructor (보조 생성자) 로 정의 할 수 있다.
 */
class SportUtilityVehicle1 : Car1 {
    var foldingChair: Boolean

    constructor(color: String, doorCnt: Int, foldingChair: Boolean) : super(color, doorCnt) {
        this.foldingChair = foldingChair
    }

    override fun carInfoPrint() {
        println("SUV 문짝이 ${doorCnt}개 이고, ${color} 색상이고 의자가 ${if (foldingChair) "접힌다." else "안접힌다."}")
    }

    open class InnerClass {
        open fun wow(){
            println("WOW")
        }
    }

    fun anonymousTest(){
        // Anonymous Class 는 Java 와 다르게 object 키워드를 명시적으로 붙여줘야 한다.
        var overrodeWow = object : InnerClass() {
            override fun wow() {
                println("Anonymouse Class에서 재정의 되었음")
            }
        }
        overrodeWow.wow()
    }
}

fun testOverride1() {
    SportUtilityVehicle("Red", 5).carInfoPrint()
    // SUV 문짝이 5개 이고, Red 색상이다.
    SportUtilityVehicle1("Red", 5, true).carInfoPrint()
    // SUV 문짝이 5개 이고, Red 색상이고 의자가 접힌다.
    SportUtilityVehicle1.InnerClass().wow()
    // WOW
    SportUtilityVehicle1("Red", 5, true).anonymousTest()
    // Anonymouse Class에서 재정의 되었음
}


fun main(arr: Array<String>) {
    testOverride()
    testOverride1()
}

Java 개발자가 Kotlin 을 공부하면서.. 끄적인 노트입니다.

 

Lambda

/*
    Lambda
    고차 함수 (High order function) : 함수를 자료형이나, 객체처럼 인자로 받거나 리턴 할수 있는 함수 (일반적으로 callback 함수라고도 함)

    일반적으로 Lambda 사용법 (Java8 부터 지원하는 Lambda 식과 유사함)
    (인자타입1, 인자타입2) -> 반환형  
    () -> Unit // argument가 없고, void
    (Int, Int) -> Int // argument를 Int, Int를 받고 Int type을 반환
 */

fun testFunc1(func: () -> Unit) { // return type 이 없는 lambda 함수를 Argument로 받을 수있는 함수
    func()
}

fun testFunc2(num: Int, func: () -> String) { // String을 return하는 lambda 함수를 Argument로 받을 수 있는 함수
    println("Argument num : '${num}'")
    var callback = func()
    println(callback)
}

fun testLambdaExec1() {
    // argument가 없고 return 값이 없는 고차 함수를 전달
    testFunc1 { println("Argument가 없고, 리턴값이 없는 고차함수") }

    /* 
        실행결과
        Argument가 없고, 리턴값이 없는 고차함수
     */

    // argument가 있고, multiple line에 return value가 있는 고차함수를 전달
    testFunc2(100) {
        println("Argument가 있고, 리턴값이 있는 고차함수")
        "Return!!!"
    }

    /* 실행결과
        Argument num : '100'
        Argument가 있고, 리턴값이 있는 고차함수
        Return!!!
     */
}


// ===================

fun testFunc3(func: (Int, Int) -> Int) {
    println("Argument가 2개, Return 값이 있는 고차함수 처리 결과 : ${func(10, 30)}")
}


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

fun testLambdaExec2() {
    testFunc3 { a, b -> a + b } // Argument가 2개, Return 값이 있는 고차함수 처리 결과 : 40
    testFunc3(::sumTest) // 미리 정의된 함수를 전달 할수도 있다. (argument와 return type 이 일치 해야함)
}


// ===================

fun testFunc4(func: (Int) -> String) {
    println("결과는 : ${func(10)}")
}

fun testLambdaExec3() {
    testFunc4 {
        println("단일 매개변수일 경우 암묵적으로 it을 keyword로 매개변수에 접근 할 수 있다. it : '${it}'")
        it.toString()
    }
}

// ================

fun testLambdaExec4() {
    val sumTest1: (Int, Int) -> Int = { a, b -> a + b }
    val sumTest2: (Int, Int) -> Int = ::sumTest // 미리 정의된 함수를 변수에 담을 수 있다.

    println("sumTest1 : ${sumTest1(10, 20)}")
    println("sumTest2 : ${sumTest2(20, 30)}")
    println("sumTest : ${sumTest(30, 40)}")
    /* 실행 결과
        sumTest1 : 30
        sumTest2 : 50
        sumTest : 70
     */
}

 

Class

/*
    Java에서 모든 Class가 Object를 상속받고 있듯이 코틀린은 모른 클래스가 Any 클래스를 상속받고있다.
    객체 생성은 Java와 유사하지만 간결한 표현을 위해 new 키워드가 생략되었다.
 */

// String 객체 생성
var str = String()

/*
    생성자

    생성자를 명시하지 않으면 Java와 같이 Default 생성자를 가진다.
    클래스 선언시 Calc() 로 선언할수 있다. () 는 생략가능하다.
 */
/**
 * 생성자를 명시하지 않으면 Java와 같이 Default 생성자를 가진다.
 * 클래스 선언시 Calc() 로 선언할수 있다. () 는 생략가능하다.
 */
class Calc {
    fun sum(a: Int, b: Int): Int {
        return a + b
    }
}


/**
 * Default constructor 를 가진 Class 정의
 * Cal1 뒤에 괄호는 생략 해도 됨
 */
class Calc1() {
    // init block 은 Class 가 생성 될때 실행 된다.
    init {
        println("Class 가 생성 될때 실행 된다.")
    }

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


/**
 * Argument가 있는 constructor를 가진 Class 정의
 */
class SmartPhone(manufacturer: String, model: String) {
    init {
        println("제조사는 : ${manufacturer}, 모델명은 : ${model}")
    }
}

fun testArgumentClass() {
    var phone = SmartPhone("SAMSUNG", "Note 20 Ultra")
    // 출력 : 제조사는 : SAMSUNG, 모델명은 : Note 20 Ultra
}


/**
 * 생성자를 Private으로 선언
 * 외부에서 객체 생성을 할수 없게 막음
 */
class PrivateConstructClass private constructor() {
    init {
        println("Private construct class..")
    }

    /**
     * companion object 는 Java의 static 과 유사하다.
     * companion object 블럭에 정의된 함수는 java의 static 함수와 동일하게 접근 할 수 있다.
     * 단 하나의 Class에 companion object 는 하나만 선언할 수 있다고 한다.
     */
    companion object { // companion object 는 java 의 static 이라고 생각해도 된다.
        fun create(): PrivateConstructClass {
            return PrivateConstructClass()
        }
    }
}

fun testPrivateConstructClass() {
    var privateConstructClass = PrivateConstructClass.create()

}

/**
 * Companion object 는 클래스 하나에 하나만 선언할 수 있다.
 * Companion object 명을 생략해도 된다.
 * companion object 블럭에 정의된 변수, 함수는 java 의 static 과 유사하게 클래스명으로 바로 접근 할 수있다.
 */
class CompanionObjectClass {
    companion object Calc { //Companion obj
        val value1 = "SUM"
        val value2 = "TEST"

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

class CompanionObjectClass1 {
    companion object { // companion object 명을 생략 할 수 있다.
        val value1 = "SUM"
        val value2 = "TEST"

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

fun testCompanionObjectClass() {
    println("Companion object 를 통해 접근 할수 있다: ${CompanionObjectClass.Calc.sum(1, 4)}")
    println("Companion object 를 통해 접근 할수 있다: ${CompanionObjectClass.Calc.value1}")
    println("Companion object 를 통해 접근 할수 있다: ${CompanionObjectClass.Calc.value2}")
    println("Class로 바로 Companion object에 접근 할수 있다: ${CompanionObjectClass.sum(1, 4)}")
    println("Class로 바로 Companion object에 접근 할수 있다: ${CompanionObjectClass.value1}")
    println("Class로 바로 Companion object에 접근 할수 있다: ${CompanionObjectClass.value2}")
    println("Companion object 명을 생략 할 수도 있다: ${CompanionObjectClass1.sum(2, 3)}")
}

class ConstructorOverloading {
    val title:String
    val subject:String
    val count:Int

    constructor(title:String, subject:String, count:Int){
        this.title = title;
        this.subject = subject;
        this.count = count;
    }

    constructor(title: String, subject: String) : this(title, subject, 0)

    constructor(title:String) : this(title, title){
        // 추가적인.. 무언가를 할 수 있다.
    }

    fun print(){
        println("Title은 ${title}, Subject는 ${subject}, Count는 ${count}이다.")
    }
}
fun testConstructorOverloading(){
    ConstructorOverloading("Kotlin").print()
    ConstructorOverloading("Kotlin", "공부하기").print()
    ConstructorOverloading("Kotlin", "공부하기", 7).print()
    /*
        Title은 Kotlin, Subject는 Kotlin, Count는 0이다.
        Title은 Kotlin, Subject는 공부하기, Count는 0이다.
        Title은 Kotlin, Subject는 공부하기, Count는 7이다.
     */
}


fun main(args: Array<String>) {
    println("Default 생성자 (괄호 생략) : ${Calc().sum(10, 30)}")
    println("Default 생성자 (괄호) 첫번째 : ${Calc1().sum(10, 30)}")
    println("Default 생성자 (괄호) 두번째 : ${Calc1().sum(10, 30)}")
    testArgumentClass()
    testPrivateConstructClass()
    testCompanionObjectClass()
    testConstructorOverloading()
}

Java 개발자가 Kotlin 을 공부하면서.. 끄적인 노트입니다.

예외처리

    /*   
        기본적인 예외처리는 Java와 거의 동일함
        다만 Java는 Checked exception 은 반드시 try-catch or throws 처리를 강제 했었으나 코틀린은 unchecked exception 으로 처리된다고 함.
     */
    try {
        println("String to Int (NumberFormatException) : ${"ABC".toInt()}")
    } catch (e: NumberFormatException) {
        println("NumberFormatException error : ${e.message}")
    }

    // 또 다른점은 try-catch 로 리턴값을 전달 받을 수 있음 (if문을 삼항연산자 처럼 사용한 방법), Number format exception 이 발생하면 default 0으로 number를 초기화 한다.
    var number:Int = try {
        "AAA".toInt()
    } catch (e:java.lang.NumberFormatException) {
        0
    }

    // try-with-resource, 코틀린은 use block 을 사용한다. use block 을 사용하면 해당 블럭이 종료되면 AutoCloseable의 close를 호출한다.

    var fileName:String = "~/test.text"
    var contents:String = "Hello Kotlin.."

    File(fileName).outputStream().use {
        it.write(contents.toByteArray())
    }

    try {
        File(fileName).inputStream().use {
            println(String(it.readBytes()))
        }
    } catch (e:FileNotFoundException) {
        println("File not found...")
    }

 

함수

  // Void 함수, Unit을 반환한다. (생략해도 됨)
  fun voidFunction(arg: String): Unit {
      println("Unit을 반환하면 Java의 Void 를 사용 하는 것과 같음")
  }

  // 반환 값이 있는 함수
  fun functionName(arg1: String, arg2: Int): String {
      sum(5)
      return "arg1과 arg2 더하기 ${arg1} : ${arg2}"
  }

  // Default Argument, default 값을 선언하면 호출할때 두번째 인자를 전달하지 않아도 1을 가본값으로 사용한다.
  fun sum(a: Int, b: Int = 1): Int {
      return a + b
  }

  fun test() {
      println("값이 머가 나올까요? ${sum(5)}") //  값이 머가 나올까요? 6
  }

  // 간단한 함수 선언
  fun multiple(a: Int, b: Int) = a * b

  fun test1() {
      println("간단함수 테스트 : ${multiple(3, 4)}") // 간단함수 테스트 : 12
  }


  // 가변 인자 (Java 의 ...)
  fun sum(vararg values: Int): Int {
      return values.sum()
  }

  fun test2() {
      println("가변인자 테스트 : ${sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)}") //가변인자 테스트 : 55
  }

  // 내부 함수, 함수 안에 함수를 정의할수 있음
  fun outer(a: String): String {
      fun inner1(b: String): String {
          fun inner2(c: String): String {
              return "(${c} Inner1)"
          }
          return "{${inner2(b)} Inner2}"
      }
      return "[${inner1(a)} outer]"
  }

  fun test3(){
      println("내부함수 테스트 : ${outer("JSMARCH")}") // [{(JSMARCH Inner1) Inner2} outer]
  }

Java 개발자가 Kotlin 을 공부하면서.. 끄적인 노트입니다.

변수

  // var : 변수
  // val : final 변수

  val name:String = "jsmarch"
  var age:Int = 39

  // name = "Poby" // Compile error
  age = 40 
  println("${name}의 나이는 ${age} 입니다.") // jsmarch의 나이는 40 입니다.

  var job = "Developer" // String 으로 명시하지 않아도 타입 추론하여 String으로 자료형을 부여한다.

  // 코틀린은 기본적으로 null을 허용하지 않는다.
  // 만약 변수를 초기화 하지 않고 사용하면 java에서는 null 로 처리되지만 (primitive type 제외)
  // 코틀린은 초기화 되지 않는 변수를 사용할때 compile error 가 발생한다.
  var nickName:String
  // println(nickName) // Compile error

  // Null을 명시적으로 사용하고 싶으면 Type 뒤에 "?"를 추가하면 된다.
  // var company:String = null // Compile error
  var company:String? = null

  // nullable한 변수에 접근해야 할때 java는 항상 null check를 해야하지만 코틀린은 nullable 변수를 쉽게 사용할수 있다.
  var phoneNumber:String? = null
  // println("나의 phone number는 ${phoneNumber.length} 자리 입니다.") // Nullable 하기때문에 compile error
  println("나의 phone number는 ${phoneNumber?.length} 자리 입니다.") // phoneNumber 가 null 이기 때문에 length를 호출하지 않고 null을 출력한다.

자료형

  val int: Int = 123
  var long: Long = 123
  var float: Float = 0.2F
  var double: Double = 3.3
  var char: Char = 'A'
  var boolean: Boolean = false
  var string: String = "12345"
  var booleanStr = "true"
  var multiLineStr: String = """
  		|여러줄은 이렇게 출력하나봄...
  		|탭을 없애려면 앞에 | 를 넣으면 된다고하네요
  		|나름 JAVA 보다 편하네요
  	""".trimMargin() // trimMargin 으로 marginPrefix (|) 까지의 white space를 제거한다.
  println(multiLineStr)

  // 타입추론
  var int1 = 123
  var long1 = 123L

  // 문자열 Template을 지원하기 때문에 Java에서 StringBuilder나 StringBuffer 또는 + 로 열심히 붙이거나 MessageFormat 을 사용하거나 했었는데 간단하게 사용할수 있네요..
  var stringTemplate = "int는 ${int} 이구요.. String은 ${string}입니다. String은 ${string.length}자입니다. 이렇게도 사용할수 있어요 $booleanStr 이렇게 사용하려면 양옆에 공백이 있어야해요."
  println(stringTemplate)

  // type 변환 (null을 허용하지 않다보니.. java에서 Wrapper type과 primitive type을 혼용할때 발생할수있는 NPE 위험은 없네요.)
  println("String to Int : ${string.toLong()}") // 문자열 format
  println("int to String : ${int.toString()}")
  println("String to Boolean : ${booleanStr.toBoolean()}")
  println("String to Boolean : ${"".toBoolean()}")

  // String을 number로 변환시 변환할수 없는 문자열일 경우 Java와 마찬가지로 NuberFromatException 이 발생한다.
  try {
      println("String to Int (NumberFormatException) : ${booleanStr.toInt()}")
  } catch (e: NumberFormatException) {
      println("NumberFormatException error : ${e.message}")
  }

  // Array 생성
  var arr: Array<String> = arrayOf("A", "B", "C", "D")
  println("Array :: ${arr.contentToString()}")

  // 기본 자료형 배열은 이렇게 선언 할 수 있다.
  var arrBoolean: BooleanArray = booleanArrayOf(true, false, true, false)
  var arrInt: IntArray = intArrayOf(1, 2, 3, 4, 5)
  var arrDouble: DoubleArray = doubleArrayOf(1.2, 1.3, 1.4, 1.5)
  var arrChar: CharArray = charArrayOf('A', 'B', 'C')

  println("BooleanArray :: ${arrBoolean.contentToString()}")
  println("IntArray :: ${arrInt.contentToString()}")
  println("DoubleArray :: ${arrDouble.contentToString()}")
  println("CharArray :: ${arrChar.contentToString()}")

  // 생성자로 배열 생성
  var arrayStr = Array(10) { i -> "#${i.toString()}#" }
  println("ArrayStr :: ${arrayStr.contentToString()}")

조건문

  // 기본적인 if, else 문은 Java 와 동일 하니 생략

  var aa = "A"
  var bb = "B"

  // 코틀린은 삼항 연산자가 없다. 대신 if문은 삼항연산자 처럼 값을 할당하는데 사용할수 있다.
  var name:String = if(aa == bb) {
          "같은 사람"
      } else {
          "다른 사람"
      }
  println("같은 사람 일까요? 다른 사람 일까요? 답은 $name")

  // block 없이
  var nonBlockName:String = if(aa != bb) "다른 사람" else "같은 사람"
  println("같은 사람 일까요? 다른 사람 일까요? 답은 $nonBlockName")

  // When : Java 의 Switch-case 와 유사함
  // case 키워드가 생략되고, :(콜론) 대산 -> 를 사용해서 간단하게 사용 할 수 있음
  // multi line 처리가 필요하면 -> {} 중괄호 안에 여러줄을 처리할수 있음
  var number:Int = 3
  when(number){
    0 -> println("0입니다.")
    1 -> println("1입니다.")
    2 -> println("2입니다.")
    3 -> {
      println("3입니다.")
      println("Multi line 이 필요 하면 이렇게..")
      println("사용 하시면 됩니다.")
    }
    4 -> println("4입니다.")
    5 -> println("5입니다.")
    else -> println("일치하는 값이 없네요~")
  }

  // when 은 여러값 or Range or 배열에 포함 되는 지의 조건을 사용 할 수 있다.
  var arr:IntArray = intArrayOf(1,2,3)
  when(number){
    0,1 -> println("0 이거나 1인경우에..")
    in 4..6 -> println("4 ~ 6 범위에 속하는 경우")
    in arr -> println("arr 에 포함되는 값이 있는경우")
    !in arr -> println("arr 에 포함되는 값이 없는 경우")
    else -> println("아무 것도 해당 사항 없을때..")
  }

반복문

  // 코틀린의 for문은 for-each 문이다.

  var arr:Array<String> = arrayOf("A", "B", "C", "D", "E")
  for(value in arr){
  	println("For-Each : ${value}")
  }

  // Index를 사용하기
  for(idx in arr.indices) {
  	println("Index 사용해보기 : idx :: ${idx}, value: ${arr[idx]}")
  }

  // Range 사용하기
  for(idx in 5..10){
  	println("Range 사용해보기 : $idx")
  }

  // index 와 value 같이 사용하기
  for((idx, value) in arr.withIndex()){
  	println("Index와 Value 같이 사용하기 : idx : $idx , value : $value")
  }

  // While 은 Java와 동일하니 Skip

  // do-while Java와 동일, 차이점은 do-while 문 안에서 선언된 변수를 사용 할 수 있음

  // continue, break 는 Java와 동일, label 은 살짝 다름 :(콤마) 대산 @(at) 사용
  for(value in arr){
    label@ for(idx in 1..5){
    	println("Label Test value : ${value}, idx : ${idx}")
    	if(idx > 2){
   			break@label
    	}
  	}
  }

 

# Ls 명령어 사용시 Directory 색상 변경

Cent OS 5.X에서 Terminal로 접속해서 ls 명령어를 사용하면 directory가 파란색으로 나와 잘 보이지 않는다.

이때 해당 컬러를 다른색으로 변경하면 가독성이 좋아진다.

변경방법은

1. vi /etc/DIR_COLORS.xterm

2. DIR 01;34 # directory ==> 이부분을 다른색으로 변경하면 된다.

색상은 XX;YY;ZZ 로 3가지 속성을 가진다.

XX : Attribute Code : 00=none, 01=bold, 04=underscore, 05=blink, 07=reverse, 08=concealed

YY : Text Color Code : 30=black, 31=red, 32=green, 33=yellow, 34=blue, 35=magenta, 36=cyan, 37=white

ZZ : Background Color code : 40=black, 41=red, 42=green, 43=yellow, 44=blue, 45=magenta, 46=cyan, 47=white

 

ex) Directory를 노란색글자에 파란색 배경색에 볼드 체로 사용하고 싶으면

DIR 01;33;44 로 수정하면 된다.

 

# Vim editor의 색상 변경

vi 편집기를 사용 하다 보면 주석 같은 텍스트 색깔이 파란색으로 표시되면 잘 보이지 않는다.

이때 Vi 편집기 텍스트 색깔을 다른색으로 변경하면 가독성이 좋아진다.

 

우선 vi 편집기에서

:hi or :highlight 명령어로 현재 설정된 텍스트 색상을 확인한다.

 

Comment 가 term=bold cterm=bold ctermfg=4 라고 설정되어 있다

해당 속성은 아래와 같다

> term=color를 지원하지 않는 terminal에서 어떻게 보여줄건지.

> cterm=color를 지원하는 terminal에서 어떻게 보여줄건지.

> ctermfg=color를 지원하는 terminal에서 글자색을 어떤색으로 보여줄건지

> ctermbg=color를 지원하는 terminal에서 글자 배경색을 무슨색으로 보여줄건지

 

highlight 명령어로 현재 vi편집기 color를 변경할 순 있으나... 매번 힘드니...

사용자가 로긴 할때 바로 적용 가능 하도록 /ect/vimrc 파일에 직접 추가했다.

 

/etc/vimrc 맨 밑에 아래와 같이 추가했다. 주석 색상을 초록색으로...

hi Comment term=bold ctermfg=2 로