들어가며

코틀린은 이미 정의된 클래스를 확장 시킬 수 있는 기능이 있다. 예를 들어서 아래와 같이 String 클래스에 원하는 메서드를 추가 할 수 있다.

fun String.getFirst() = this[0]
println("hello".getFirst())
//print 'h'

원리

원리는 생각보다 간단하다. 자바에서 static을 사용하여 기존 클래스를 입력받아 처리하는 메서드를 생성함으로써 사용자 입장에서는 기존의 클래스를 확장시킨 것처럼 보이게 하는 것이다.

public static String getFisrt(String string){ return string[0];}

Question?

그렇다면 만약 상속 관계에 있는 두 개의 클래스를 이용하여 아래와 같이 코드를 작성하면 어떤 문자열이 출력될까?

open class Parent 
class Child: Parent()

fun Parent.foo() = "parent" 
fun Child.foo() = "child"

fun main(args: Array<String>) { 
  val parent: Parent = Child()
  println(parent.foo()) 
// 여기서 출력되는건 무엇일까?
}

Answer & Why

원리를 알고있다면 추론하는 것은 간단하다. 위 코드가 자바로 변환 되었다고 생각해보자. static을 이용하므로 아래와 같이 변환 되었을 것이다.

//... 클래스 정의는 생략

public static String foo(Parent parent){return "parent";)
public static String foo(Child child){return "child";)

public static void main(String[] args){
	Parent parent=new Child();
	System.out.println(foo(parent));
}

자바 공부를 열심히 했다면 결과는 "parent"가 출력되었을 것이라고 바로 알았을 것이다. 자바는 오버로딩을 통해서 어규먼트와 파라미터가 일치하는 함수를 호출하기 때문이다. 위 코드를 보면 오버라이딩을 통해서 "child"가 출력될수 있다고 착각할 수있다. 그러나 확장함수는 인스턴스의 실제 값과는 관련없이 클래스에만 선언되기 때문에 함수를 호출할 때 인스턴스의 클래스 종류만을 따른다.

Question2 : Member vs Extension

자 그럼 만약 확장함수를 기존 클래스의 멤버 메소드를 오버라이딩 하는 것처럼 사용하면 어떻게 될까?

class A{
	fun foo()="member"
}
fun A.foo()="extension"

println(A().foo()) //??

Answer & Why

정답은 "member"를 출력한다. 왜냐하면 코틀린은 확장함수가 멤버 함수를 오버라이딩 하는 것을 허용하지 않는다. 왜 그럴까? 만약 반대로 허용한다고 생각해보자, 코드를 작성 할때 전역에서 확장함수로 Array.get()을 새로 정의 했다고 하자. 추후 다른 사람이 이 파일에 새로운 코드를 추가한다고 했을 때, get()을 본래의 의도로 사용하여 작성한다면 코드는 버그 투성이가 될 것이고 이를 인지하고 본래의 함수를 사용하고자 해도 확장함수로 정의된 이상 불가능하다.

하지만 코틀린은 확장함수로 오버로딩을 하는 것은 허용한다. 이는 기존의 멤버 메소드에 영향을 끼치지 않기 때문이다.

class A{
	fun foo()="member"
}
fun A.foo(i:Int)="extension $i"

println(A().foo(2)) //extension 2

 

Basics

Extension Function은 클래스를 확장한다. 클래스 밖에서 정의 되지만 regular 멤버로 클래스 내부에서 호출할 수 있다.

fun String.getLast(number: Int) = this.get(this.length-1)
class Test{
 fun main(){
        val c: Char ="abc".getLast()
    }
}

Receiver

확장 함수가 호출 될 때는 this가 reveiver로서 호출된다. 또한 기본적으로 this의 멤버 변수나 함수를 호출하기 위해서 일일이 this를 정의할 필요가 없다.

fun String.getLast(number : Int)=get(length()-1)

Import

extension을 이용하기 위해선 import를 이용해서 명시해주어야 한다. 왜냐하면 extension function은 기본적으로 전체프로젝트에서 사용할 수 없도록 되어있기 때문이다.

fun String.lastChar()=get(length-1)

import com.example.util.lastChar
val c: Char="abc".lastChar()

Calling Extension Functions from Java code

자바에서 코틀린의 확장함수를 호출할때는 static형태로 호출된다. 이 또한 import를 통해서 static한 함수만을 호출 할 수 있다.

//StringExtension.kt
fun String.lastChar()=get(length-1)

//JavaClass.java
import static StringExtensionKt.lastChar;
char c=lastChar("abc");
//or
char c=StringExtensionKt.lastChar("abc");

위에서 보듯 자바에서 확장함수를 호출할때는 첫 번째 파라미터로 리시버를 입력한다. 확장함수가 추가적인 파라미터가 필요하다면 자바에서는 두 번째 파라미터부터 입력하면 된다.

//StringExtension.kt
fun String.getChar(pos : Int)=get(pos)

//JavaClass.java
import static StringExtensionKt.getChar;
char c=getChar("abc",2);

Accessing private members

자바에서는 다른 클래스의 static 함수에서 private member를 호출할 수 없다.

코틀린의 확장함수는 분리된 보조 클래스에서 정의된 static 함수이다.

따라서 확장함수에서는 private member를 호출 할 수 없다.

Reference

Examples from the Standard Library - Starting up with Kotlin | Coursera

들어가며

코틀린의 패키지 호출은 파이썬과 동일하므로 이를 안다면 굳이 아래의 내용을 볼 필요가 없습니다.

1.Packages

 패키지는 다른 소스파일의 패키지를 호출하는 것을 의미합니다. 사용법은 간단하게 package로 호출하면 됩니다.

package org.example

fun printMessage() { /*...*/ }
class Message { /*...*/ }

// ...

2.Imports

import지시어를 통해서 패키지 내부의 클래스를 따로 호출 할 수 있습니다.

import org.example.Message // Message is now accessible without qualification

import org.example.* // everything in 'org.example' becomes accessible

만약 클래스명이 충돌할 경우 이를 대체 할 수 있습니다. 

import org.example.Message // Message is accessible
import org.test.Message as testMessage // testMessage stands for 'org.test.Message'

 

'Android > Kotlin' 카테고리의 다른 글

Calling Extension  (0) 2021.08.19
[Kotlin] Extension Function  (1) 2021.07.19
Kotlin - 1. 기본 타입(numbers, characters, booleans, arrays, and strings)  (0) 2020.03.27

들어가며

이번 글에서는 처음으로 코틀린을 공부해보는 만큼 코틀린에 쓰이는 기본타입(numbers, characters, booleans, arrays, and strings)에 대해서 알아보도록 하겠습니다. 

 

1. Numbers

1. 정수

1. 파이썬, javascript처럼 변수에 따로 타입을 선언하지 않음

2. 일반적인 정수를 저장하면 Int로 설정

3. Int의 범위 밖이나 뒤에 L을 추가하면 Long으로 설정

4. 변수이름 : 타입 = ? 구문을 통해서 직접 타입 설정 가능 

val one = 1 // Int
val threeBillion = 3000000000 // Long
val oneLong = 1L // Long
val oneByte: Byte = 1

2. 실수

val pi = 3.14 // Double
val e = 2.7182818284 // Double
val eFloat = 2.7182818284f // Float, actual value is 2.7182817

1. 일반 실수 입력시 Double로 설정

2. 뒤에 F또는 f 추가시 float으로 설정되고 범위 밖의 데이터는 소멸

3. 독립성

코틀린은 일반적인 언어처럼 숫자 파라미터가 포괄적인게 아니라 해당 파라미터만 수용가능, 그렇지 않을 경우 에러 발생

fun main() {
    fun printDouble(d: Double) { print(d) }

    val i = 1    
    val d = 1.1
    val f = 1.1f 

    printDouble(d)
//    printDouble(i) // Error: Type mismatch
//    printDouble(f) // Error: Type mismatch
}

4.. 리터럴 상수(Literal constants)

  • 10진수, Decimals: 123
    • Longs are tagged by a capital L: 123L
  • 8진수,  Hexadecimals: 0x0F
  • 2진수, Binaries: 0b00001011

NOTE: Octal literals are not supported.

 

  • Doubles by default: 123.5, 123.5e10
  • Floats are tagged by f or F: 123.5f

5. 가독성을 위한  언더스코어 사용가능

val oneMillion = 1_000_000
val creditCardNumber = 1234_5678_9012_3456L
val socialSecurityNumber = 999_99_9999L
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010

6. 변환

val b: Byte = 1 // OK, literals are checked statically
val i: Int = b // ERROR

타입은 명백히 독립적이므로 바로 대입하는것은 불가(java와 다름)

변환하기 위해선 toInt,toLong등의 메서드 사용필요

val i: Int = b.toInt() // OK: explicitly widened
print(i)
  • toByte(): Byte
  • toShort(): Short
  • toInt(): Int
  • toLong(): Long
  • toFloat(): Float
  • toDouble(): Double
  • toChar(): Char

*오퍼레이터 오버로딩

val l = 1L + 3 // Long + Int => Long

서로 다른 두 데이터의 수학적 계산 결과의 타입은 자동으로 최적화됨

 

*나눗셈으로 인한 타입 결정은 java와 같음

7.비교(Floating point numbers comparison)

The operations on floating point numbers discussed in this section are:

  • Equality checks: a == b and a != b
  • Comparison operators: a < b, a > b, a <= b, a >= b
  • Range instantiation and range checks: a..b, x in a..b, x !in a..b

2.Characters

Characters are represented by the type Char. They can not be treated directly as number

fun check(c: Char) {
    if (c == 1) { // ERROR: incompatible types
        // ...
    }
}

특수문자사용 : \t, \b, \n, \r, \', \", \\ and \$

유니코드 인코딩 : '\uFF00'

 

Convert a character to an Int number

fun decimalDigitValue(c: Char): Int {
    if (c !in '0'..'9')
        throw IllegalArgumentException("Out of range")
    return c.toInt() - '0'.toInt() // Explicit conversions to numbers
}

 

3.Booleans

The type Boolean represents booleans, and has two values: true and false.

Built-in operations on booleans include

  • || – lazy disjunction
  • && – lazy conjunction
  • ! - negation

4.Arrays

Arrays in Kotlin are represented by the Array class

메서드 : set,get( that turn into[] by operator overloading conventions )

특성 : size

 

사용법

1. arrayOf(1, 2, 3) creates an array [1, 2, 3]

2. arrayOfNulls() library function can be used to create an array of a given size filled with null elements.

3. Array constructor

it takes array size and the function that can return the initial value of each array element given its index

// Creates an Array<String> with values ["0", "1", "4", "9", "16"]
val asc = Array(5) { i -> (i * i).toString() }
asc.forEach { println(it) }

Primitive type arrays

ByteArray, ShortArray, IntArray등 Kotlin에는 특수한 배열들이 있습니다. 각 배열은 Array에 상속되지 않지만 동일한 메서드를 사용하며 []를 통해 접근이 가능합니다.

val x: IntArray = intArrayOf(1, 2, 3)
x[0] = x[1] + x[2]
// Array of int of size 5 with values [0, 0, 0, 0, 0]
val arr = IntArray(5)

// e.g. initialise the values in the array with a constant
// Array of int of size 5 with values [42, 42, 42, 42, 42]
val arr = IntArray(5) { 42 }

// e.g. initialise the values in the array using a lambda
// Array of int of size 5 with values [0, 1, 2, 3, 4] (values initialised to their index value)
var arr = IntArray(5) { it * 1 }

5.Strings

Strings are represented by the type String

특성

1.Strings are immutable

2.Elements of a string are characters that can be accessed by the indexing operation: s[i]

3. string can be iterated over with a for-loop:

for (c in str) {
    println(c)
}

4.  concatenate strings using the + operator, as long as the first element in the expression is a string

val s = "abc" + 1
println(s + "def")

String literals

1.특수 문자사용

val s = "Hello, world!\n"

2. """ 사용

val text = """
    for (c in "foo")
        print(c)
"""

String templates

$기호를 사용해서 템플릿으로 사용할 수 있음

val i = 10
println("i = $i") // prints "i = 10"

 

중괄호를 사용해서 임의의 표현까지 템플릿으로 사용 가능

val s = "abc"
println("$s.length is ${s.length}") // prints "abc.length is 3"

 

'Android > Kotlin' 카테고리의 다른 글

Calling Extension  (0) 2021.08.19
[Kotlin] Extension Function  (1) 2021.07.19
Kotlin - 2. Packages and Imports  (0) 2020.03.29

+ Recent posts