[Android/Kotlin] 7 변수
변수는 프로그래밍의 가장 기초 내용이므로 가장 중요하기 때문에 확실하게 알아보도록 하겠다!
1) 변수 var
변수(Variable)란?
값을 임시로 메모리(저장 공간)에 저장하고 그 저장 공간에 이름을 부여한 것을 말한다.
이름과 값으로 구성되며 메모리의 특정 위치에 이름표를 붙이고 값을 담아두는 것과 같다.
메모리에 값을 입력하면서 이름표를 붙여두면 소스 코드에서 해당 값을 사용하고자 할 때 앞에서 붙였던 이름으로 사용할 수 있다.
2) 변수 선언방식
첫째, 변수 선언과 동시에 값 넣기
입력되는 값으로 타입을 추론할 수 있다.
값이 입력되는 순간 해당 값의 형태(문자, 숫자, 불리언 등)로 변수의 타입이 결정된다.
var 변수명(이름) = 값
(예시)
var myName = "홍길동" // 변수명 myName에 문자열 "홍길동"을 입력
둘째, 값으로 초기화하지 않고 선언만 하고 사용하기
- 선언만 할 경우, 반드시 변수명 옆에 콜론(:) 구분자를 붙여서 타입(자료형)을 지정해야 한다.
var 변수명: 타입
변수명 = 값
(예시)
var myAge: Int // 변수명 myAge를 Int 타입으로 선언
myAge = 27 // 숫자 '27'을 입력
위 두가지 예시로 변수는 최초 선언 시에 타입이 결정된다는 것을 예상할 수 있다.
변수는 한번 타입을 결정하면 동일한 타입의 값만 담을 수 있다.
만약 아래와 같이 Int 타입의 변수에 문자열 “홍길동”을 입력한다면 컴파일 에러가 발생한다.
var myAge: Int
myAge = 27
myAge = "홍길동" // (X) 선언했던 값의 타입과 다르기 때문에 입력할 수 없습니다.
myAge = 19 // (O) 선언했던 타입과 같은 숫자(Int)는 입력할 수 있습니다.
3) 데이터 타입
코틀린에서 제공되는 기본 데이터 타입은 다음과 같다.
구분 | 테이터타입 | 설명 | 값의 범위 및 예 |
---|---|---|---|
숫자형 | Double | 64비트 실수 | -1.7E+308의 근삿값 ~ 1.7E+308의 근삿값 |
Float | 32비트 실수 | -3.4E+38의 근삿값 ~ 3.4E+38의 근삿값 | |
Long | 64비트 실수 | -2E63 ~ 2E63-1 | |
Int | 32비트 실수 | -2,147,483,648 ~ 2,147,483,647 | |
Short | 16비트 실수 | -32,768 ~ 32,767 | |
Byte | 8비트 실수 | -128 ~ 127 | |
문자형 | Char | 1 개의 문자 | ‘글’ (외따옴표) |
String | 여러 개의 문자 | “여러 개의 글자입니다.” (쌍따옴표) | |
불리언형 | Boolean | true, false 두 가지 값 | true 또는 false |
기본 타입
기본 타입(Primitive Type)이란?
변수에 저장되는 값의 크기가 미리 정해져 있는 타입을 말한다.
Int는 2,147,483,647까지만 저장할 수 있다.
Byte는 127까지만 저장할 수 있다.
Byte에 저장 범위를 넘어서는 128을 입력할 경우 전혀 다른 숫자가 된다.
String의 경우는 쌍따옴표(“ “) 사이에 몇 글자가 들어가도 상관없기 때문에 값의 크기를 특정할 수 없다. 따라서 String은 기본 타입이 아니다!
Double
- 소수점이 있는 값을 저장할 때 사용한다.
var doubleValue: Double
doubleValue = 3.141592
Float
Double과 동일한 용도이지만 더 작은 범위의 숫자를 저장할 때 사용한다.
안드로이드 스튜디오는 Double과 Float을 구분하기 위해 Float의 경우 숫자 끝에 ‘F’를 붙여준다.
var floatValue: Float
floatValue = 3.141592F
Int
- 소수점이 없는 정숫값을 저장할 때 사용한다.
var intValue: Int
intValue = 2147483647
- 다음과 같이 가독성을 높이기 위해서 언더바(_)로 자릿수를 구분할 수 있다.
var intValue: Int
intValue = 2_147_483_647
- 다만, 언더바는 개발자가 읽기 쉽게 하기 위한 것으로 컴퓨터는 앞의 값과 동일하게 인식한다!
Long
Int보다 큰 범위의 정수를 저장할 수 있다.
Double과 Float의 관계처럼 Int와 구분하기 위해서 숫자의 끝에 ‘L’을 붙여준다.
Long 타입의 경우에는 설정한 글꼴에 따라 숫자 1과 구분이 어려울 수 있기 때문에 대문자를 사용한다.
var longValue: Long
longValue = 2147483647L
여기서 잠깐!
정수형 변수에 실수형 값을 입력할 수 있나요?
같은 숫자라도 타입이 다르면 기본적으로 입력할 수 없다.
예를 들어 정수형(Int) 숫자 1과 실수형(Double) 숫자 1.0은 서로 다른 타입으로 인식하기 때문에 정수형으로 선언된 변수에 실수형 값을 입력할 수 없다.
꼭 필요한 경우에는 데이터 타입(자료형)을 변환하는 과정을 거쳐서 입력할 수 있다.
Short와 Byte
- 정숫값을 저장할 때 사용하는데 입력할 수 있는 값의 크기가 Int보다 작다.
var shortValue: Short = 32_767
var byteValue: Byte = 127
여기서 잠깐!
타입을 지정하지 않은 숫자는 어떻게 저장되나요?
위 예제들을 살펴보면, 정수형 변수 다음에 항상 콜론(:) 구분자를 사용해서 타입을 지정하였다.
이유는 타입을 지정하지 않은 일반 숫자 값을 모두 Int 타입으로 인식해 의도치 못한 결과를 가져올 수 있기 때문이다.
var byteValue: Byte = 127 // 127이 Byte 타입으로 저장됩니다.
var byteValue2 = 127 // 127이 Int 타입으로 저장됩니다.
var floatValue: Float = 3.14F // 3.14가 Float 타입으로 저장됩니다.
var doubleValue = 3.14 // 끝에 F를 붙이지 않으면 Double로 저장됩니다.
Char
하나의 글자만 외따옴표(‘‘)로 감싸서 저장할 수 있다.
타입을 특정하지 않아도 외따옴표 안에 하나의 글자만 지정하기 때문에 Char로 저장된다.
var charValue = 'A'
String
여러 개의 문자를 지정할 수 있다.
기본 타입에는 포함되지 않지만 거의 모든 컴퓨터 언어에서 가장 많이 사용되는 타입이다.
var stringValue: String = "ABCDEF"
Boolean
true, false 둘 중에 1개의 값을 저장할 수 있다.
주로 조건문에서 의사결정을 하기 위한 용도로 많이 사용된다.
var boolValue = true
여기서 잠깐!
Int 2,147,483,647 값에 1을 더하면?
양수의 저장
Int는 32비트까지 표현할 수 있기 때문에 32개의 비트로 구성되어 있다.
Int 2,147,483,647을 2진수로 변환하면 32개의 비트로 다음과 같이 표현된다.
예) 0111 1111 1111 1111 1111 1111 1111 1111 <- 가장 앞의 비트 0은 값이 아닌 부호 비트로, 부호 비트가 0이면 양수(+), 1이면 음수(-) 이다.
2의 보수와 입력값의 제한
대부분의 컴퓨터는 음수를 ‘2의 보수법’으로 계산하여 저장한다.
이는 음수의 절댓값을 2진수로 바꾸고(1의 보수), 여기에 다시 1을 더하는(2의 보수) 것을 말한다.
-2,147,483,648은 값(1의 보수)에 1을 더한 결과이므로 가장 오른쪽의 1부터 자리가 바뀌면서 모두 0으로 바뀌게 된다.
Int 타입에 저장할 수 있는 가장 큰 값인 2,147,483,647을 이진수로 표시하면 다음과 같다.
예) 0111 1111 1111 1111 1111 1111 1111 1111
위 값에 1을 더하면 모든 값이 올림이 되면서 음수 -2,147,483,648이 되고 의도치 않은 결과가 발생할 수 있다. –> 1000 0000 0000 0000 0000 0000 0000 0000
Byte나 Long도 동일한 원리로 동작합니다.
데이터 타입을 선언할 때는 저장될 데이터의 크기를 항상 고려해주어야 한다!
실습
- 앞서 생성한 BasicSyntax 프로젝트의 MainActivity.kt 파일에 아래와 같이 코드를 작성한다.
package net.flow9.thisiskotlin.basicsyntax
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var myName = "홍길동"
var myAge: Int
myAge = 27
myAge = myAge + 1
Log.d("BasicSyntax", "myName = $myName, myAge = $myAge")
}
}
myName = 홍길동, myAge = 28
여기서 잠깐!
문자열 템플릿
코틀린에서는 더하기(+) 연산으로 2개의 문자를 하나로 합칠 수 있다.
예) “홍” + “길동”처럼 2개의 문자열을 더하면 “홍길동”과 같이 하나의 문자열로 만들어 준다.
추가로 문자열을 보다 쉽게 다룰 수 있는 문자열 템플릿도 제공한다.
- 문자열 내부에서 달러($) 기호를 넣으면 해당 영역이 문자가 아닌 코드라는 것을 알려준다.
var name="홍길동"
Log.d("BasicSyntax", "제 이름은 $name 입니다.") // 출력: 제 이름은 홍길동 입니다.
주의할점
- $변수 다음에 이어지는 문자가 있다면 공백으로 구분되어야 한다.
“제 이름은 $name(공백) 입니다.” // 이어지는 문자열과의 사이에 공백이 없으면 문자열로 인식한다.
- 실제로 편집기 창에서 Log.d 코드를 입력하면 다음과 같이 ‘tag’와 ‘msg’가 자동으로 입력된다.
Log.d(tag: "BasicSyntax", msg: "myName = $myName, myAge = $myAge")
읽기 전용 변수 Val
변수의 다른 선언 방법으로는 읽기 전용 변수인 val이 있다.
위의 var과는 다르게 한 번 입력된 값은 변경할 수 없다.
변하지 않는 값을 미리 입력해둘 때 사용한다.
상수라고도 부르기도 하지만 상수와는 의미가 조금 다르다.
읽기 전용 변수 val 선언하기
- 선언하는 방법은 변수와 동일하며 이름 앞에 val을 붙이면 읽기 전용 변수가 된다.
val 변수명(이름) = 값
- 변하지 않는 값을 미리 지정해두고 필요에 따라 다른 값과 조합해서 사용할 수 있다.
val roadName = "필문대로" // 미리 지정
var address = roadName + "8길" // 필요할 때 조합해서 사용합니다.
- val로 정의된 변수는 값을 변경할 수 없으므로 다음과 같이 입력하면 에러가 발생한다.
val language = "kotlin"
language = "java" // ERROR, val은 값을 변경할 수 없습니다.
상수 const
상수는 주로 기준이 되는 변하지 않는 값을 입력해둘 때 사용한다.
읽기 전용 변수인 val 앞에 const 키워드를 붙여서 만든다.
const val PI = 3.141592
- val과 같이 읽기 전용인 것은 동일하지만, 컴파일 시에 값이 결정되기 때문에 Int, Long과 같은 기본형과 문자열인 String만 입력할 수 있다.
코딩 컨벤션
규모가 큰 프로젝트의 경우 천 명 이상의 프로그래머가 함께 일하기도 한다.
이렇게 많은 프로그래머가 각자의 방식으로 코딩하면 개발이 완료된 다음 오류를 수정하거나 업데이트를 할 때 코드를 분석하기 어려울 수 있다.
대부분의 회사에서는 최초 개발자와 유지보수 담당자가 다르기에 각 프로젝트별로 코드를 작성하는 규칙을 만든다.
이를 코딩 컨벤션(Coding Convention)이라고 한다.
클래스명
클래스란 코드를 알아보기 쉽게 하나의 파일에 모아 놓은 것으로 생각하면 된다.
대체로 클래스의 네이밍 컨벤션(명명 규칙)은 캐멀 케이스(Camel Case)를 사용한다.
클래스명은 단어의 첫 글자는 대문자로, 나머지 글자는 소문자로 표기한다.
새로운 단어가 나타나면 첫 글자를 대문자로 표기한다. (마치 낙타의 등처럼 높낮이가 생긴다고 해서 캐멀 케이스(낙타표기법)라고 부른다.)
클래스명은 일반적으로 파일명과 동일하지만 항상 그런 것은 아니다.
앞에서 작성한 코드 중에 class MainActivity…로 시작하는 코드가 있는데, 여기서 MainActivity가 클래스명이다.
- class MainActivity <- MainActivity에서 첫 글자인 M은 대문자, 새로운 단어인 Activity의 첫 글자도 대문자이다.
클래스는 다음과 같이 변수와 함수를 포함한다.
class MainActivity{
val name = "kotlin"
var age = 17
fun getFullName(): String{
...
}
}
함수명과 변수명
- 캐멀 케이스를 따르며 첫 글자만 소문자로, 이후 새로운 단어의 첫 글자는 대문자로 표기한다.
// 첫 글자인 o는 소문자, 새로운 단어(Create, Activity)의 첫 글자(C, A)는 대문자
fun onCreateActivity()
// 첫 글자인 i는 소문자, 새로운 단어(Value)의 첫 글자(V)는 대문자
var intValue: Int
상수명
- 상수명은 모두 대문자로 작성한다.
const val HELLO: String = "안녕"
- 상수명이 2개의 단어로 이루어져 있다면 다음 예시 코드의 상수명처럼 단어 사이를 언더바(_)로 구분하는 스네이크 케이스(Snake Case)를 사용할 수 있다.
const val HOW_ARE_YOU: String = "어떻게 지내?"
여기서 잠깐!
요즘의 함수명과 변수명 작성 규칙은?
요즘은 클래스명을 제외한 함수명과 변수명을 스네이크 케이스로 작성하는 회사도 많아졌다.
캐멀 케이스보다 가독성이 좋기 때문이다.
다만! 스네이크 케이스의 경우 상수명만 대문자를 사용하고 다른 때는 소문자를 사용한다.
fun on_create_activity()
- 개인 선호도에 따라 캐멀 케이스, 스네이크 케이스 중 한 가지를 선택할 수 있지만, 하나의 프로젝트를 스네이크 케이스로 시작했다면 모든 코드를 스네이크 케이스로 작성하는 일관성이 있어야 한다!
들여쓰기
새로운 코드 블록이 시작되면 스페이스바(Spacebar) 또는 탭(Tab) 키로 동일한 간격만큼 들여쓰기를 한다.
코딩 규칙을 통틀어서 가장 중요한 규칙이다!
코드에 들여쓰기(Indent)가 되어 있지 않다면 프로그래머로서 면접을 볼 기회조차 없을 수 있을 정도로 프로그래머들은 중요하게 생각한다.
class MainActivity{ // 새로운 블록의 시작
var newVariable = 50 // 일반적으로 맨 앞에 공백 4칸 들여쓰기 사용
fun on_create_activity(){ // 다시 새로운 블록의 시작
var variable = 10 // 추가 공백 4칸(8칸) 들여쓰기
Log.d("BasicSyntax", "변수의 값은 $variable 입니다.") // 추가 공백 4칸(8칸) 들여쓰기
} // 공백 4칸 들여쓰기
}