diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..99c3817 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..f5a0c5d --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/kotlin-study.iml b/.idea/kotlin-study.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/.idea/kotlin-study.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 0000000..fdf8d99 --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..e800438 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..aec898a --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/kotlin-baseball/src/main/kotlin/KtInActionPractice/JointKt.kt b/kotlin-baseball/src/main/kotlin/KtInActionPractice/JointKt.kt new file mode 100644 index 0000000..0cff2c0 --- /dev/null +++ b/kotlin-baseball/src/main/kotlin/KtInActionPractice/JointKt.kt @@ -0,0 +1,45 @@ +package KtInActionPractice + +const val UNIT_LINE_SEPARATOR = "\n" + +fun joinToString(collection: Collection, + separator: String, + prefix: String, + postfix: String): String { + val result = StringBuilder(prefix) + for ((index, element) in collection.withIndex()) { + if (index > 0) result.append(separator) + result.append(element) + } + + result.append(postfix) + return result.toString() +} + +fun String.lastChar(): Char = this.get(length - 1) + +var StringBuilder.lastChar: Char + get() = get(length - 1) + set(value: Char) { + this.setCharAt(length - 1, value) + } + +fun Collection.joinToString2( + separator: String = ", ", + prefix: String = "", + postfix: String = "" +): String { + val result = StringBuilder(prefix) + for ((index, element) in this.withIndex()) { + if (index > 0) result.append(separator) + result.append(element) + } + result.append(postfix) + return result.toString() +} + +fun Collection.joinToString3( + separator: String = ", ", + prefix: String = "", + postfix: String = "" +) = joinToString2(separator, prefix, postfix) \ No newline at end of file diff --git a/kotlin-baseball/src/main/kotlin/KtInActionPractice/KtInActionPart1.kt b/kotlin-baseball/src/main/kotlin/KtInActionPractice/KtInActionPart1.kt new file mode 100644 index 0000000..2584196 --- /dev/null +++ b/kotlin-baseball/src/main/kotlin/KtInActionPractice/KtInActionPart1.kt @@ -0,0 +1,35 @@ +package KtInActionPractice + +/* +* 코틀린의 주요 특성 +* 코틀린을 도입하면 더 적은 코드로 더 편하게 프로그래머의 목표를 달성할 수 있다. +* +* 정적 타입 지정 언어 +* 자바랑 똑같이 타입을 컴파일 시점에 알 수 있음. +* 이로 인해서 오는 장점이 성능, 신뢰성, 유지 보수성, 도구 지원 등이 있다고 함. 개인적으로는 컴파일 안 하는 언어는 왜 쓰는지 모르겠음 ㅋㅋ; +* +* 함수형 프로그래밍과 객체지향 프로그래밍 +* 어제 자면서 본 레퍼런스에서 코틀린과 스칼라가 자주 비교됨. 이유는 스칼라가 함수형 프로그래밍 언어이기 때문인 것 같은데 코틀린도 함수형 프로그래밍 언어를 좋아함. +* 함수형 프로그래밍의 장점 +* 1. 간결함. 명령형(imperative) 코드에 비해 더 간결하며 우아함. +* 2. 다중 스레드에도 안전함. 불변 데이터 구조를 사용하고 순수 함수를 그 데이터 구조에 적용하면 같은 데이터를 여러 스레드가 변경할 수 없음. 그래서 복잡한 동기화를 적용하지 않아도 됨. (아직 이해 x) +* 3. 테스트하기 쉬움. 자바는 전체 환경을 구성하는 준비 코드가 필요한데 순수 함수는 그런 준비 없이 독립적으로 테스트가 가능하다고 함. 이번 스터디에서 이 부분에 대해 공부해 봐야 할 듯!! +* +* 코틀린의 철학 +* 실용성 +* 코틀린은 실제 문제를 해결하기 위해 만들어진 언어라고 함. 연구를 하기 위한 언어가 아니고 이미 성공적으로 검증된 해법과 기능에 의존함. 이 부분이 개인적으로 마음에 듬. +* +* 간결성 +* 개인적으로 자바에서 쓸모없는 코드가 몇 개 보였음. private final 같은.. 자바는 쓸모 없는 코드 때문에 코드의 양이 엄청 많아짐. +* 이렇게 코드의 양이 많아지면 읽기가 싫어지고 유지보수하기 싫어짐. 이는 공학작문및발표 수업에서 배웠고 매우 동의하는 바임. +* 코드가 간결할수록 내용을 파악하기 더 쉬움. 코틀린은 간결하면서도 의도를 쉽게 파악할 수 있는 구문 구조를 제공함. 이 부분도 개인적으로 마음에 듬. +* 하지만 책을 조금 읽어봤을 때 너무 간결해 오히려 불편함.. 조금 적응하면 나아질 수 있다고 생각함. +* +* 안전성 +* 코틀린은 자바보다 NPE에 안전함. 보통 언어가 안전하다면 생산성이 하락하는데 코틀린에서는 ? 한 글자만 추가하면 됨. 굉장히 간결함. +* 또, 타입 캐스팅을 할 때도 극도로 간결함. 자바의 필요 없는 구문들이 거의 제거 됨. +* +* */ + +class KtInActionPart1 { +} \ No newline at end of file diff --git a/kotlin-baseball/src/main/kotlin/baseball/Application.kt b/kotlin-baseball/src/main/kotlin/baseball/Application.kt index 148d75c..2aef0d7 100644 --- a/kotlin-baseball/src/main/kotlin/baseball/Application.kt +++ b/kotlin-baseball/src/main/kotlin/baseball/Application.kt @@ -1,5 +1,12 @@ package baseball +import step1.* +import step2.api.BaseballController +import step2.application.BaseballService +import step2.global.BaseballApplication +import step2.view.BaseballView + fun main() { - TODO("프로그램 구현") + BaseballApplication(BaseballView(), BaseballController(BaseballService())).run() } + diff --git a/kotlin-baseball/src/main/kotlin/step1/BaseBallGame.kt b/kotlin-baseball/src/main/kotlin/step1/BaseBallGame.kt new file mode 100644 index 0000000..aa8845d --- /dev/null +++ b/kotlin-baseball/src/main/kotlin/step1/BaseBallGame.kt @@ -0,0 +1,52 @@ +package step1 + +import camp.nextstep.edu.missionutils.Randoms + +fun announceBaseballGameStartMessage() = println("숫자 야구 게임을 시작합니다.") + +fun announceEnterNumbersMessage() = print("숫자를 입력해주세요 : ") + +fun announceThreeStrike() = println("3개의 숫자를 모두 맞히셨습니다! 게임 종료") + +fun announceBaseballRestartMessage() = println("게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요.") + +fun announceBaseballResult(userInputNumbers: String, randomNumbers: String) = println(calculateBaseballGameResult(userInputNumbers, randomNumbers)) + +fun pickRandomNumbers(): String { + val randomNumbers = mutableListOf() + while (randomNumbers.size < 3) { + val randomNumber = Randoms.pickNumberInRange(1, 9) + if (!randomNumbers.contains(randomNumber)) { + randomNumbers.add(randomNumber) + } + } + return randomNumbers.joinToString(separator = "") +} + +fun isUserInputValid(userInputNumbers: String): Boolean = userInputNumbers.length == 3 && userInputNumbers.toIntOrNull() != null + + +private fun calculateBaseballGameResult(userInputNumbers: String, randomNumbers: String): String { + val result = StringBuilder() + var strikeCount = 0 + var ballCount = 0 + + for (i in randomNumbers.indices) { + if (userInputNumbers[i] == randomNumbers[i]) { + strikeCount++ + } else if (userInputNumbers[i] in randomNumbers) { + ballCount++ + } + } + + if (ballCount > 0) { + result.append("${ballCount}볼 ") + } + + if (strikeCount > 0) { + result.append("${strikeCount}스트라이크") + } + + return if (ballCount == 0 && strikeCount == 0) "낫싱" else result.toString() + +} \ No newline at end of file diff --git a/kotlin-baseball/src/main/kotlin/step1/StepOneBaseballGameMain.kt b/kotlin-baseball/src/main/kotlin/step1/StepOneBaseballGameMain.kt new file mode 100644 index 0000000..02d5283 --- /dev/null +++ b/kotlin-baseball/src/main/kotlin/step1/StepOneBaseballGameMain.kt @@ -0,0 +1,28 @@ +package step1 + +import camp.nextstep.edu.missionutils.Console + +fun stepOneRunner() { + announceBaseballGameStartMessage() + var randomNumbers = pickRandomNumbers() + + while (true) { + announceEnterNumbersMessage() + val readLine = Console.readLine() + if (!isUserInputValid(readLine)) { + throw IllegalArgumentException() + } + + announceBaseballResult(readLine, randomNumbers) + if (readLine.isThreeStrike(readLine, randomNumbers)) { + announceThreeStrike() + announceBaseballRestartMessage() + if (Console.readLine() == "2") break + randomNumbers = pickRandomNumbers() + } + } +} + + +fun String.isThreeStrike(numbers: String, randomNumbers: String): Boolean = + numbers[0] == randomNumbers[0] && numbers[1] == randomNumbers[1] && numbers[2] == randomNumbers[2] \ No newline at end of file diff --git a/kotlin-baseball/src/main/kotlin/step2/api/BaseballController.kt b/kotlin-baseball/src/main/kotlin/step2/api/BaseballController.kt new file mode 100644 index 0000000..66d79a5 --- /dev/null +++ b/kotlin-baseball/src/main/kotlin/step2/api/BaseballController.kt @@ -0,0 +1,20 @@ +package step2.api + +import step2.application.BaseballService +import step2.dto.BaseballResultDto +import step2.view.BaseballView + +class BaseballController( + val baseballService: BaseballService +) { + + fun saveRandomNumbers() { + baseballService.createBaseball() + } + + fun getBaseballGameResult(userInputNumbers: String): BaseballResultDto = + requireNotNull(userInputNumbers.takeIf { it.length == 3 && it.toIntOrNull() != null }) { + "유효하지 않은 숫자 또는 문자입니다." + }.let { baseballService.compareRandomNumbers(it) } + +} \ No newline at end of file diff --git a/kotlin-baseball/src/main/kotlin/step2/application/BaseballService.kt b/kotlin-baseball/src/main/kotlin/step2/application/BaseballService.kt new file mode 100644 index 0000000..cd703a0 --- /dev/null +++ b/kotlin-baseball/src/main/kotlin/step2/application/BaseballService.kt @@ -0,0 +1,17 @@ +package step2.application + +import step2.dto.BaseballResultDto +import step2.entity.RandomNumbers +import step2.global.createUniqueRandomNumbers + +class BaseballService { + + fun createBaseball() { + val createUniqueRandomNumbers = createUniqueRandomNumbers(3) + RandomNumbers.changeRandomNumbers(createUniqueRandomNumbers) + } + + fun compareRandomNumbers(userInputNumbers: String): BaseballResultDto = + BaseballResultDto(RandomNumbers.countStrike(userInputNumbers), RandomNumbers.countBall(userInputNumbers)) + +} \ No newline at end of file diff --git a/kotlin-baseball/src/main/kotlin/step2/dto/BaseballResultDto.kt b/kotlin-baseball/src/main/kotlin/step2/dto/BaseballResultDto.kt new file mode 100644 index 0000000..d0f7459 --- /dev/null +++ b/kotlin-baseball/src/main/kotlin/step2/dto/BaseballResultDto.kt @@ -0,0 +1,6 @@ +package step2.dto + +data class BaseballResultDto( + val strikeCount: Int, + val ballCount: Int, +) diff --git a/kotlin-baseball/src/main/kotlin/step2/entity/RandomNumbers.kt b/kotlin-baseball/src/main/kotlin/step2/entity/RandomNumbers.kt new file mode 100644 index 0000000..4b88001 --- /dev/null +++ b/kotlin-baseball/src/main/kotlin/step2/entity/RandomNumbers.kt @@ -0,0 +1,14 @@ +package step2.entity + +object RandomNumbers { + private var randomNumber: String? = null + + fun changeRandomNumbers(number: String): String { + this.randomNumber = number + return this.randomNumber!! + } + + fun countStrike(number: String): Int = number.indices.count { i -> number[i] == randomNumber!![i] } + + fun countBall(number: String): Int = number.indices.count { i -> number[i] != randomNumber!![i] && number[i] in randomNumber!! } +} diff --git a/kotlin-baseball/src/main/kotlin/step2/global/ExtensionUtils.kt b/kotlin-baseball/src/main/kotlin/step2/global/ExtensionUtils.kt new file mode 100644 index 0000000..7a530b1 --- /dev/null +++ b/kotlin-baseball/src/main/kotlin/step2/global/ExtensionUtils.kt @@ -0,0 +1,12 @@ +package step2.global + +import camp.nextstep.edu.missionutils.Console +import camp.nextstep.edu.missionutils.Randoms + +fun createUniqueRandomNumbers(count: Int): String = + generateSequence { Randoms.pickNumberInRange(1, 9) } + .distinct() + .take(count) + .joinToString("") + +fun getThreeUniqueNumbers(): String = Console.readLine() ?: throw IllegalArgumentException() diff --git a/kotlin-baseball/src/main/kotlin/step2/global/RunApplication.kt b/kotlin-baseball/src/main/kotlin/step2/global/RunApplication.kt new file mode 100644 index 0000000..90ef39d --- /dev/null +++ b/kotlin-baseball/src/main/kotlin/step2/global/RunApplication.kt @@ -0,0 +1,48 @@ +package step2.global + +import camp.nextstep.edu.missionutils.Console +import camp.nextstep.edu.missionutils.Randoms +import step2.api.BaseballController +import step2.entity.RandomNumbers +import step2.view.BaseballView +import kotlin.system.exitProcess + +class BaseballApplication( + val baseballView: BaseballView, + val baseballController: BaseballController +) { + + fun run() { + // STEP1 > 게임 시작 문구 출력 + baseballView.announceBaseballGameStart() + + while (true) { + // STEP2 > 랜덤한 숫자 저장 + baseballController.saveRandomNumbers() + + // STEP3 > 숫자 입력 메시지 출력 + baseballView.printReceiveNumberMessage() + + // STEP4 > 숫자 입력 + val userInputNumbers = getThreeUniqueNumbers() + + // STEP5 > 결과 출력 + val baseballGameResult = baseballController.getBaseballGameResult(userInputNumbers) + + // STEP6 > 게임 종료 문구 출력 + baseballView.announceGameResult(baseballGameResult) + + // STEP7 > 새로 시작 or 종료 + baseballGameResult.strikeCount.takeIf { it == 3 } + ?.apply { println(""" + 3개의 숫자를 모두 맞히셨습니다! 게임 종료 + 게임을 새로 시작하려면 1, 종료하려면 2를 입력하세요. + """.trimIndent()) } + ?.apply { Console.readLine() + .takeIf { it == "2" } + ?.apply { exitProcess(0) } } + + } + } + +} \ No newline at end of file diff --git a/kotlin-baseball/src/main/kotlin/step2/view/BaseballView.kt b/kotlin-baseball/src/main/kotlin/step2/view/BaseballView.kt new file mode 100644 index 0000000..1e5e68e --- /dev/null +++ b/kotlin-baseball/src/main/kotlin/step2/view/BaseballView.kt @@ -0,0 +1,20 @@ +package step2.view + +import step2.dto.BaseballResultDto + +class BaseballView { + + fun announceBaseballGameStart() = println("숫자 야구 게임을 시작합니다.") + + fun printReceiveNumberMessage() = print("숫자를 입력해주세요 : ") + fun announceGameResult(baseballGameResult: BaseballResultDto) = + println( + with(baseballGameResult) { + listOfNotNull( + ballCount.takeIf { it > 0 }?.let { "${it}볼" }, + strikeCount.takeIf { it > 0 }?.let { "${it}스트라이크" } + ).takeIf { it.isNotEmpty() }?.joinToString(" ") ?: "낫싱" + } + ) + +} \ No newline at end of file diff --git a/kotlin-baseball/src/test/kotlin/KtInActionPractice/chapter2/KtInActionPart2.kt b/kotlin-baseball/src/test/kotlin/KtInActionPractice/chapter2/KtInActionPart2.kt new file mode 100644 index 0000000..9e85ddb --- /dev/null +++ b/kotlin-baseball/src/test/kotlin/KtInActionPractice/chapter2/KtInActionPart2.kt @@ -0,0 +1,145 @@ +package KtInActionPractice.chapter2 + +import org.junit.jupiter.api.Test +import java.io.BufferedReader +import java.io.StringReader +import java.util.TreeMap + +class KtInActionPart2Test { + + @Test + fun printHelloWorld() { + println("Hello, World") + } + + @Test + fun maxTest() { + println(max(3, 2)) + println(max(2,4)) + } + + /* + * 식이 본문인 함수의 반환 타입만 생략이 가능하다. + * */ + private fun max(a: Int, b: Int): Int = if (a > b) a else b + + @Test + fun variable() { + val languages = arrayListOf("Java") + languages.add("Kotlin") + println(languages) + } + + @Test + fun stringTemplate() { + val name = "Kotlin" + println("Hello, $name!") + println("Hello, ${name}님 반가워용") +// println("Hello, $name님 반가워용") + } + + @Test + fun classTest() { + val person = Person("ggang9", false) + println(person.name) + println(person.isMarried) + println(person) + } + + class Person ( + val name: String, + val isMarried: Boolean + ) + + @Test + fun enumTest() { + println(Color2.BLUE.rgb()) + } + + enum class Color { + RED, ORANGE, YELLOW + } + + enum class Color2( + val r: Int, val g: Int, val b: Int + ) { + RED(255, 0, 0), + ORANGE(0, 255, 0), + BLUE(0, 0, 255) + ; + + fun rgb() = (r * 256 + g) % 256 + b + } + + @Test + fun enumTest2() { + println(getMnemonic(Color2.BLUE)) + } + + private fun getMnemonic(color: Color2) = + when (color) { + Color2.RED -> "Richard" + Color2.BLUE -> "Battle" + Color2.ORANGE -> "Of" + } + + @Test + fun testForLoop() { + for (i in 1..100) { + println(fizzBuzz(i)) + } + } + + private fun fizzBuzz(i: Int) = when { + i % 15 == 0 -> "FizzBuzz" + i % 3 == 0 -> "Fizz" + i % 5 == 0 -> "Buzz" + else -> "${i}" + } + + @Test + fun testForLoop2() { + for (i in 100 downTo 1 step 2) { + println(fizzBuzz(i)) + } + } + + @Test + fun testMapIter() { + val binaryReps = TreeMap() + for (c in 'A'..'F') { + val binary = Integer.toBinaryString(c.code) + binaryReps[c] = binary + } + + for ((letter, binary) in binaryReps) { + println("$letter = $binary") + } + } + + @Test + fun testWhenIn() { + println(recognize('B')) + } + + private fun recognize(c: Char) = when (c) { + in '0'..'9' -> "It's a digit!" + in 'a'..'z', in 'A'..'Z' -> "It's a letter!" + else -> "I don't know" + } + + @Test + fun readNumber() { + println(readNumber(BufferedReader(StringReader("not a number")))) + } + + private fun readNumber(reader: BufferedReader) { + val number = try { + Integer.parseInt(reader.readLine()) + } catch (e: NumberFormatException) { + null + } + } + + +} \ No newline at end of file diff --git a/kotlin-baseball/src/test/kotlin/KtInActionPractice/chapter3/KtInActionPart3.kt b/kotlin-baseball/src/test/kotlin/KtInActionPractice/chapter3/KtInActionPart3.kt new file mode 100644 index 0000000..7e28dc9 --- /dev/null +++ b/kotlin-baseball/src/test/kotlin/KtInActionPractice/chapter3/KtInActionPart3.kt @@ -0,0 +1,122 @@ +package KtInActionPractice.chapter3 + +import KtInActionPractice.* +import org.junit.jupiter.api.Test + + +class KtInActionPart3Test { + + @Test + fun testJoinToString() { + val list = listOf(1, 2, 3) + println(joinToString(list, "; ", "(", ")")) + } + + @Test + fun testJoinToString2() { + val collection = listOf(1, 2, 3) + println(joinToString(collection, separator = " ", prefix = " ", postfix = ".")) + } + + @Test + fun utilPropertyTest() { + println(UNIT_LINE_SEPARATOR) + } + + @Test + fun extensionFun1() { + println("Kotlin".lastChar()) + } + + @Test + fun extensionFun2() { + val list = listOf(1, 2, 3) + println(list.joinToString2(" ")) + } + + @Test + fun extensionFun3() { + val list = listOf("one", "two", "three", "eight") + println(list.joinToString3(" ")) + } + + @Test + fun spreadTest() { + val list = arrayOf("String", "Java", "Kotlin") + spread(list) + } + + private fun spread(args: Array) { + println(listOf("args: ", *args)) + } + + @Test + fun mapToTest() { + val map = mapOf(1 to "one", 7 to "seven", 53 to "fifty-three") + println(map) + } + + @Test + fun splitTest() { + println("12.345-6.A".split(".", "-")) + } + + @Test + fun parseTest() { + val path = "/Users/yole/kotlin-book/chapter.adoc" + parsePath(path) + } + + private fun parsePath(path: String) { + val directory = path.substringBeforeLast("/") + val fullName = path.substringAfterLast("/") + val fileName = fullName.substringBefore(".") + val extension = path.substringAfterLast(".") + println("Dir: $directory, name: $fileName, ext: $extension") + } + + @Test + fun StringTest() { + println(""" + | // + | // + | /\ + """.trimIndent()) + } + + class User( + val id: Int, + val name: String, + val address: String + ) + + private fun saveUser(user: User) { + fun validate(user: User, + value: String, + fieldName: String) { + if (value.isEmpty()) { + throw IllegalArgumentException("Can't save User ${user.id}: empty $fieldName") + } + } + + validate(user, user.name, "Name") + validate(user, user.address, "Address") + } + + /* + * Issue Entity 클래스에 몰빵 vs 디렉토리 나눠서 확장 함수로 관리? -> (p.138 list 3.14) 자바는 어쩔 수 없이 클래스 안에 몰빵해야 하는데 코틀린은 확장 함수 때문에 분리해서 관리할 수 있음. + * Issue Builder vs 생성자 -> 커맨드 + P + p.108 + * */ + + private fun saveUser2(user: User) { + fun validate(value: String, fieldName: String) { + if (value.isEmpty()) { + throw IllegalArgumentException("Can't save User ${user.id}: empty $fieldName") + } + } + + validate(user.name, "Name") + validate(user.address, "Address") + } + +} diff --git a/kotlin-baseball/src/test/kotlin/KtInActionPractice/chapter4/Clickable.kt b/kotlin-baseball/src/test/kotlin/KtInActionPractice/chapter4/Clickable.kt new file mode 100644 index 0000000..5adf286 --- /dev/null +++ b/kotlin-baseball/src/test/kotlin/KtInActionPractice/chapter4/Clickable.kt @@ -0,0 +1,10 @@ +package KtInActionPractice.chapter4 + + +interface Clickable { + + fun click() + + fun showOff() = println("I'm clickable!") + +} \ No newline at end of file diff --git a/kotlin-baseball/src/test/kotlin/KtInActionPractice/chapter4/Focusable.kt b/kotlin-baseball/src/test/kotlin/KtInActionPractice/chapter4/Focusable.kt new file mode 100644 index 0000000..dcf52e8 --- /dev/null +++ b/kotlin-baseball/src/test/kotlin/KtInActionPractice/chapter4/Focusable.kt @@ -0,0 +1,8 @@ +package KtInActionPractice.chapter4 + +interface Focusable { + + fun setFocus(b: Boolean) = println("I ${if (b) "got" else "lost"} focus.") + + fun showOff() = println("I'm focusable!") +} \ No newline at end of file diff --git a/kotlin-baseball/src/test/kotlin/KtInActionPractice/chapter4/KtInActionPart4.kt b/kotlin-baseball/src/test/kotlin/KtInActionPractice/chapter4/KtInActionPart4.kt new file mode 100644 index 0000000..ef56ae0 --- /dev/null +++ b/kotlin-baseball/src/test/kotlin/KtInActionPractice/chapter4/KtInActionPart4.kt @@ -0,0 +1,133 @@ +package KtInActionPractice.chapter4 + +import org.junit.jupiter.api.Test +import java.io.Serializable + + +/* +* -------------------------------- +* 추상 메서드와 디폴트 메서드를 구현할 수 있음. 다만, 아무런 상태(필드)도 들어갈 수 없음. +* override 변경자를 꼭 붙여야 함. 어노테이션으로 하면 안됨. +* +* 똑같은 메서드 이름을 가진 디폴트 메서드가 있다면 둘 다 선택 x +* 따라서 오버라이드를 통해서 명시적으로 작성을 해 줘야 함. +* +* 기본적으로 final 과거 스택 오버플로 조사에서는 50 : 50 으로 나눠졌다고 하는데 최종적으로 final 이 채택되었음. +* 하위 클래스가 기반 클래스에 대해 가졌던 가정이 기반 클래스를 변경함으로써 깨져버린 경우를 대비해서 final 로 채택함 + 성능적인 문제도 있었던 걸로 기억함. +* 따라서 상속을 허용하려면 open 변경자를 붙여야 함. +* +* +* +* */ + +class Button: Clickable, Focusable { + + override fun click() = println("I was clicked!") + + override fun showOff() { + super.showOff() + super.showOff() + } + +} + +open class RichButton: Clickable { + fun disable() {} + open fun animate() {} + final override fun click() { + + } +} + +abstract class Animated { + abstract fun animate() + + open fun stopAnimating() { + + } + + fun animateTwice() { + + } +} + +/* +* 내부 클래스와 중첩된 클래스: 기본적으로 중첩 클래스 +* 클래스 안에 다른 클래스를 선언하면 도우미 클래스를 캡슐화하거나 코드 정의를 그 코드를 사용하는 곳 가까이에 두고 싶을 때 유용하다 -> 이해 잘 못 함; +* 도우미 클래스? +* 중첩 클래스는 명시적으로 요청하지 않는 한 바깥쪽 클래스 인스턴스에 대한 접근 권한이 없음 -> 이해 잘 못 함; +* +* */ + +interface State: Serializable + +interface View { + fun getCurrentState(): State + fun restoreState(state: State) +} + +class OtherButton: View { + override fun getCurrentState(): State = ButtonState() + + override fun restoreState(state: State) { + TODO("Not yet implemented") + } + + class ButtonState: State { + + } + +} + +//interface User { +// val nickName: String +//} +// +//class PrivateUser(override val nickName: String): User +// +//class SubscribingUser(val email: String): User { +// override val nickName: String +// get() = email.substringBefore("@") +//} +// +//class FacebookUser(val accountId: Int) : User { +// override val nickName: String +// get() = getFacebookName(accountId) +//} + +class User(val name: String) { + var address: String = "unspecified" + set(value: String) { + println("""Address was changed for ${name}: "${field}" -> "${value}".""".trimIndent()) + field = value + } +} + + +class KtInActionPart4Test { + + @Test + fun simpleInterfaceTest() { + Button().click() + } + + @Test + fun defaultMethodTest() { + Button().showOff() + } + + @Test + fun showOffTest() { + val button = Button() + button.showOff() + button.setFocus(true) + button.click() + } + + @Test + fun changeTest() { + val user = User("Alice") + user.address = "부산대학로 원룸" + } + +}