올해는 머신러닝이다.
코틀린 빠르게 훝어보기 #1 본문
코틀린은 다음과 같은 길이를 가지고 있다.
Type | Width |
---|---|
Long | 64 |
Int | 32 |
Short | 16 |
Byte | 8 |
Double | 64 |
Float | 32 |
테스트를 해보자
val int = 123
val long = 123456L
val double = 12.34
val float = 12.34F
val hexadecimal = 0XAB
val binary = 0b01010101
println( "$int , $long , $double , $float , $hexadecimal , $binary")
==================
123 , 123456 , 12.34 , 12.34 , 171 , 85
String 멀티 라인
val rawString = """
안녕하세요
반갑습니다.
코틀린입니다
"""
print(rawString)
==============
안녕하세요
반갑습니다.
코틀린입니다
Arrays
val array = arrayOf( 1 ,2 ,3)
var perfectSquares = Array(10 , { k -> k * k}) // 0(k)부터 시작해서 10개의 값을 가져온다.
println(Arrays.toString(array))
println(Arrays.toString(perfectSquares))
=====================
[1, 2, 3]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
String 템플릿
- $변수 , ${변수} 가 가능하다..
var name = "Tommy"
var str = "Hello $name , Your name has ${name.length} characters"
println( name )
println( str )
===========
Tommy
Hello Tommy , Your name has 5 characters
Range
val aToZ = "a".."z";
val isTrue = "c" in aToZ
val oneToNine = 1..9
val isFalse = 11 in oneToNine
println( aToZ )
println( oneToNine )
println( " c is in aToZ = $isTrue , 11 is in oneToNine = $isFalse" )
==========
a..z
1..9
c is in aToZ = true , 11 is in oneToNine = false
추가적으로 .downTo()
.rangeTo()
로 지정도 가능하다.
- downTo()
- rangeTo()
- step (띄워넘기)
- reversed
val counttingDown = 100.downTo(0)
val rangeTo = 10.rangeTo(20)
println( counttingDown )
println( rangeTo )
val oneToFifty = 1..50
val oddNumbers = oneToFifty.step(2)
val isIncludeNegativeNumber = 2 in oddNumbers
val isIncludePositiveNumber = 1 in oddNumbers
println( " 짝수 포함여부 : $isIncludeNegativeNumber , 홀수 여부 $isIncludePositiveNumber" )
val isIncludePositiveNumberReversed = 2 in (2..100).step(2).reversed()
println( " 뒤집었을 경우 짝수 포함여부 : $isIncludePositiveNumberReversed" )
==============
100 downTo 0 step 1
10..20
짝수 포함여부 : false , 홀수 여부 true
뒤집었을 경우 짝수 포함여부 : true
Loop
- for in
- array.indices -> index 가져올 수 있
val oneToTen = 1..2
for(k in oneToTen){
for(j in 1..2){
println(k * j)
}
}
val array = arrayOf("A" , "B")
for(index in array.indices) { //인덱스를 가져올 수 있다.
println("Element $index is ${array[index]}")
}
=============
1
2
2
4
Element 0 is A
Element 1 is B
Expression & Scope
- @ 을 체크
fun testExpression() {
Building("서울").Reception("02").printAddress()
}
class Building(val address : String) {
inner class Reception(private val telephone : String) {
fun printAddress() = println("${this@Building.address} , ${this.telephone} ")
}
}
===========
서울 , 02
When
- java 의 switch -> when 으로 사용가능하다.
fun testZeroFromWhen(){
print(" ${testWhen( Int.MIN_VALUE )} , ${testWhen( Int.MAX_VALUE ) }" )
}
fun testWhen( x : Int) : Boolean{
return when(x){
Int.MIN_VALUE -> true
Int.MAX_VALUE -> true
else -> false
}
}
//리스트안에 있는 체크
fun isInNumber(x : Int): Boolean {
return when(x) {
in listOf( 1, 2, 3, 4, 5) -> true // 리스트값 체크 가
else -> false
}
}
//when 에 인자가 없는 경
fun checkNoWhen( x : Int , y : Int) {
when {
x < y -> println("x is less then y")
x > y -> println("x is greater than y")
else -> println("x must equal y")
}
}
라벨 정하기
fun printUntilStop(){
val list = listOf( "a" , "b" , "stop" , "d")
list.forEach stop@{
if(it == "stop") return@stop
else println(it)
}
list.forEach {
if (it == "stop") return@forEach
else println(it)
}
}
========
a
b
d <-- 주의
a
b
d <-- 주의
클래스정의시 생성자 넣는 방법
require
로 조건 체크 가능.. 오류시 ->java.lang.IllegalArgumentException
발생시킴
fun main(args : Array<String>){
val person1 = Person( "" , "kim" , 20) // java.lang.IllegalArgumentException 발생
val person2 = Person( "tommy" , "kim" , 20)
print("person value = ${person1.firstName} , ${person2.lastName} , ${person1.age}")
}
class Person(val firstName : String , val lastName : String , val age : Int?){
init{
require( firstName.trim().length > 0 ) { "Invalid firstName argument."}
require(lastName.trim().length > 0) { "Invalid lastName argument" }
if(age != null) {
require( age > 0 && age < 150) { "Invalid age argument" }
}
}
}
==============
코틀린에서 값을 가져올때
val person = Person( "tommy" , "kim" , 20)
print("person value = ${person.firstName} , ${person.lastName} , ${person.age}")
//위와 같이 접근이 가능하다.
그럼 자바에서는 코틀린의 Person 값을 어떻게 가져올까?
Person person = new Person("tommy" , "kim" , 26);
System.out.println(String.format("person firstName -> %s" , person.getFirstName()));
// 위와 같이 getter 로 접근 가능하다.
만약 생성자에서 nullSafe 가 있는 경우 어떻게 될까?
- constructor : this 유
fun main(args : Array<String>){
val person = Person( "tommy" , "kim" ) // 인자가 2개인걸 유의
print("person value = ${person.firstName} , ${person.lastName} , ${person.age}")
}
class Person(val firstName : String , val lastName : String , val age : Int?){
//생성자에서 null로 따로 치환해줘야 한다. ( this(...) )
constructor( firstName: String , lastName: String) : this(firstName , lastName , null)
init{
require( firstName.trim().length > 0 ) { "Invalid firstName argument."}
require(lastName.trim().length > 0) { "Invalid lastName argument" }
if(age != null) {
require( age > 0 && age < 150) { "Invalid age argument" }
}
}
}
내부 변수 초기화시 다른 방법으로도 가능하다.
fun main(args : Array<String>){
val person2 = Person2( "tommy" , "kim" , null)
print("person value = ${person2.getName()} , ${person2.getAge()}")
}
class Person2(firstName: String , lastName: String , howOld : Int?) {
private val name : String
private val age : Int?
init {
this.name = "$firstName,$lastName" //이렇게 초기화 가능
this.age = howOld
}
fun getName() : String = this.name //함수 초기화가 이런식이 가능함
fun getAge() : Int? = this.age
}
중첩 클래스
중첩시 내부 클래스가 가능하다. 내부 클래스에서 외부 클래스의 변수 접근을 위해선 inner를 붙여야 한다.
- Inner
fun main(args : Array<String>){
Line("ttt").InnerLine(2).draw()
}
class Line(graphName : String){
private val name : String
init{
name = graphName
}
inner class InnerLine(val x : Int) {
fun draw() : Unit {
//$name 사용시 inner를 없는 경우 컴파일 오류
print("Drawing Line from $x , name -> $name")
}
}
}
중첩시 라벨을 통해서 같은 변수를 지정이 가능하다.
UI에서 뷰 변수를 지정시 많이 사용한다고 한다.
- @A , @B 를 유의해서 보자.
fun main(args : Array<String>){
A().B().foo("")
}
class A {
private val somefield : Int = 1
inner class B {
private val somefield : Int = 2
fun foo(s : String) {
println("Field <somefield from B : " + this.somefield)
println("Field <somefield from B : " + this@B.somefield)
println("Field <somefield from A : " + this@A.somefield)
}
}
}
=============
Field <somefield from B : 2
Field <somefield from B : 2
Field <somefield from A : 1
Enum
Enum은 기본형태는 자바와 같다.
fun testEnum(){
println(Day.valueOf("MONDAY"))
println( Arrays.toString(Day.values()))
}
enum class Day(val weather : String) {
MONDAY("good") , TUESDAY("rainy")
}
=============
MONDAY
[MONDAY, TUESDAY]
Interface
로 확장도 가능하다.
fun testEnum(){
val w = Word.HELLO
w.print()
}
interface Printable {
fun print()
}
enum class Word : Printable {
HELLO {
override fun print() {
println("Word is HELLO")
}
},
BYE {
override fun print() {
println("Word is BYE")
}
}
}
==========
Word is HELLO
Static 으로 접근
자세한 설명은 해당 링크로 가자.
fun main(args : Array<String>) {
print(Student.create("tommy").name)
}
interface StudentFactory {
fun create(name : String) : Student
}
class Student private constructor(val name : String) {
companion object : StudentFactory{
override fun create(name: String): Student {
return Student(name)
}
}
}
Interface
fun main(args : Array<String>) {
val pngImage = PNGImage()
pngImage.save()
}
open class Image{
open fun save() {
println("Image save called")
}
}
interface VendorImage{
fun save() {
println("VendorImage save called")
}
}
class PNGImage : Image() , VendorImage {
override fun save(){
super<VendorImage>.save() //제너릭으로 해당 부모를 지정하는 것에 유의
super<Image>.save() //제너릭으로 해당 부모를 지정하는 것에 유의
}
}
확장함수
함수를 통해서 원하는 클래스에 추가적으로 붙일 수 있다.
우선 확장 안된 함수를 보자.
fun main(args : Array<String>){
val items = listOf( 1 , 3, 7 , 9, 100)
val newItems = drop( 2 , items)
println( newItems )
}
fun <E> drop(k : Int , list : List<E>) : List<E> {
val resultSize = list.size - k
when {
//resultSize 가 0보다 작으면 빈 리스트 변환
resultSize <= 0 -> return emptyList<E>()
//그렇지 않을 경우 k번째 아이템을 제거하고 새로운 리스트를 리턴받는다.
else -> {
val newList = ArrayList<E>(resultSize)
for(index in k..list.size - 1) {
newList.add(list[index])
}
return newList
}
}
}
==========
[7, 9, 100]
실행시 별도의 drop으로 함수를 호출해서 해야 한다.
그럼 이걸 이제 확장 함수로 바꿔보자.
fun main(args : Array<String>){
val items = listOf( 1 , 3, 7 , 9, 100)
val newItems = items.drop( 2 , items) // 컬렉션 변수뒤에 붙는 걸 유의해서 보자.
println( newItems )
}
fun <E> List<E>.drop(k : Int , list : List<E>) : List<E> {
val resultSize = list.size - k
when {
//resultSize 가 0보다 작으면 빈 리스트 변환
resultSize <= 0 -> return emptyList<E>()
//그렇지 않을 경우 k번째 아이템을 제거하고 새로운 리스트를 리턴받는다.
else -> {
val newList = ArrayList<E>(resultSize)
for(index in k..size - 1) {
newList.add(this[index]) //this를 주의
}
return newList
}
}
}
==========
[7, 9, 100]
위와 같이 내부적으로 this를 사용해서 해당 컬렉션을 가져오는 걸 볼수 있다.
순서와 매개변수에 따른 함수 호출
같은 이름과 같은 매개변수의 함수의 경우 ordering이 다를 수 있다. 다음의 예를 보자.
class Apple {
fun eat() : Unit {
println("Apple eat")
}
fun drop() : Unit {
println("Apple drop")
}
}
fun Apple.eat(): Unit {
println("another Apple eat");
}
fun Apple.drop(where : String) : Unit {
println("apple drop at $where")
}
실행시
val apple = Apple()
apple.eat() //<- 처음 함수만 실행
apple.drop()
apple.drop("school")
===========
Apple eat
Apple drop
apple drop at school
Null 오브젝트의 확장함수
null safe 오브젝트의 확장함수는 다음과 같다.
val test : String? = "222"
val test2 : String = "222"
var test3 : String? = null
val isEqual = test?.safeEquals(test2)
val isEqual2 = test3?.safeEquals(test2)
println(isEqual)
println(isEqual2)
//확장 함수
fun Any?.safeEquals(other : Any?) : Boolean {
if (this == null && other == null) return true
if (this == null) return false
return this.equals(other)
}
===============
true
null
확장함수내에 맴버 변수 사용하기
확장함수를 사용시 클래스 내에 맴버변수를 같이 연동시 어떻게 하는 지 살펴보자.
- 내부적으로 사용되는
hascode()
를 통해서 어떤 객체의 값을 가져오는지를 판별하자.
fun main(args : Array<String>){
val mapping = Mappings()
val inputStr = "Add one"
println("hasCode #1-1 ${inputStr.hashCode()}")
mapping.add(inputStr)
mapping.add2("Add two")
println("hasCode #2-1 ${mapping.hashCode()}")
print(mapping.get(mapping.hashCode()))
}
class Mappings {
private val map = hashMapOf<Int, String>()
//확장 함수내부에 맴버변수를 사용가능하다.
private fun String.stringAdd() : Unit {
val hasCode = hashCode() //<- String 값에 대한 hascode
println("hasCode #1-2 $hasCode")
map.put( hasCode, this)
}
private fun String.stringAdd2() : Unit {
val hasCode = this@Mappings.hashCode() //Mappings 클래스에 대한 hascode
println("hasCode #2-2 $hasCode")
map.put( hasCode , this)
}
fun add(str : String) : Unit = str.stringAdd()
fun add2(str : String) : Unit = str.stringAdd2()
fun get(key : Int) : String? = map.get(key)
}
=============
hasCode #1-1 514527815
hasCode #1-2 514527815
hasCode #2-2 1360875712
hasCode #2-1 1360875712
Add two
다중 반환 값
Pair
에 대해서 알아보자
fun main(args : Array<String>) {
val positiveRootVal = positivieRoot(4)
val negativeRootVal = negativeRoot(4)
print("$positiveRootVal , $negativeRootVal");
}
fun positivieRoot(k : Int) : Double {
require( k >= 0)
return Math.sqrt(k.toDouble())
}
fun negativeRoot(k : Int) : Double {
require( k >= 0)
return -Math.sqrt(k.toDouble())
}
=========================
// 2.0 , -2.0
위 내용을 보면 주어진 값의 제곱근을 양수와 음수로 보여준다.
중복된 내용이 많이 보인다. 이걸 다시 리펙토링 하면
fun main(args : Array<String>) {
val roots = roots(5)
print("positive -> ${roots[0]} , ${roots[1]}")
}
fun roots(k: Int) : Array<Double> {
require( k >= 0 )
val root = Math.sqrt( k.toDouble() )
return arrayOf(root , -root)
}
================
//2.23606797749979 , -2.23606797749979
이렇게 배열로 하나 만들수 있다. 하지만 어느쪽이 양수인지를 알수가 없다.
그러면 Pair로 바꾸면 다음과 같다.
fun main(args : Array<String>) {
val roots = roots(5)
print("positive -> ${roots.first} , ${roots.second}")
}
fun roots(k: Int) : Pair<Double , Double> {
require( k >= 0 )
val root = Math.sqrt( k.toDouble() )
return Pair(root , -root)
}
그럼 이걸 좀 더 줄이면
val (pos , neg) = roots(5) //!!
print("positive -> $pos , $neg")
이런식으로 줄일수 있다.
그리고 중위 함수(Infix)
로 바꿀수 있다
연산자
코틀린도 Swift
처럼 연산자를 오버로딩이 가능하다.
fun main(args : Array<String>) {
val board = Chessboard()
board[0 , 4] = Piece.Queen
println(board[0,4])
}
enum class Piece {
Empty , Pawn, Bishop , Queen
}
class Chessboard() {
private val board = Array<Piece>(64 , {Piece.Empty})
operator fun get(rank : Int, file : Int) : Piece = board[cal(rank , file)]
operator fun set(rank : Int , file: Int , value : Piece) : Unit {
println("set -> $rank , $file , $value , ${file * 8 + rank}")
board[cal(rank , file)] = value
println("check empty board -> ${board[cal( 0,2)]}") // 0, 2를 입력해서 빈값이 나오는지 확인
}
fun cal ( rank : Int , file : Int) : Int = file * 8 + rank
}
========
set -> 0 , 4 , Queen , 32
check empty board -> Empty
Queen
배열을 set , get을 하기위한 연산자를 오버로딩이 가능하다.
그리고 함수 호출 이름을 연산자로도 사용이 가능하다.
클래스를 함수처럼 사용도 가능하다.
fun main(args : Array<String>) {
val longs = RandomLongs(3)
println("${longs()} , ${longs()} , ${longs()}")
}
class RandomLongs(seed : Long) {
private val random = Random(seed)
operator fun invoke() : Long = random.nextLong()
}
========
-4961115986754665064 , 1309571695633557482 , 1238145679872042884
또 다른 예제로는 다음과 같다.
여러개의 함수를(생성자가 아니다) 만들어서 그에 맞는 걸 실행도 가능하다.
fun main(args : Array<String>) {
val Min1 = Min(1, 2, 3)
val Min2 = Min(1L, 2L)
print("$Min1 , $Min2")
}
object Min {
operator fun invoke(a: Int, b: Int): Int = if (a <= b) a else b
operator fun invoke(a: Int, b: Int, c: Int): Int = invoke(invoke(a, b), c)
operator fun invoke(a: Int, b: Int, c: Int, d: Int): Int = invoke(invoke(a, b), invoke(c, d))
operator fun invoke(a: Long, b: Long): Long = if (a <= b) a else b
operator fun invoke(a: Long, b: Long, c: Long): Long = invoke(invoke(a, b), c)
operator fun invoke(a: Long, b: Long, c: Long, d: Long): Long = invoke(invoke(a, b), invoke(c, d))
}
=========
1 , 1
비교 연산 할때도 자주 쓰인다.
fun main(args : Array<String>) {
val tommy = BingoNumber(30)
val kim = BingoNumber(24)
println( tommy < kim )
println( kim < tommy )
}
class BingoNumber(val age: Int) {
operator fun compareTo(other : BingoNumber) : Int {
return when {
age < other.age -> -1 //age 가 other의 나이보다 적다면 -1
age > other.age -> 1 //크다면 1
else -> 0 //기타 0
}
}
}
=======
false
true