코틀린에서 변수를 다루는 방법
- 모든 변수는 var / val 을 붙여 주어야 한다.
- var : 변경 가능하다.
- val : 변경 불가능하다 (read-only)
- 타입을 명시적으로 작성하지 않아도 타입이 추론된다.
- Primitive TYpe과 Reference Type을 구분하지 않아도 된다.
- Null이 들어갈 수 있는 변수 타입 뒤에 ?를 붙여 주어야 한다.
- 아예 다른 타입으로 간주된다.
- 객체를 인스턴스화 할 때 new를 붙이지 않아야 한다.
- 모든 변수는 우선 val(불변)으로 만들고 꼭 필요한 경우 var로 변경한다.
-- Java
long number1 = 10L;
final long number2 = 10L;
Long number3 = 1_000L;
License license = new License("productName");
-- Kotlin
var number1 = 10L; // 가변
var number1: Long = 10L;
val number2 = 10L; // 불변
val number2: Long = 10L;
초기값을 지정해주지 않는 경우는
var number: Int
println(number) // 컴파일 에러 발생 Variable 'number' must be initialize
코틀린에서 Primitive Type and Reference Type
코틀린에서는 숫자, 문자, 불리언과 같은 몇몇 타입은 내부적으로 특별한 표현을 갖는다. 이 타입들은 실행시에 Primitive Value로 표현되지만 코드에서는 평번한 클래스 처럼 보인다. 즉, 프로그래머가 boxing / unboxing을 고려하지 않아도 되도록 Kotlin이 알아서 처리해준다. Java의 경우 primitive의 경우 int, long..., reference type의 경우 Integer, Long 로 표현하지만 코틀린에서는 var or val 로 통합되면 자동으로 치환된다. 결론은 개발자가 신경쓰지 않아도 되는 영역이다.
-- java
long number1 = 10L; // primitive type
Long number3 = 1_000L; // reference type
-- kotiln
var number1 = 10L;
var number3 = 10L;
var number1: Long = 10L;
코틀린에서 null 변수
코틀린에서는 기본적으로 모든 변수에 null을 가질 수 없도록 되어있다. 만약 null을 사용해야 한다면 아래와 같다.
var number: Long? = 1_000L
number = null
코틀린에서 객체 인스턴스화
코틀린에서는 java에서 사용하는 new 키워드를 사용하지 않는다.
-- java
Person person = new Person("java")
-- kotlin
var person = Person("kotlin")
코틀린에서 null을 다루는 방법
코틀린에서 null 체크
-- Java
public class Lec02Main {
public boolean startsWithA1(String str) {
if (str == null) {
throw new IllegalArgumentException("null이 들어왔습니다");
}
return str.startsWith("A");
}
public Boolean startsWithA2(String str) {
if (str == null) {
return null;
}
return str.startsWith("A");
}
public boolean startsWithA3(String str) {
if (str == null) {
return false;
}
return str.startsWith("A");
}
public static void main(String[] args) {
}
}
-- Kotlin
fun main() {
println(startWithA1("Addd"))
println(startWithA2(null))
}
fun startWithA1(str: String?) : Boolean {
if (str == null) {
throw IllegalArgumentException("str is null")
}
return str.startsWith("A")
}
fun startWithA2(str: String?) : Boolean? {
if ( str == null ) {
return null
}
return str.startsWith("A")
}
fun startWithA3(str: String?) : Boolean {
if ( str == null ) {
return false
}
return str.startsWith("A")
}
Safe Call과 Elvis 연산자
코틀린에서는 null이 가능한 타입을 완전히 다르게 취급한다. null 이 가능한 타입만을 위한 기능은 아래와 같다.
-- safe call
var str: String? = "ABC"
str.length // 불가능, 사용할 수 없다.
str?.length // 가능 , null이면 실행하지 않고(그대로 null), null이 아니면 실행
-- elvis 연산자
var str: String? = "ABC"
str?.length ?: 0 // str 값이 null 이면 0으로 사용한다.
// 위 코틀린 코드를 safe call과 elvis 적용한 예시
// 널을 허용하지만 절대 널일 수 없다!!
fun startWithA4(str: String?) : Boolean {
return str!!.startsWith("A")
}
fun startWithA1Refector(str: String?) : Boolean {
return str?.startsWith("A") ?: throw IllegalArgumentException("str is null")
}
fun startWithA2Refector(str: String?) : Boolean? {
return str?.startsWith("A")
}
fun startWithA3Refector(str: String?) : Boolean {
return str?.startsWith("A") ?: false
}
코틀린에서 Type를 다루는 방법
기본 타입
- Byte
- Short
- Int
- Long
- Float
- Double
- 부호 없는 정수들
코틀린에서는 선언된 기본 값을 보고 타입을 추론한다
val number1 = 3 // Int
val number2 = 3L // Long
val number3 = 3.0f // Float
val number4 = 3.0 // Double
// 타입을 명시하고 싶다면 아래와 같이 사용한다.
val number1: Int = 3
val number2: Long = 3L
val number3: Float = 3.0f
val number4: Double = 3.0
Java : 기본 타입간의 변환은 암시적으로 이루어질 수 있다.
Kotlin : 기본 타입간의 변환은 명시적으로 이루어져야 한다.
// java
// int 타입의 값이 long 타입으로 암시적으로 변경되었습니다. Java에서 더 큰 타입으로 암시적 변경
int number1 = 4;
long number2 = number1;
// kotlin
// 코틀린에서는 암시적 타입 변경이 불가능합니다.
// 아래 처럼 toLong 또는 Long -> Int로 변경해야 합니다.
fun typeCheck() {
val number1 = 4
val number2: Long = number1.toLong()
println(number1 + number2)
}
val number1: Int = 4
val number2: Long = number1.toLong()
val number1 = 3
val number2 = 5
val result = number1 / number2.toDouble()
타입 캐스팅
코틀린에서는 is, !is, as, as? 를 이용해 타입을 확인하고 캐스팅 합니다.
기본 타입이 아닌 일반 객체 타입의 처리는 아래와 같습니다.
// java
public static void pringAgeIfPerson(Object obj) {
if (obj instanceof Person) {
Person person = (Person) obj;
System.out.println(person.getAge());
}
}
// kotlin
fun pringAgeIfPerson(obj: Any) {
if ( obj is Person ) {
val person = obj as Person // as Person 은 자동으로 만들어짐
println(person.age)
}
}
fun pringAgeIfPerson(obj: Any) {
if ( obj is Person ) {
println(obj.age) // 스마트 캐스트
}
}
// type 에서의 null 체크
fun printAgeIfPersonNull(obj: Any?) {
val person = obj as? Person
println(person?.age)
}
Kotlin의 3가지 특이한 타입
Any <-> Object
- Java의 Object 역할 ( 모든 객체의 최상위 타입 )
- 모든 Primitive Type의 최상의 타입도 Any이다.
- Any 자체로는 null을 포함할 수 없어 null을 포함하고 싶다면 Any?로 표현
- Any에 equals / hasCode / toString 존재
Unit <-> Void
- Unit는 Java의 Void와 동일한 역할
- void와는 다르게 Unit는 그 자체로 타입 인자로 사용 가능하다.
- 함수형 프로그래밍에서 Unit는 단 하나의 인스턴스만 갖는 타입을 의미 즉, 코틀린의 Unit은 실제 존재하는 타입이라는 것을 표현
Nothing
- 함수가 정상적으로 끝나지 않았다는 사실을 표현하는 역할
- 무조건 예외를 반환하는 함수 / 무한 루프 함수 등
fun fail(message: String): Nothing {
throw IllegaArgumentException(message)
}
String Interpolation, String indexing
자바에서는 문자열을 연결할 때 StringBuilder 이나 %s 등으로 사용한다. 하지만, 코틀린에서는 사용 방법이 아래와 같다.
var person = Person("name", 10)
var log = "사람의 이름은 ${person.name}이고 나이는 ${person.age}세 입니다"
특정 문자열의 index 로 가져오기
// java
String str = "ABC";
char ch = str.charAt(1);
"B"
// kotlin
var str = "ABC"
var ch = str[1]
"B"
코틀린에서 연산자를 다루는 방법
단항 연산자 / 산술 연산자
코틀린에서의 단항/산술 연산자는 자바와 완전 동일합니다.
- 단항연산자 : ++, --
- 산술연산자 : +, -, *, /, %
- 산술대입연산자 : +=, -=. *=, /=, %=
비교 연산자와 동등성, 동일성
비교연산자 : >, <, >=, <=
자바, 코틀린 사용법은 동일합니다. 단, 객체를 비교할 때 Java와 다르게 비교 연산자를 사용하면 자동으로 compareTo를 호출해줍니다.
동등성(Equality) : 두 객체의 값이 같은가
동일성(Identity) : 완전히 동일한 객체인가? 즉 주소가 같은가?
자바에서 두 인스턴스의 정체성이 동일하다 (동일성, 객체의 주소가 같은가) : == 사용
자바에서 두 인스턴스의 값이 동등하다 (동등성, 값이 같은가) : equals 사용
코틀린에서 동일성 : ===
코틀린에서 동등성 : == 을 사용하면 간접적으로 equals를 호출
논리연산자와 코틀린에 있는 특이한 연산자
논리연산자 : &&, ||, !
자바와 완전히 동일합니다. Java 처럼 Lazy 연산을 수행
특이한 연산자
in / !in : 컬렉션이나 범위에 포함되어 있다, 포함되어 있지 않다를 표현합니다.
println(1 in numbers)
println(1 !in numbers)
a..b : a부터 b 까지의 범위 객체를 생성한다
연산자 오버로딩
코틀린에서는 객체마다 연산자를 직접 정의할 수 있다.
fun calculateMoney() {
val money1 = JavaMoney(1_000L)
val money2 = JavaMoney(3_000L)
println(money1 + money2) // JavaMoney{amount=4000}
}
코틀린에서 조건문을 다루는 방법
if 문 & Expression과 Statement
자바에서 if-else는 Statement 이지만 Kotlin에서는 Express 입니다.
- Satement : 프로그램의 문장, 하나의 값으로 도출되지 않는다.
- Expression : 하나의 값으로 도출되는 문장
- int score = 30 + 40 -> 70이라는 하나의 값이 나옵니다. Expression 이면서 Statement
예를 들어 아래 코드처럼 java에서 하나의 값으로 취급하게 되면 에러가 발생합니다.
if 문을 하나의 값을 취급하지 않으니 에러가 발생합니다. 하지만 코틀린에서는 if 문 또한 하나의 값으로 취급할 수 있습니다.
코틀린에서는 if-else를 expression으로 사용할 수 있기 때문에 3항 연산자가 없습니다.
String grade = if ( score >= 50 ) {
"P"
} else {
"F"
}
// 자바에서 하나의 값으로 취급 하기
// 3항 연산자 사용
String grade = score >= 50 ? "P" : "F";
fun validateScoreIsNotNegative(score: Int) : Unit {
if ( score < 0 ) {
throw IllegalArgumentException("${score}는 0보다 작을 수 없습니다.")
}
}
fun getPassOrFail(score: Int) : String {
return if ( score >= 50 ) {
"P";
} else {
"F";
}
}
fun getGrade(score: Int) : String {
// 하나의 값으로 취급 가능 Expression 하고 Statement 하다.
return if ( score >= 90 ){
"A"
} else if ( score >= 80 ) {
"B"
} else if ( score >= 70 ) {
"C"
} else if ( score >= 60 ) {
"D"
} else {
"F"
}
}
// if ( score >=0 && score <= 100) {
fun getRange(score: Int) : Boolean {
return if ( score in 0..100) {
true
} else {
false
}
}
switch와 when
코틀린에서의 switch when 문이 없습니다 대신 아래와 같이 사용할 수 있습니다. 또한, 자바의 switch case 문 보다 휠씬 유연하며, enum class, sealed class와 함께 사용할 경우, 더욱더 진가를 발휘한다.
when (값) { --> 값이 없을경우는 early retrun 처럼 동작
조건부 -> 어떠한 구문
조건부 -> 어떠한 구믄
else -> 어떠한 구문
}
fun getGradeWithSwitch(score: Int): String {
return when (score) {
in 90..100 -> "A"
in 80..89 -> "B"
in 70..79 -> "C"
in 60..69 -> "D"
else -> "F"
}
}
fun getGradeWithSwitch(score: Int): String {
return when ( score / 10 ) {
9 -> "A"
8 -> "B"
7 -> "C"
else -> "D"
}
}
fun startWithA(obj: Any) : Boolean {
return if ( obj is String ) {
obj.startsWith("A")
} else {
false
}
}
fun startWithB(obj: Any) : Boolean {
return when (obj) {
is String -> obj.startsWith("A")
else -> false
}
}
// when에 값이 있다면 값에 따라서 조건을 살펴보고
fun judgeScore(number: Int): Unit {
when ( number ) {
1,0,-1 -> println("same")
else -> println("not same")
}
}
// when에 값이 없다면 들어온 값에 따라서 조건을 수행한다.
fun judgeNumber2(number: Int) {
when {
number == 0 -> println("주어진 숫자는 0 입니다.")
number % 2 == 0 -> println("주어진 숫자는 짝수 입니다.")
else -> println("주어진 숫자는 홀수 입니다.")
}
}
조건문 정리
- if / if - else / if - else if - else 모두 자바 문법이 동일하다.
- 단 코틀린에서는 Expression으로 취급된다.
- 때문에 코틀린에서는 삼항 연산자가 없다.
- 자바의 switch는 코틀린에서 when으로 대체되었고, when은 더 유연하며 더 강력한 기능을 갖습니다.
코틀린에서 반복문을 다루는 방법
코틀린에서의 반복문은 java와 굉장히 유사하다.
for-each 문 & 전통적인 for 문 & Progression 과 Range
// for each 문
fun getListOf() {
val list = listOf(1, 2, 3, 4, 5) // 컬럭션 만드는 방법
for (i in list) {
println(i)
}
}
// 전통적인 for 문
// for 1~3 까지 출력
// for 3~1 까지 출력
// for 2칸씩 올라가는 경우
fun getForLoop() {
for (i in 1..3) {
println(i)
}
for (i in 3 downTo 1) {
println(i)
}
for (i in 1..3 step 2) {
println(i)
}
}
코틀린에서 예외를 다루는 방법
try catch finally 구문
자바 구문과 동일하다. 단 Expression으로 사용할 수 있다.
// try catch finally
fun getParseIntOrThrow(str: String) : Int {
return try {
str.toInt()
} catch (e: NumberFormatException) {
throw IllegalArgumentException("주어진 ${str} 숫자가 아닙니다.")
} finally {
println("M")
}
}
// 실패하면 null 반환
fun parseIntOrThrowV2(str: String) : Int? {
return try {
str.toInt()
} catch (e: NumberFormatException) {
null
}
}
Chekced Exception & Unchecked Exception
코틀린에서는 checked exception이 없다. 모두 unchecked exception 이다.
아래 코드를 보면 자바에서는 파일을 읽는 인스턴스를 사용하게 되면 checked exception을 명시해야만 한다. 하지만 코틀린은 모두 unchecked exception으로 생략이 가능하다.
-- java
public void readFile(String path) throws IOException {
File currentFile = new File(".");
FIle file = new File(currentFile.getAbsolutePath() + "/a.txt");
BufferedReader reader = new BufferedReader(new FileReader(file));
System.out.println(reader.readLine());
reader.close();
}
-- kotlin
fun readFile(path: String) : Unit {
val currentFile = File(".")
val file = File(currentFile.absolutePath + "a.txt")
val reader = BufferedReader(FileReader(file))
println(reader.readLine())
reader.close()
}
예외 정리
- try catch finally 구문은 문법적으로 완전히 동일하다.
- 코틀린에서 try catch가 expression 이다.
- 코틀린에서 모든 예외는 unchecked exception이다.
- 코틀린에서는 try with resources 구문이 없다. 대신 코틀린의 언어적 특징을 활용해 close를 호출해준다.
코틀린에서 함수를 다루는 방법
함수 선언 문법
두 정수를 받아 더 큰 정수를 반환하는 예제
// 접근 지시어 public 는 default 로 생략이 가능하다.
// fun 함수를 의미하는 키워드
// max 함수 이름
// a: Int, b: Int ( 매개변수 명 : 타입 )
// : Int ( 리턴 타입 )
public fun max(a: Int, b:Int) : Int {
return if ( a > b ) {
a
} else {
b
}
}
// 함수가 하나의 결과값이면 block 대신 = 사용 가능하다.
public fun maxV2(a: Int, b:Int) : Int =
if ( a > b ) {
a
} else {
b
}
// 함수가 하나의 결과값이면 block 대신 = 사용 가능하며 한줄로 변경 가능
// = 을 사용하는 경우는 반환 타입 생략이 가능하다.
fun maxV3(a: Int, b: Int) = if ( a > b ) a else b
- block {} 을 사용하는 경우에는 반환 타입이 Unit이 아니면 명시적으로 반환 타입을 작성해 주어야 합니다.
- 함수는 클래스 안에 있을 수도, 파일 최상단에 있을 수도 있습니다.
- 또한, 한 파일 안에 여러 함수들이 있을 수도 있습니다.
default parameter
public void repeat(String str, int num, boolean useNewLine) {
for ( int i=0; i<=num; i++ ) {
if (useNewLine) {
System.out.println(str);
}
else {
System.out.println(str);
}
}
}
// 많은 코드에서 useNewLine 를 사용한다면 ?
// 메소드 오버로딩을 사용하여 해결
public void repeat(String str, int num) {
repeat(str, num, true);
}
// 많은 코드에서 출력을 3회씩 사용한다면?
// 다시 한번 오버로딩을 사용
public void repeat (String str) {
repeat(str, 3, true);
}
위 코드는 자바로 작성된 코드입니다. 각각의 사용에 맞게 메소드 오버로딩을 이용하여 함수를 호출하고 있습니다. 각각의 상황에 맞게 하기 위해 메소드가 3개가 작성되었습니다. 코틀린에서는 이와 같은 경우 간단히 제어할 수 있는 문법이 제공됩니다.
fun main() {
repeat("test")
}
// 위 메인 함수에서 repeat 인자 값으로 주어지지 않으면 기본 값을 사용하게된다.(default parameter)
fun repeat(str: String, num: Int = 3, useNewLine: Boolean = true) : Unit {
for ( i in 1..num) {
if ( useNewLine) {
println(str)
}
else {
println(str)
}
}
}
물론 코틀린에서 Java와 동이랗게 오버로딩 기능이 있습니다.
매개변수 이름을 통해 직접 지정, 지정되지 않은 매개 변수는 기본 값을 사용
named argument ( parameter )
builder를 직접 만들지 않고 builder의 장점을 가지게 된다. 이로인해 자바의 빌더로 인자값을 넣는 것과 동일한 효과를 볼 수 있다.
코틀린에서 java 함수를 가져다 사용할 때는 named argument를 사용할 수 없다.
repeat("hello world", useNewLine = false)
같은 타입의 여러 파라미터 받기 ( 가변 인자 )
문자열 N개를 받아 출력하는 예제
// java
// List<String> data = List.of("a1", "b1", "c1");
// printAll( "a", "b", "c");
// printAll(String.valueOf(data));
public static void printAll(String... strings) {
for (String string : strings) {
System.out.println("string = " + string);
}
}
// kotlin
// printAll("hello", "world")
// val array = arrayOf("hello", "world");
// printAll(*array) 배열을 바로 넣는 대신 연산자 (*)를 붙여주어야 한다.
// 자바에서의 ... 대신 vararg 사용
fun printAll(vararg strings: String) {
for (string in strings) {
println(string)
}
}
함수 다루는 방법 정리
- 함수의 문법은 자바와 다르다
- 접근 지시어 fun 함수이름(파라미터): 반환타입 {}
- body가 하나의 값으로 간주되는 경우 block를 없앨 수도 있고, block가 없다면 반환 타입을 없앨 수도 있다.
- 함수 인자에 기본값을 설정할 수 있다.
- 가변 인자에는 vararg 키워드를 사용하며, 가변인자 함수를 벼열과 호출할 때는 *를 붙여주어야 한다.
코틀린에서 클래스를 다루는 방법
아래는 자바 코이이며, 이를 코틀린과 비교하여 작성하겠습니다.
public class JavaPerson {
private final String name;
private int age;
public JavaPerson(String name, int age) {
if (age <= 0) {
throw new IllegalArgumentException(String.format("나이는 %s일 수 없습니다", age));
}
this.name = name;
this.age = age;
}
public JavaPerson(String name) {
this(name, 1);
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public boolean isAdult() {
return this.age >= 20;
}
}
클래스와 프러퍼티
package com.lannstrak.lec09
fun main() {
val person = Person("홍길동", 20)
// .필드를 통해 gettter 와 setter 을 바로 호출할 수 있다.
println(person.name);
println(person.age);
person.age = 30; //
}
// 코틀린에서 주 생성자는 클래스 선언과 동시에 작성
// getter, setter 를 자동으로 생성
public class Person constructor(
val name: String,
var age: Int
) {
}
코틀린에서는 getter, setter이 자동으로 생성되기 때문에 자바처럼 get,set 메소드를 별도로 만들거나 lombok를 사용하여 어너테이션을 작성할 필요가 없습니다. .필드 를 통해 바로 접근 가능합니다.
생성자와 init
코틀린에서의 주 생성자는 클래스 선언과 동시에 사용하는게 일반적입니다.
위 자바 코드에서 인스턴스 생성과 동시에 나이를 검증하는 로직이 있는데요. 코틀린에서는 인스턴스 생성과 동시에 작업을 하기 위해서는 int 블럭을 사용하여 검증할 수 있습니다.
public class Person constructor(
val name: String,
var age: Int
) {
init {
if ( age <= 0 ) {
throw IllegalArgumentException("나이는 ${age}보다 커야 합니다.")
}
}
}
추가적으로 생성자를 만들고 싶다면 아래와 같이 하면 됩니다.
// 코틀린에서 주 생성자는 클래스 선언과 동시에 작성
// getter, setter 를 자동으로 생성
public class Person constructor(
val name: String,
var age: Int
) {
init {
if ( age <= 0 ) {
throw IllegalArgumentException("나이는 ${age}보다 커야 합니다.")
}
}
constructor(name: String) : this(name, 0) {
println("보조 생성자 호출")
}
constructor() : this("이름 없음") {
println("보조 생성자 호출")
}
}
하지만 부 생성자의 경우 거의 사용할 일이 없으며 이럴 경우 default parameter or 정적팩토리를 만들어 사용하는걸 추천드립니다.
// 코틀린에서 주 생성자는 클래스 선언과 동시에 작성
// getter, setter 를 자동으로 생성
public class Person constructor(
val name: String = "",
var age: Int = 0
) {
init {
if ( age <= 0 ) {
throw IllegalArgumentException("나이는 ${age}보다 커야 합니다.")
}
}
}
// 코틀린에서 주 생성자는 클래스 선언과 동시에 작성
// getter, setter 를 자동으로 생성
public class Person constructor(
val name: String,
var age: Int
) {
// 클래스가 생성되는 시점에 호출 한다.
// 이는 자바 생성자에서 로직을 처리하는 것과 동일하다.
init {
if ( age <= 0) {
throw IllegalArgumentException("나이는 ${age}로 설정할 수 없습니다.")
}
}
companion object {
// 정적 팩토리 메서드 - 이름과 나이를 사용하는 방식
fun fromNameAndAge(name: String, age: Int): Person {
return Person(name, age)
}
// 정적 팩토리 메서드 - 이름만 사용하는 방식 (나이는 기본값 1로 설정)
fun withNameOnly(name: String): Person {
return Person(name, 1)
}
// 정적 팩토리 메서드 - 기본값 사용 (이름 없음, 나이 1)
fun defaultPerson(): Person {
return Person("이름 없음", 1)
}
}
}
커스텀 getter, setter
예를 들어 성인 여부를 확인하는 함수를 추가 하겠습니다.
public class Person constructor(
val name: String = "",
var age: Int = 0
) {
// 함수로 getter 를 대체할 수 있다.
fun isAdult(): Boolean {
return this.age >= 20
}
}
만약 하나의 결과값을 도출하는 것 이면 아래와 같이 custom gettter 형식으로 만들 수 있습니다.
val isAdult: Boolean
get() = this.age >=20
위 두개의 방법중 어떤 방법을 사용하는 것이 좋을까요?
객체의 속성이라면 ( 객체가 포함하는 멤버변수 ) custom getter를 그렇지 않다면 함수 방식을 선호합니다.
또 하나의 예로 name 을 가져올 때 무조건 대문자로만 가져오고 싶다면? 이는 객체의 속성이므로 custom getter 방식을 사용합니다.
val nameUpperCase: String
get() = name.uppercase()
---
val name: String = this.name
get() = field.uppercase() // field or this 사용
name에 대한 custom getter를 만들 때 field를 사용합니다. 왜 field를 사용할까요?
name은 name에 대한 getter를 호출 하니깐 다시 get을 부릅니다. getter 안에는 다시 name이 있다. -> 무한 루프 발생
무한루프를 발생을 막기위해 field 라는 자신을 가르키는 예약어를 사용합니다. 이를 backing field라고 합니다.
하지만 custom getter 에서 backing field를 쓰는 경우는 거의 없습니다.
아래 코드는 name를 set할 때 무조건 대문자로 바꾸는 코드입니다.
var name: String = name
set(value) {
field = value.uppercase()
}
사실은 setter 자체를 지양하기 때문에 custom setter도 잘 안쓴다!
클래스 정리
- 코틀린에서는 필드를 만들면 getter와 (필요에 따라) setter가 자동으로 생긴다.
- 때문에 이를 프로퍼티라고 부른다.
- 코틀린에서는 주생성자가 필수다.
- 코틀린에서는 consturcotr 키워드를 사용해 부생성자를 추가로 만들 수 있다.
- 단 default parameter나 정적 팩토리 메소드를 추천한다.
- 실제 메모리에 존재하는 것과 무관하게 custom getter와 custom setter를 만들 수 있다.
- custom getter, setter 에서 무한루프를 막기 위해 field라는 키워드를 사용한다.
- 이를 backing field라고 부른다.
코틀린에서 상속을 다루는 방법
public abstract class JavaAnimal {
protected final String species;
protected final int legCount;
public JavaAnimal(String species, int legCount) {
this.species = species;
this.legCount = legCount;
}
abstract public void move();
public String getSpecies() {
return species;
}
public int getLegCount() {
return legCount;
}
}
추상 클래스
인터페이스
클래스를 상속할 때 주의할 점