함수 정의와 호출 
코틀린 컬렉션 
1 2 3 4 5 6 7 8 9 val  set  = hashSetOf(1 , 7 , 53 )val  list = listOf(1 , 7 , 53 )val  map = hashMapOf(1  to "one" , 7  to "seven" , 53  to "fifty-three" )fun  main (args: Array <String >)     println(set .javaClass)	     println(list.javaClass)       println(map.javaClass)   } 
자바 컬렉션을 활용한 코틀린 컬렉션  
자바보다 더 많은 기능을 쓸 수 있음 
 
1 2 3 4 val  strings = listOf("first" , "second" , "fourteenth" )println(strings.last())	 val  numbers = setOf(1 , 14 , 2 )println(numbers.max())	 
last(), max() 는 코틀린에서 편의를 위해 추가한 확장함수  
직관적인 함수 호출 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 fun  <T>  joinToString (         collection: Collection <T >,         separator: String ,         prefix: String ,         postfix: String  )     val  result = StringBuilder(prefix)     for  ((index, element) in  collection.withIndex()) {         if  (index > 0 ) result.append(separator)         result.append(element)     }     result.append(postfix)     return  result.toString() } fun  main (args: Array <String >)     val  list = listOf(1 , 2 , 3 )     val  alphabets = listOf("a" , "b" , "c" )     println(joinToString(list, separator = "; " , prefix = "(" , postfix = ")" ))     println(joinToString(alphabets, separator = "; " , prefix = "(" , postfix = ")" ))           } 
Java 
1 joinToString(collection,  "" ,  "" ,  "." ); 
Kotlin 
1 joinToString(collection, prefix = "(" , separator = ";" , postfix = ")" ) 
차이점?  파라미터 이름이 같다면 순서를 바꿔도 가능!주의!  자바에서 작성한 코드를 코틀린에서 호출한 경우에는 인자 이름 명시 불가능!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 fun  <T>  joinToString (     collection : Collection <T >,         separator: String  = ", " ,         prefix: String  = "" ,         postfix: String  = ""  )     ... }    fun  main (args: Array <String >)     val  list = listOf(1 , 2 , 3 )     println(joinToString(list))	          println(joinToString(list, separator = ";" ))	          println(joinToString(list, separator = "; " , prefix = "(" ))	          println(joinToString(list, separator = "; " , prefix = "(" , postfix = ")" ))          } 
값을 지정하지 않은 모든 인자는 디폴트 값을 적용 
 
주의!    자바에는 디폴트 파라미터 값이라는 개념이 없기때문에 코틀린 함수를 자바에서 호출하는 경우
최상위 함수와 프로퍼티 
1 2 3 4 5 package  strings;public  class  JoinKt 	public  static  String joinToString (...)  } 
1 2 3 package  stringsfun  joinToString (...) 
1 2 3 4 import  strings.JointKt; ...  JoinKt.joinToStirng(list,"," ,"" ,"" ); 
1 2 3 @file:JvmName ("StringFunctions" )	package  strings
  1 2 3 import  strings.StringFunctions;StringFunctions.joinToString(list, "," , "" , "" ); 
UtilClass를 대신해서 사용할 수 있다는 장점이 있다고 했는데, Util이 더 편하지않나…? 
 
1 2 3 4 5 6 var  cnt;		fun  performOperation () 	cnt++ } ... 
프로퍼티를 클래스보다 더 이전에 위치 
최상위 프로퍼티도 접근자 메소드를 통해 접근가능 
const 변경자를 추가하면 public static final 필드로 만들 수 있다. 
확장함수와 확장 프로퍼티 
1 2 3 fun  String.lastChar () Char  = this .get (this .length - 1 )println("Kotlin" .lastChar()) 
수신객체타입(Receiver Type): 확장이 정의될 클래스 ex) String 
수신객체(Receiver Object): 위 클래스의 인스턴스 객체 ex)“Kotlin”, this 
확장함수 안에서는 private나 protected 접근 불가능, 해당 클래스의 public함수에만 접근 가능 
확장함수의 Import 
확장함수를 사용하기 위해서는 import를 통해서 사용 
 
1 2 3 import  strings.lastChar		val  c = "Kotlin" .lastChar()
주의!  : 한 클래스 내의 같은 이름의 확장함수가 둘 이상일 경우 이름이 충돌할 수 있으니 as를 사용
1 2 import  strings.lastChar as  lastval  c = "Kotlin" .last()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 fun  <T>  Collection<T> .joinToString (         separator: String  = ", " ,         prefix: String  = "" ,         postfix: String  = ""  )     val  result = StringBuilder(prefix)     for  ((index, element) in  this .withIndex()) {	         if  (index > 0 ) result.append(separator)         result.append(element)     }     result.append(postfix)     return  result.toString() } fun  Collection<String> .join (         separator: String  = ", " ,         prefix: String  = "" ,         postfix: String  = ""  ) fun  main (args: Array <String >)     println(listOf("one" , "two" , "eight" ).join(" " ))	 } 
내부적으로 확장함수는 수신객체 를 첫 번째 인자 로 받음 
자바에서 호출시 수신객체 를 첫 번째 인자로 넘겨줘야함 
 
확장함수의 오버라이드 
1 2 3 4 5 6 7 8 9 10 11 12 open  class  View      open  fun  click () "View clicked" )	 } class  Button : View     override  fun  click () "Button clicked" ) } fun  main (args: Array <String >)     val  view: View = Button()     view.click()	 } 
확장 함수가 정적 메소드와 같은 특징을 가지므로 확장함수를 하위 클래스에서 오버라이드 할 수 없다. 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 open  class  View      open  fun  click () "View clicked" )	 } class  Button : View     override  fun  click () "Button clicked" ) } fun  View.showOff () "I'm a view!" )		fun  Button.showOff () "I'm a button!" )	fun  main (args: Array <String >)     val  view: View = Button()     view.showOff()	 } 
확장 함수는 클래스의 밖에 선언되며 확장함수 호출 시, 수신 객체로 지정한 변수의 정적 타입에 의해 어떤 확장함수가 올 지 결정되는 것이지, 변수의 객체에 저장된 동적인 타입에 의해 확장 함수가 결정되지 않는다. 
 
<그림 3.2 참고>
TODO - 알고가자!
open class : 코틀린에서의 모든 클래스는 암시적으로 상위 클래스로 Any클래스를 가지는데,명시적으로 상위클래스를 지정하기 위해서는 open을 명시멤버메서드? 정적메서드? 확장메서드? 
특정 클래스에 추가된 확장함수가 멤버함수와 이름이 같다면 우선순위는 멤버함수 > 확장함수  
 
확장 프로퍼티 
1 2 3 4 5 6 7 8 val  String.lastChar : Char 	get () = get (length - 1 )		 var  StringBuilder.lastChar : Char 	get () = get (length - 1 ) 	set (value : Char ){         this .setChartAt(length - 1 , value)     } 
일반 프로퍼티와 같은데 대신 수신객체클래스 가 추가 되었을 뿐 
확장 프로퍼티는 backing field를 가지고 있지않으니 최소한 getter()는 구현해야 함. 
 
컬렉션 처리 
1 2 3 4 val  numbers = listOf(1 ,2 ,3 ,4 ,5 )val  strings = listOf("1" , "2" , "3" )fun  listOf<T> (vararg  var : T ) 
1 2 3 4 5 6 7 8 9 fun  printNumbers (vararg  numbers: Int )     for  (number in  numbers) {         println(number)     } } val  numbers = intArrayOf(1 , 2 , 3 )printNumbers(*numbers) printNumbers(10 , 20 , *numbers, 30 , 40 ) 
자바의 스프레드 연산자: ... / 코틀린의 스프레드 연산자 * 
배열(Array)을 함수에 전달할 수 있다. 
중위 호출 
 
1 infix  fun  Any.to (other : Any ) this , other)    
data class 형태로 나오는 여러 값을 동시에 반환할 수 있다. 
자동으로 componentN함수가 생성됨. 
 
1 2 3 for  ((index, element) in  this .withIndex()) {	     println("$index : $element " ) } 
1 2 3 4 5 6 7 8 9 10 11 data  class  Size val  width : Int , val  height : Int )fun  main ()     val  size = Size(10 , 20 )     val  (w, h) = size     val  a = size.component1()     val  b = size.component2()          println(a)      println(b)  } 
문자열과 정규식 
1 2 3 public  inline  fun  CharSequence.split (regex: Regex , limit: Int  = 0 ) this , limit)public  fun  CharSequence.split (vararg  delimiters: String , ignoreCase: Boolean  = false , limit: Int  = 0 ) 
1 2 3 4 println("12.345-6.A" .split("\\.|-" .toRegex()))		 println("12.345-6.A" .split("." , "-" ))			     
마침표 . 를 문자로 읽기위해 \\.을 사용하여 escape 시킴 
코틀린에서는 split()의 확장함수를 제공함으로써 Java의 와일크카드 문자(?)로 인한 혼동을 피함.
 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 fun  parsePath (path: String )     val  regex = """(.+)/(.+)\.(.+)""" .toRegex()                    val  matchResult = regex.matchEntire(path)     if  (matchResult != null ) {         val  (directory, filename, extension) = matchResult.destructured                                    println("Dir: $directory , name: $filename , ext: $extension " )     } } fun  main (args: Array <String >)     parsePath("/Users/yole/kotlin-book/chapter.adoc" ) } 
3중 따옴표를 사용하면, \, \n등을 포함한 어떤 문자도 따로 escape  시킬 필요가 없다. 
주의!  : 역슬래쉬(\)나 줄바꿈(\n)등을 3중 따옴표 안에 넣으면 문자로 인식해서 그냥 출력된다. 
중첩함수(Local method) 
함수에서 추출한 함수를 원 함수 내부에 중첩시키는 것을 말함. 
코드중복을 보여주는 예제  //아직 중첩함수 X 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class  User val  id: Int , val  name: String, val  address: String)fun  saveUser (user: User )     if  (user.name.isEmpty()) {					         throw  IllegalArgumentException(             "Can't save user ${user.id} : empty Name" )     }     if  (user.address.isEmpty()) {				         throw  IllegalArgumentException(             "Can't save user ${user.id} : empty Address" )     }      } fun  main (args: Array <String >)     saveUser(User(1 , "" , "" ))      } 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class  User val  id: Int , val  name: String, val  address: String)fun  saveUser (user: User )     fun  validate (user: User ,				                   value: String ,                  fieldName: String )         if  (value.isEmpty()) {             throw  IllegalArgumentException(                 "Can't save user ${user.id} : empty $fieldName " )         }     }     validate(user, user.name, "Name" )     validate(user, user.address, "Address" )      } fun  main (args: Array <String >)     saveUser(User(1 , "" , "" )) } 
중첩함수는 자신이 속한 바깥함수의 모든 파라미터와 변수를 참조 가능 
따라서 validate의 파라미터인 user까지 제거 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class  User val  id: Int , val  name: String, val  address: String)fun  saveUser (user: User )     fun  validate (value: String , fieldName: String )          if  (value.isEmpty()) {             throw  IllegalArgumentException(                 "Can't save user ${user.id} : "  +                     "empty $fieldName " )         }     }     validate(user.name, "Name" )							     validate(user.address, "Address" )					      } fun  main (args: Array <String >)     saveUser(User(1 , "" , "" )) } 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class  User val  id: Int , val  name: String, val  address: String)fun  User.validateBeforeSave ()     fun  validate (value: String , fieldName: String )          if  (value.isEmpty()) {             throw  IllegalArgumentException(                "Can't save user $id : empty $fieldName " )	         }     }     validate(name, "Name" )     validate(address, "Address" ) } fun  saveUser (user: User )     user.validateBeforeSave()      } fun  main (args: Array <String >)     saveUser(User(1 , "2" , "" )) } 
TODO - 생각해보자!
좋은 구조인가??과유불급 이라고 중첩된 함수의 깊이가 깊어지면 깊어질수록 코드 읽기가 어려우니한 단계 만 함수를 중첩시키는 것을 권장한다.
참고