코틀린 & Java/코틀린인액션

[Kotlin] 기본 요소 함수와 변수에 대해 알아보자 (1)

코딩하는후운 2024. 3. 19. 14:28
반응형

코틀린에서 타입 선언을 생략해도 된다.

# 코틀린 특징

  • 코틀린 표준 라이브러리는 여러가지 표준 자바 라이브러리 함수를 간결하게 사용할 수 있게 감싼 래퍼(wrapper)를 제공
  • 줄 끝에 세미콜론(;)을 붙이지 않아도 된다.
  • if는(값을 만들어내지 못하는 문장이 아니고 결과를 만드는 식)

문(statement)과 식(expression) 식은 값을 만들어 내며 다른 식의 하위 요소로 계산에 참여할 수 있다. 문은 가장 안쪽 블록의 최상위 요소로 존재, 아무런 값을 만들어내지 않는다.

코틀린은 루프를 제외한 대부분의 제어 구조가 식이다 자바는 모든 제어 구조가 문이다

반면

대입문은 자바에서는 식이었으나, 코틀린에서는 문이 됐다.

# 함수

fun max(a: Int, b: Int): Int {
}
  • 함수를 선언할 때에는 fun 키워드를 사용
  • fun 다음에는 함수 이름
  • 함수 이름 뒤에는 괄호안에 파라미터 목록이 온다.
  • 반환 타입은 사이클 콜론(:)으로 구분
  • 자바와 달리 꼭 클래스 안에 함수를 넣어야 할 필요가 없다. : 함수를 최상위 수준에 정의 할수 있다.

식이 본문인 함수

return을 제거하면서 등호(=)를 식앞에 붙인 함수.

: 등호와 식으로 이뤄진 함수

본문이 중괄호로 둘러싸인 함수는 블록이 본문인 함수라 부른다.

반환 타입을 생략할 수 있는 이유?

(= 타입추론type interface)

코틀린은 정적 타입 지정 언어이므로 컴파일 시점에 모든식의 타입이 정해져야 한다.

하지만 식이 본문인 함수의 경우 반환 타입을 적지 않아도 컴파일러가 함수 본문 식을 분석해서 반환타입으로 정해준다.

식이 본문인 함수의 반환 타입만 생략 가능 블록이 본문인 함수가 값을 반환한다면, 반환 타입을 지정하고 return문을 사용해 반환 값을 명시

# 변수

자바 : 타입이 맨 앞에 온다.

코틀린 : 변수 이름 뒤에 타입을 명시

코틀린은 타입 지정을 생략하는 경우가 흔하다. 타입으로 변수 선언을 시작하면 타입을 생략할 경우 식과 변수 선언을 구별할 수 없다.

초기화 식을 사용하지 않고 변수를 선언하려면 변수타입을 반드시 명시

val answer: Int
answer = 42

변수에 저장될 값에 대해 아무정보가 없기 때문에 컴파일러가 타입추론을 할 수 없다.

변경 가능한 변수(var) 와 변경 불가능한 변수(val)

변수 선언시 사용하는 키워드는 2가지 이다.

  • val(값을 뜻하는 value) - 변경 불가능한(immutable)참조를 저장하는 변수 초기화 하고 나면 재대입 불가능. (자바 final)
  • var(변수를 뜻하는 variable) - 변경 가능한(mutable)참조 변수의 값은 바뀔 수 있다. (자바 일반변수)

기본적으로 모든 변수를 val로 선언하고, 나중에 꼭 필요할 때에만 var로 변경하라. 이유 : (변경 불가능한 참조 & 변경 불가능한 객체)를 부수 효과가 없는 함수와 조합해 사용하면 코드가 함수형 코드에 가까워진다.

부수 효과가 없는 : 함수가 만들어진 목적과는 다른 효과 또는 부작용

val 참조 자체는 불변일지라도, 그 참조가 가리키는 객체의 내부 값은 변경될 수 있다.

val languages = arrayListOf("Java") <- 불변 참조를 선언
languages.add("Kotlin") <- 참조를 가리키는 객체 내부를 변경

var 참조는 변수의 값을 변경할 수 있지만, 타입은 고정돼 바뀌지 않는다.

var answer = 42
answer = "no answer" <- Error: type mismatch 컴파일 오류

컴파일러는 변수 선언 시점의 초기화 식으로부터 변수의 타입을 추론하며, 재대입이 이뤄질 때에는 이미 추론한 변수의 타입을 염두에 두고 타입을 검사 한다. 다른 타입의 값을 저장하고 싶다면 변환 함수를 써서 타입을 변환하거나, 강제 형 변환 해야한다.

문자열 템플릿

변수를 문자열 안에 사용할 수 있다.

필요한 곳에 변수를 넣되 변수 앞에 $를 추가

"hellow $name !"
자바의 문자열 접합 연산과 동일한 기능
("hellow, " + name + "!")
  • 문자로 $를 넣고 싶으면 \(이스케이프)시켜야한다. “\$x”
  • 복잡한 식도 중괄호 { }로 둘러 싸서 문자열 템플릿 안에 넣을 수 있다.
  • 중괄호로 둘러싼 식 안에서 큰 따옴표를 사용할 수도 있다.
"hello ${name}!"

$ 로 변수를 지정할 때 변수명 바로 뒤에 글을 붙여서 사용하면 코틀린 컴파일러는 영문자와 한글을 한꺼번에 식별자로 인식해서 unresolved reference오류를 발생

해결 방법은 ${name}처럼 { }로 감싸는 것이다.

# 클래스

자바 클래스 선언

public class Person {
	private final String name;

	public Person(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}
}

자바에서는 생성자 본문에 이 같은 코드가 반복적으로 들어가는 경우가 많다.

코틀린 클래스 선언

class Person(val name: String)
  • 코드가 없이 데이터만 저장하는 클래스를 값 객체(value object)라 부른다.
  • 코틀린의 기본 가시성은 public 이므로 이런 경우 변경자를 생략해도 된다.

클래스의 목적은 데이터를 캡슐화(encapsulate)하고, 캡슐화한 데이터를 다루는 코드를 한 주체 아래 가두는 것.

# 프로퍼티

자바에서는 필드와 접근자를 한데 묶어 프로퍼티(property)라 부른다.

코틀린은 프로퍼티를 언어 기본 기능으로 제공

필드 : 데이터를 필드에 저장

접근자 메서드 : 클라이언트가 그 데이터에 접근하는 통로

  • 게터 : 필드를 읽기 위함
  • 세터 : 필드를 변경

코틀린에서 프로퍼티를 선언하는 방식은 접근자를 선언하는 것이다.

class Person(
	val name: String,
	var isMarried: Boolean
)

val

name : 읽기 전용 프로퍼티, 게터를 만들어 낸다.

var

isMarried : 쓸 수 있는 프로퍼티, 게터, 세터를 만들어 낸다.

게터와 세터의 이름을 정하는 규칙에는 예외가 있다. is로 시작하는 프로퍼티의 게터에는 get이 붙지 않고 원래 이름 그대로 사용. 세터에는 is를 set으로 바꾼 이름을 사용

val person = Person("Bob", true) // new 키워드를 사용하지 않고 생성자 호출
println(person.name) //프로퍼티 이름을 직접 사용해도 게터를 자동으로 호출
println(person.isMarried)

뒷 받침하는 필드

프로퍼티 값을 그때그때 계산.

커스텀 접근자

val isSquire: Boolean
	get() { //프로퍼티 게터 선언
		return height == width
	}
  • isSquare 프로퍼티에는 자체 값을 저장하는 필드가 필요 없다.
  • 프로퍼티에 접근할 때마다 게터가 프로퍼티 값을 매번 다시 계산.

파라미터가 없는 함수를 정의하는 방식 vs 커스텀 게터를 정의하는 방식 두방식 모두 비슷. 구현이나 성능상 차이는 없다. 가독성의 차이만 있다.

# 코틀린 소스 구조

자바의 경우

  • 모든 클래스를 패키지 단위로 관리
  • 같은 패키지에 속해 있다면 다른 파일에서 정의한 선언일지라도 직접 사용 가능
  • 다른 패키지라면 import를 통해 선언을 불러와야 함
package geometry.shapes //패키지 선언

import java.util.Random //자바 라이브러리 클래스 임포트

코틀린의 경우

  • 클래스 임포트와 함수 임포트에 차이가 없다. 모든 선언을 import로 가져올 수 있다.
import geometry.shapes.createRandomRectangle // 이름으로 함수 임포트하기

패키지 뒤에 .*을 추가하면 패키지 안의 모든 선언을 임포트 할 수 있다.

스타 임포트를 사용하면 패키지 안에 있는 모든 클래스 뿐만 아니라 최상위에 정의된 함수나 프로퍼티까지 모두 불러온다는 점에 유의

코틀린 특징

  • 여러 클래스를 한 파일에 넣을 수 있다.
  • 파일의 이름도 마음대로 정할 수 있다.
  • 디스크상의 어느 디렉터리에 소스코드 파일을 위치시키든 관계 없다.

하지만 대부분의 경우 자바와 같이 패키지 별로 디렉터리를 구성하는 편이 낫다.

: 자바 클래스를 코틀린 클래스로 마이그레이션할 때 문제가 생길 수 있다.

# enum클래스

  • 코틀린에서는 enum class를 사용하지만 자바에서는 enum을 사용
  • 코틀린에서 enum은 소프트키워드라 부르는 존재.
  • enum은 class앞에 있을 때는 특별한 의미를 지니지만 이름에 사용할 수 있다.

class는 키워드다.

  • class라는 이름을 사용할 수 없으므로 클래스를 표현하는 변수 등을 정의할 때 clazz나 aClass와 같은 이름으로 사용
enum class Color {
	val r: Int, val g: Int, val b: Int // 상수의 프로퍼티 정의
} {
	RED(255, 0, 0), ORANGE(255, 165, 0) // 각 상수를 생성할 때 그에 대한 프로퍼티값 지정
	INDIGO(75, 0, 130); //반드시 세미콜론을 사용!

	fun rgb() = (r * 256 + g) * 256 + b // enum 클래스안에 메서드 정의
}

코틀린에서 유일하게 세미콜론(;)이 필수인 부분

enum 클래스 안에 메서드를 정의할 경우 반드시 num상수 목록과 메서드 정의 사이에 세미콜론을 넣어줘야 한다.

# When

  • 자바의 switch에 해당하는 코틀린 구성요소는 when이다.
  • if 와 마찬가지로 when도 값을 만들어내는 식이다.
  • 자바와 달리 각 분기의 끝에 break를 넣지 않아도 된다.
  • 한 분기 안에 여러 값을 매치 패턴으로 사용할 수도 있다. 그 경우 값 사이를 콤마(,)로 구분
fun getwarmth(color: Color) = when(color) {
	Color.RED, Color.ORANGE -> "warm"
	Color.GREEN -> "neutral"
}

분기 조건에 상수(enum상수나 숫자 리터럴)만을 사용할 수 있는 자바 switch와는 달리 코틀린 when의 분기 조건은 임의의 객체를 허용

fun mix(c1: Color, c2: Color) = 
	when (setOf(c1, c2)) {
		setOf(RED, YELLOW) -> ORANGE
	}

인자 없는 when

  • 인자가 없는 when식을 사용하면 불필요한 객체 생성을 막을 수 있다.
when {
	(c1 == RED && c2 == YELLOW) ||
	(c1 == YELLOW && c2 == RED) ->
		ORANGE
}

when에 인자가 없으려면 분기의 조건이 Boolean 결과를 계산하는 식이어야 한다.

  • if 중첩을 when으로 사용 할 수 있다.
  • if와 when 모두 분기에 블록을 사용할 수 있다. 그런 경우 블록의 마지막 문장이 블록 전체의 결과가 된다.(값을 만들어내야 하는 경우 항상 성립)

# 스마트 캐스트

is

코틀린에서는 is를 사용해 변수 타입을 검사한다. (자바의 instanceof와 비슷)

  • 자바에서는 instanceof로 확인 후 접근하기 위해서는 타입캐스팅을 해야한다.
  • 코틀린에서는 프로그래머 대신 컴파일러가 캐스팅을 해준다.

스마트 캐스트 : 컴파일러가 캐스팅을 수행해준다.

  • is로 변수에 든 값의 타입을 검사한 다음에 그 값이 바뀔 수 없는 경우에만 작동 프로퍼티는 반드시 val이어야 함. 커스텀 접근자를 사용한 것이어도 안된다.

이유 : 프로퍼티에 대한 접근이 항상 같은 값을 내놓는다고 확신할 수 없기 때문

as

원하는 타입으로 명시적으로 타입 캐스팅

반응형