Kotlin] 기본 문법(1)
Kotlin
해당 내용은 테크과학! DiMo Youtube 내용을 보고 정리하였습니다.
학습에 사용한 Web Tool: play.kotlinlang.org
1 | fun main() { |
클래스 이름: 파스칼 표기법
파스칼 표기법
모든 단어를 대문자로 시작
ClassName
함수나 변수 이름: 카멜 표기법
CamelCase
첫 단어만 소문자로 시작
functionName
변수 선언법
var
일반적으로 통용되는 변수
언제든지 읽기 쓰기가 가능함
val
선언시에만 초기화 가능
중간에 값을 변경할 수 없음
(const나 final과 같은 변수 -> 상수)
클래스에 선언된 변수: Property(속성)
1 | fun main() { |
위의 코드를 실행하면 에러가 발생하게 된다.
코틀린은 기존의 언어와는 다르게 값이 할당되지 않으면 Error를 표시하게 된다
Variable 'a' must be initialized
코틀린은 기본 변수로 Null값을 허용하지 않아서 원천적으로 의도치 않은 동작이나 null point exception을 막아준다.
1 | fun main() { |
프로그래밍을 하면서 Null이 필요할 때가 있는데 이러할 때는 아래와 같이 하면 된다
1 | fun main() { |
자료형 뒤 물음표를 붙이면 nullable 변수로 선언해 줄 수 있다.
:warning:nullable 변수는 null인 상태로 연산할 시 null point exception이 발생할 수 있으므로 주의해서 사용 필요
변수의 초기화를 늦추는 lateinit
이나 lazy
속성은 클래스에 대한 지식 필요
Primitive Type
기본 자료형은 Java와의 호환을 위해 거의 동일
숫자형 타입
정수형
참고, Kotlin은 8진수의 표기는 지원하지 않음
1 | fun main() { |
실수형
1 | fun main() { |
문자형
코틀린 문자열은 UTF-16 BE 규칙을 따르고 있으며 글자 하나가 2bytes의 메모리 공간 사용
1 | fun main() { |
Boolean
true(참) / false(거짓)
1 | fun main() { |
문자열
1 | fun main() { |
형변환과 배열
Type Casting
형변환은 하나의 변수에 지정된 자료형을 호환되는 다른 자료형으로 변환하는 기능
형변환 함수
- toByte()
- toShort()
- toInt()
- toLong()
- toFloat()
- toDouble()
- toChar()
- toString()
명시적 형변환(Explicit type casting)
변환될 자료형을 개발자가 직접 지정함
1 | fun main() { |
:man_teacher:코틀린은 형변환시 발생할 수 있는 오류를 막기 위해 암시적 형변환은 지원하지 않음
Array
Array<T>
1 | fun main() { |
타입 추론과 함수
타입 추론
1 | fun main() { |
var a:Int = 5
라고 사용하지 않아도 Kotlin이 타입을 추론하여 Int형태로 선언해주는 것을 알 수 있다.
함수
function의 fun
을 이용하여 선언
:runner:연습. 3가지 숫자를 받아서 합해주는 함수를 만들어보기
1 | fun plusCalc(a:Int, b:Int, c:Int):Int { |
단일 표현식 함수(Single-Expression Function)
1 | fun plusCalc(a:Int, b:Int, c:Int) = a + b + c |
단일 표현식 함수에서는 반환형의 타입을 생략할 수 있다.
함수형 프로그래밍 언어를 이해하기 위해서 함수는 자료형이 결정된 변수라는 개념으로 접근하는 것이 좋다.
조건문
when
다른 언어의 switch문을 when으로 변경하여 사용함
다른 언어의 break문 사용없이 위에 조건에 맞게 되면 아래의 조건은 실행하지 않음
:warning:등호나 부등호의 사용은 불가능
1 | /* 조건에 따른 출력 */ |
1 | /* 조건에 따른 변수값 지정 */ |
반복문
파이썬과 다르게 마지막 값까지 포함하여 반복
1 | fun main() { |
1씩 증가하는 것이 아닌 단계별 증가를 시키기 위해서는 step을 사용하면 된다
1 | fun main() { |
..
이 아닌 downTo를 이용하여 감소를 시킬 수 있다
1 | fun main() { |
1 | fun main() { |
step을 사용할 수도 있다
character형태가 숫자로도 나타낼 수 있기에…
흐름제어
break문과 continue문은 동일하나 코틀린에서 label 기능을 사용 할 수 있다
다중 반복문을 통해서 label의 기능을 살펴보자
먼저, 일반적인 이중 반복문에서 break를 이용한 방식을 보면 첫번째 for문으로 인해 이후의 값들도 지속됨을 알 수 있다
1 | fun main() { |
다음의 label의 기능을 쓴 방식은 처음 for문을 빠져나가게 해줌으로서 이후의 값이 출력되지 않는다
1 | fun main() { |
이렇게 보았을 때 시간 복잡도상으로 따진다면 큰 이득을 얻을 수 있다
보통은 함수처리하여 return
으로 마무리 했었는데 좋은 기능이 추가 되었음을 알 수 있다
Class
고유의 특징값(속성) + 기능의 구현(함수)로 이루어짐
1 | fun main() { |
여기서 클래스 옆에 var name:String, val birthYear: Int
는 클래스의 속성들을 선언함과 동시에 생성자 역시 선언하는 방법
생성자(Constructor)
새로운 인스턴스를 만들기 위해 호출하는 특수한 함수
생성자를 호출하면 클래스의 인스턴스를 만들어 반환 받을 수 있음
- 인스턴스의 속성을 초기화
- 인스턴스 생성시 구문을 수행
init함수를 통해 구문 수행 가능
init 함수는 파라미터나 반환형이 없는 특수한 함수로 생성자가 생성될 때 실행됨
1 | fun main() { |
기본 생성자: 클래스를 만들 때 기본으로 선언init
보조 생성자: 필요에 따라 추가적으로 선언constructor
:man_teacher:보조 생성자를 만들 때는 반드시 기본 생성자를 통해 속성을 초기화 해주어야 한다
보조 생성자가 기본 생성자를 호출하도록 하려면 아래와 같이 :this
라는 키워드를 사용하고 기본 파라미터가 필요한 부분을 괄호 안에 넣어주면 된다
1 | fun main() { |
상속(Inheritance)
코틀린은 상속 금지가 기본이기에 상속을 하기 위해서는 부모 클래스가 open
이어야 한다
open: 클래스가 상속될 수 있도록 클래스 선언시 붙여줄 수 있는 키워드
- 서브 클래스는 슈퍼 클래스에 존재하는 속성과 ‘같은 이름’의 속성을 가질 수 없다
- 서브 클래스가 생성될 떄는 슈퍼 클래스의 생성자까지 호출하여야 한다
1 | fun main() { |
오버라이딩과 추상화
Overriding
슈퍼 클래스에서 함수가 open
이 되어 있는 것은 서브 클래스에서 override
를 사용하여 재정의를 할 수 있다
1 | fun main() { |
Abstraction
추상화는 선언부만 있고 기능이 구현되지 않은 추상함수(abstraction function)와 추상클래스(abstraction class)로 구성
1 | fun main() { |
Interface
다른 언어에서 인터페이스는 추상함수로만 이루어져 있는 순수 추상화 기능
코틀린에서는 인터페이스도 속성, 추상함수, 일반함수를 가질 수 있다
다만, 추상함수는 생성자를 가질 수 있는 반면에 인터페이스는 생성자를 가질 수는 없다
구현부가 있는 함수: open함수로 간주
구현부가 없는 함수: abstract함수로 간주
별도의 키워드가 없어도 포함된 모든 함수를 서브클래스에서 구현 및 재정의 가능
한번에 여러 인터페이스를 상속할 수 있기에 유연한 설계 가능
[정리]
오버라이딩: 이미 구현이 끝난 함수의 기능을 서브클래스에서 재정의 할 때
추상화: 형식만 선언하고 실제 구현은 서브클래스에 일임할 때
인터페이스: 서로 다른 기능들을 여러개 물려주어야 할 때
접근제한자
Access Modifier
- public
- internal
- private
- protected
패키지 스코프
키워드 | 접근 범위 |
---|---|
public(기본값) | 어떤 패키지에서도 접근 가능 |
internal | 같은 모듈 내에서만 접근 가능 |
private | 같은 파일 내에서만 접근 가능 |
*protected는 사용하지 않음
클래스 스코프
키워드 | 접근 범위 |
---|---|
public(기본값) | 클래스 외부에서 늘 접근 가능 |
private | 클래스 내부에서만 접근 가능 |
protected | 클래스 자신과 상속받은 클래스에서 접근 가능 |
*internal은 사용하지 않음
고차함수와 람다함수
고차함수
(String)->Unit: 반환형에는 값이 없다라는 의미로 Unit를 사용하며, 람다식에 주로 사용
function1(::function2): 일반 함수를 고차 함수로 변경해 주는 연산자로 ::
를 사용
1 | fun main() { |
Lambda Function
위의 내용을 람다식을 써서 이렇게 나타낼 수도 있다
1 | fun main() { |
위의 람다함수를 이렇게 간략하게 쓸수도 있다
위의 람다함수에서는 String을 하나 받지만 반환값이 없기에 아래와 같이 쉽게 나타낼 수 있다
1 | fun main() { |
람다식에서도 여러줄을 사용할 수 있다
1 | fun main() { |
파라미터가 없을 시,
1 | fun main() { |
파라미터가 하나일 경우 다른방법으로 파라미터 가져오기: it
1 | fun main() { |
Scope Function
함수형 언어의 특징을 조금 더 편리하게 사용할 수 있도록 기본 제공하는 함수들
- apply
- run
- with
- also
- let
apply: 인스턴스를 생성한 후 변수에 담기 전에 ‘초기화 과정’을 수행할 때 많이 사용됨
1 | fun main() { |
main함수와 별도의 스코프에서 인스턴스의 변수와 함수를 조작하므로 코드가 깔끔해진다
run: 스코프 안에서 참조연산자를 사용하지 않아도 된다는 점은 apply와 같지만, 일반 람다함수처럼 인스턴스 대신 마지막 구문에서 값 반환
이미 인스턴스가 만들어진 후에 인스턴의 함수나 속성을 scope 내에서 사용해야할 떄 유용
1 | fun main() { |
with: run과 동일한 기능을 가지지만 단지 인스턴스를 참조연산자 대신 파라미터로 받는다는 차이
처리가 끝나면 인스턴스를 반환
: apply, also
처리가 끝나면 최종값을 반환
: run, let
참조연산자 없이 인스턴스의 변수와 함수를 사용: apply, run
파라미터로 인스턴스를 넘긴 것처럼 it
을 통해서 인스턴스 사용: also, let
also와 let이 파라미터를 통해서 인스턴스를 받는 이유
같은 이름의 변수나 함수가 scope 바깥에 중복되어 있는 경우에 혼란을 방지하기 위해서
1 | fun main() { |
이러한 경우 27000이 나와야하지만, 위의 price와 혼동이 생겨 5000이 나오게 됨