Kotlin을 시작해보자. (상속)
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()
}
Kotlin을 시작해보자. (Lambda, Class)
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()
}
Kotlin을 시작해보자. (예외처리, 함수)
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]
}
Kotlin을 시작해보자. (변수선언, 자료형, 조건문, 반복문)
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
}
}
}
Linux terminal directory color 및 Vim editor text color 변경
# 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 로