├── .gitignore ├── LICENSE.txt ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── src ├── i_introduction │ ├── _0_Hello_World │ │ └── n00Start.kt │ ├── _10_Object_Expressions │ │ └── n10ObjectExpressions.kt │ ├── _11_SAM_Conversions │ │ └── n11SAMConversions.kt │ ├── _12_Extensions_On_Collections │ │ └── n12ExtensionsOnCollections.kt │ ├── _1_Java_To_Kotlin_Converter │ │ ├── JavaCode1.java │ │ └── n01JavaToKotlinConverter.kt │ ├── _2_Named_Arguments │ │ └── n02NamedArguments.kt │ ├── _3_Default_Arguments │ │ ├── JavaCode3.java │ │ └── n03DefaultArguments.kt │ ├── _4_Lambdas │ │ ├── JavaCode4.java │ │ └── n04Lambdas.kt │ ├── _5_String_Templates │ │ └── n05StringTemplates.kt │ ├── _6_Data_Classes │ │ ├── JavaCode6.java │ │ └── n06DataClasses.kt │ ├── _7_Nullable_Types │ │ ├── JavaCode7.java │ │ └── n07NullableTypes.kt │ ├── _8_Smart_Casts │ │ ├── JavaCode8.java │ │ └── n08SmartCasts.kt │ └── _9_Extension_Functions │ │ ├── JavaCode9.java │ │ └── n09ExtensionFunctions.kt ├── ii_collections │ ├── _24_JavaCode.java │ ├── n13Introduction.kt │ ├── n14FilterMap.kt │ ├── n15AllAnyAndOtherPredicates.kt │ ├── n16FlatMap.kt │ ├── n17MaxMin.kt │ ├── n18Sort.kt │ ├── n19Sum.kt │ ├── n20GroupBy.kt │ ├── n21Partition.kt │ ├── n22Fold.kt │ ├── n23CompoundTasks.kt │ ├── n24ExtensionsOnCollections.kt │ ├── shop.kt │ └── todoUtil.kt ├── iii_conventions │ ├── MyDate.kt │ ├── MyDateUtil.kt │ ├── n25Comparison.kt │ ├── n26InRange.kt │ ├── n27RangeTo.kt │ ├── n28ForLoop.kt │ ├── n29OperatorsOverloading.kt │ ├── n29Tips.kt │ ├── n30DestructuringDeclarations.kt │ └── n31Invoke.kt ├── iv_properties │ ├── n32Properties.kt │ ├── n33LazyProperty.kt │ ├── n34DelegatesExamples.kt │ └── n35HowDelegatesWork.kt ├── util │ ├── JavaCode.java │ ├── LinksToDocumentation.kt │ ├── YourOldJavaCodeUsingRunnable.java │ ├── kotlinUtil.kt │ └── questions │ │ └── Question.kt ├── v_builders │ ├── data.kt │ ├── html.kt │ ├── htmlDemo.kt │ ├── n36ExtensionFunctionLiterals.kt │ ├── n37StringAndMapBuilders.kt │ ├── n38TheFunctionApply.kt │ ├── n39HtmlBuilders.kt │ └── n40BuildersHowItWorks.kt └── vi_generics │ └── n41GenericFunctions.kt └── test ├── i_introduction ├── _0_Hello_World │ └── N00StartKtTest.kt ├── _10_Object_Expressions │ └── N10ObjectExpressionsKtTest.kt ├── _11_SAM_Conversions │ └── N11SAMConversionsKtTest.kt ├── _12_Extensions_On_Collections │ └── N12ExtensionsOnCollectionsKtTest.kt ├── _1_Java_To_Kotlin_Converter │ └── N01JavaToKotlinConverterKtTest.kt ├── _2_Named_Arguments │ └── N02NamedArgumentsKtTest.kt ├── _3_Default_Arguments │ └── N03DefaultArgumentsKtTest.kt ├── _4_Lambdas │ └── N04LambdasKtTest.kt ├── _5_String_Templates │ └── N05StringTemplatesKtTest.kt ├── _6_Data_Classes │ └── N06DataClassesKtTest.kt ├── _7_Nullable_Types │ └── N07NullableTypesKtTest.kt ├── _8_Smart_Casts │ └── N08SmartCastsKtTest.kt └── _9_Extension_Functions │ └── N09ExtensionFunctionsKtTest.kt ├── ii_collections ├── N13IntroductionKtTest.kt ├── N14FilterMapKtTest.kt ├── N15AllAnyAndOtherPredicatesKtTest.kt ├── N16FlatMapKtTest.kt ├── N17MaxMinKtTest.kt ├── N18SortKtTest.kt ├── N19SumKtTest.kt ├── N20GroupByKtTest.kt ├── N21PartitionKtTest.kt ├── N22FoldKtTest.kt ├── N23CompoundTasksKtTest.kt ├── N24ExtensionsOnCollectionsKtTest.kt └── TestShop.kt ├── iii_conventions ├── DateUtil.kt ├── N25ComparisonKtTest.kt ├── N26InRangeKtTest.kt ├── N27RangeToKtTest.kt ├── N28ForLoopKtTest.kt ├── N29OperatorsOverloadingKtTest.kt ├── N30DestructuringDeclarationsKtTest.kt └── N31InvokeKtTest.kt ├── iv_properties ├── N32PropertiesKtTest.kt ├── N33LazyPropertyKtTest.kt ├── N34DelegatesExamplesKtTest.kt └── N35HowDelegatesWorkKtTest.kt ├── util └── AdditionalTest.kt ├── v_builders ├── N36ExtensionFunctionLiteralsKtTest.kt ├── N37StringAndMapBuildersKtTest.kt ├── N38TheFunctionApplyKtTest.kt ├── N39HtmlBuildersKtTest.kt └── N40BuildersHowItWorksKtTest.kt └── vi_generics └── N41GenericFunctionsKtTest.kt /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | 3 | # IDEA 4 | .idea 5 | *.iml 6 | local.properties 7 | 8 | # Gradle 9 | .gradle 10 | build 11 | 12 | # Eclipse 13 | bin 14 | .settings 15 | .project 16 | .classpath 17 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2015 JetBrains s.r.o. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![obsolete JetBrains project](https://jb.gg/badges/obsolete-plastic.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) 2 | 3 | ### This project is obsolete. 4 | ### For the last version of Koans, check [the course](https://www.jetbrains.com/help/education/learner-start-guide.html?section=Kotlin%20Koans) in [the EduTools plugin](https://www.jetbrains.com/help/education/install-edutools-plugin.html?section=IntelliJ%20IDEA) or [online version](https://play.kotlinlang.org/koans/overview). 5 | ### The task content can be found at https://github.com/Kotlin/kotlin-koans-edu. 6 | 7 | Kotlin Koans 8 | =========== 9 | 10 | Kotlin Koans Workshop is a series of exercises to get you familiar with the Kotlin Syntax. 11 | Each exercise is created as a failing unit test and your job is to make it pass. 12 | 13 | #### How to build and run tests 14 | 15 | ##### Working with the project using Intellij IDEA: 16 | 17 | 1. Import the project as Gradle project. 18 | 2. To build the project and run the tests use `test` task on the Gradle tool window 19 | (`View | Tool Windows | Gradle`). 20 | 21 | Here https://www.jetbrains.com/help/idea/gradle.html#gradle_tasks you can read 22 | how to run Gradle task from the Gradle toolbar in IntelliJ IDEA. 23 | 24 | Note that this project isn't intended to be used in Android Studio. If you want to solve koans in Android Studio, check the course in Android Studio with EduTools plugin installed https://github.com/kotlin/kotlin-koans#other-ways-to-solve-koans. 25 | 26 | ##### Working with the commandline 27 | 28 | You can also build the project and run all tests in the terminal: 29 | ``` 30 | ./gradlew test 31 | ``` 32 | But since running all the tests tend to take longer and the output can be 33 | cluttered, it's more ideal to run selected tests only: 34 | ``` 35 | $ ./gradlew test --tests i_* # run tests in part 1 36 | $ ./gradlew test --tests ii_* # run tests in part 2 37 | $ ./gradlew test --tests ii_*22* # run test number 22 in part 2 38 | ``` 39 | 40 | 41 | #### How to check yourself 42 | 43 | The repository has two branches, `master` which contains the exercises for you to do and `resolutions` which contains the resolved exercises. 44 | Make sure you don’t cheat! 45 | 46 | 47 | #### How the tasks are organized 48 | 49 | You have 42 tasks to do. 50 | Each task lives in its own function: from `task0` to `task41`. 51 | For each task, there is an associated unit test that checks your solution. 52 | 53 | You may navigate to the corresponding test automatically when you read the task. 54 | Open the source file with the task and use the action `Navigate -> Test` to open the test file. 55 | You may also use `Navigate -> Test Subject` for reversed navigation. 56 | 57 | Individual tasks generally require you to change the function `taskX` by completely replacing the body of the function. 58 | Your goal is to solve the problem and allow the associated unit test to pass. 59 | If you run the unit test for a task that is not correct, the unit test results will be displayed. 60 | If you have not yet made any changes to a task, the exception will be thrown and the task's TODO message will be displayed. 61 | 62 | In the first example, this means replacing the code 63 | 64 | ```kotlin 65 | fun task0(): String { 66 | return todoTask0() 67 | } 68 | ``` 69 | 70 | with the correct, meaningful code in order to solve the problem and allow the associated unit test to pass, such as: 71 | 72 | ```kotlin 73 | fun task0() = "OK" 74 | ``` 75 | 76 | The `resolutions` branch contains all the solutions. 77 | It's a good idea to check the proposed solution after completing each task. 78 | Open the file with your solution, call the `Compare with branch...` action and choose the `resolutions` branch. 79 | You can find [here](https://www.jetbrains.com/help/idea/navigating-to-action.html) how to call an action. 80 | 81 | 82 | #### Other ways to solve Koans 83 | 84 | You can solve the similar tasks using Educational Plugin or in the browser: 85 | 86 | - EduTools Plugin https://www.jetbrains.com/help/education/learner-start-guide.html?section=Kotlin%20Koans 87 | - online version of koans https://try.kotl.in 88 | 89 | The koans tasks for web-demo and educational plugin can be found here: https://github.com/Kotlin/kotlin-koans-edu 90 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.21' 3 | 4 | repositories { 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 10 | } 11 | } 12 | 13 | apply plugin: 'kotlin' 14 | 15 | sourceSets { 16 | main.java.srcDirs += 'src' 17 | test.java.srcDirs += 'test' 18 | } 19 | 20 | repositories { 21 | mavenCentral() 22 | } 23 | 24 | dependencies { 25 | compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 26 | compile 'com.google.guava:guava:28.0-jre' 27 | testCompile 'org.junit.jupiter:junit-jupiter:5.4.0' 28 | } 29 | 30 | test { 31 | useJUnitPlatform() 32 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kotlin/kotlin-koans/5935a3cab5293bd7967b1bf1f4d2ae713f9e0e9e/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Oct 05 21:35:33 MSK 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /src/i_introduction/_0_Hello_World/n00Start.kt: -------------------------------------------------------------------------------- 1 | package i_introduction._0_Hello_World 2 | 3 | import util.TODO 4 | import util.doc0 5 | 6 | /* 7 | 8 | Note that this project is obsolete. 9 | For the last version of Koans, check either 10 | online version 11 | - https://play.kotlinlang.org/koans/overview 12 | or 13 | the course in the EduTools plugin 14 | - https://www.jetbrains.com/help/education/learner-start-guide.html?section=Kotlin%20Koans 15 | - https://www.jetbrains.com/help/education/install-edutools-plugin.html?section=IntelliJ%20IDEA 16 | 17 | The task content can be found at https://github.com/Kotlin/kotlin-koans-edu. 18 | 19 | */ 20 | 21 | fun todoTask0(): Nothing = TODO( 22 | """ 23 | Task 0. 24 | 25 | Read README.md to learn how to work with this project and check your solutions. 26 | 27 | Using 'documentation =' below the task description you can open the related part of the online documentation. 28 | Press 'Ctrl+Q'(Windows) or 'F1'(Mac OS) on 'doc0()' to call the "Quick Documentation" action; 29 | "See also" section gives you a link. 30 | You can see the shortcut for the "Quick Documentation" action used in your IntelliJ IDEA 31 | by choosing "Help -> Find Action..." (in the top menu), and typing the action name ("Quick Documentation"). 32 | The shortcut in use will be written next to the action name. 33 | 34 | Using 'references =' you can navigate to the code mentioned in the task description. 35 | 36 | Let's start! Make the function 'task0' return "OK". Note that you can return expression directly. 37 | """, 38 | documentation = doc0(), 39 | references = { task0(); "OK" } 40 | ) 41 | 42 | fun task0(): String { 43 | return todoTask0() 44 | } -------------------------------------------------------------------------------- /src/i_introduction/_10_Object_Expressions/n10ObjectExpressions.kt: -------------------------------------------------------------------------------- 1 | package i_introduction._10_Object_Expressions 2 | 3 | import util.TODO 4 | import util.doc10 5 | import java.util.* 6 | 7 | fun todoTask10(): Nothing = TODO( 8 | """ 9 | Task 10. 10 | Read about object expressions that play the same role in Kotlin as anonymous classes in Java. 11 | 12 | Add an object expression that provides a comparator to sort a list in a descending order using 'java.util.Collections' class. 13 | In Kotlin you use Kotlin library extensions instead of java.util.Collections, 14 | but this example is still a good demonstration of mixing Kotlin and Java code. 15 | """, 16 | documentation = doc10() 17 | ) 18 | 19 | fun task10(): List { 20 | val arrayList = arrayListOf(1, 5, 2) 21 | Collections.sort(arrayList, todoTask10()) 22 | return arrayList 23 | } -------------------------------------------------------------------------------- /src/i_introduction/_11_SAM_Conversions/n11SAMConversions.kt: -------------------------------------------------------------------------------- 1 | package i_introduction._11_SAM_Conversions 2 | 3 | import util.TODO 4 | import util.doc11 5 | import java.util.* 6 | 7 | fun todoTask11(): Nothing = TODO( 8 | """ 9 | Task 11. 10 | When an object implements a SAM interface (one with a Single Abstract Method), you can pass a lambda instead. 11 | Rewrite the previous example changing an object expression to a lambda. 12 | """, 13 | documentation = doc11() 14 | ) 15 | 16 | fun task11(): List { 17 | val arrayList = arrayListOf(1, 5, 2) 18 | Collections.sort(arrayList, { x, y -> todoTask11() }) 19 | return arrayList 20 | } 21 | -------------------------------------------------------------------------------- /src/i_introduction/_12_Extensions_On_Collections/n12ExtensionsOnCollections.kt: -------------------------------------------------------------------------------- 1 | package i_introduction._12_Extensions_On_Collections 2 | 3 | import util.TODO 4 | import util.doc12 5 | 6 | fun todoTask12(): Nothing = TODO( 7 | """ 8 | Task 12. 9 | Kotlin standard library contains lots of extension functions that make the work with collections more convenient. 10 | Rewrite the previous example once more using an extension function 'sortedDescending'. 11 | 12 | Kotlin code can be easily mixed with Java code. 13 | Thus in Kotlin we don't introduce our own collections, but use standard Java ones (slightly improved). 14 | Read about read-only and mutable views on Java collections. 15 | """, 16 | documentation = doc12() 17 | ) 18 | 19 | fun task12(): List { 20 | todoTask12() 21 | return arrayListOf(1, 5, 2) 22 | } 23 | 24 | -------------------------------------------------------------------------------- /src/i_introduction/_1_Java_To_Kotlin_Converter/JavaCode1.java: -------------------------------------------------------------------------------- 1 | package i_introduction._1_Java_To_Kotlin_Converter; 2 | 3 | import util.JavaCode; 4 | 5 | import java.util.Collection; 6 | import java.util.Iterator; 7 | 8 | public class JavaCode1 extends JavaCode { 9 | public String task1(Collection collection) { 10 | StringBuilder sb = new StringBuilder(); 11 | sb.append("{"); 12 | Iterator iterator = collection.iterator(); 13 | while (iterator.hasNext()) { 14 | Integer element = iterator.next(); 15 | sb.append(element); 16 | if (iterator.hasNext()) { 17 | sb.append(", "); 18 | } 19 | } 20 | sb.append("}"); 21 | return sb.toString(); 22 | } 23 | } -------------------------------------------------------------------------------- /src/i_introduction/_1_Java_To_Kotlin_Converter/n01JavaToKotlinConverter.kt: -------------------------------------------------------------------------------- 1 | package i_introduction._1_Java_To_Kotlin_Converter 2 | 3 | import util.TODO 4 | 5 | fun todoTask1(collection: Collection): Nothing = TODO( 6 | """ 7 | Task 1. 8 | Convert the Java method 'task1' of the class 'JavaCode1' into Kotlin. 9 | In IntelliJ IDEA or Android Studio, you can copy the Java code, 10 | paste it into the Kotlin file and let IDE convert it. 11 | Please use automatic conversion for this task only. 12 | """, 13 | references = { JavaCode1().task1(collection) }) 14 | 15 | 16 | fun task1(collection: Collection): String { 17 | todoTask1(collection) 18 | } -------------------------------------------------------------------------------- /src/i_introduction/_2_Named_Arguments/n02NamedArguments.kt: -------------------------------------------------------------------------------- 1 | package i_introduction._2_Named_Arguments 2 | 3 | import i_introduction._1_Java_To_Kotlin_Converter.task1 4 | import util.TODO 5 | import util.doc2 6 | 7 | // default values for arguments 8 | fun bar(i: Int, s: String = "", b: Boolean = true) {} 9 | 10 | fun usage() { 11 | // named arguments 12 | bar(1, b = false) 13 | } 14 | 15 | fun todoTask2(): Nothing = TODO( 16 | """ 17 | Task 2. 18 | Print out the collection contents surrounded by curly braces using the library function 'joinToString'. 19 | Specify only 'prefix' and 'postfix' arguments. 20 | 21 | Don't forget to remove the 'todoTask2()' invocation which throws an exception. 22 | """, 23 | documentation = doc2(), 24 | references = { collection: Collection -> task1(collection); collection.joinToString() }) 25 | 26 | fun task2(collection: Collection): String { 27 | todoTask2() 28 | return collection.joinToString() 29 | } -------------------------------------------------------------------------------- /src/i_introduction/_3_Default_Arguments/JavaCode3.java: -------------------------------------------------------------------------------- 1 | package i_introduction._3_Default_Arguments; 2 | 3 | import util.JavaCode; 4 | 5 | public class JavaCode3 extends JavaCode { 6 | private int defaultNumber = 42; 7 | 8 | public String foo(String name, int number, boolean toUpperCase) { 9 | return (toUpperCase ? name.toUpperCase() : name) + number; 10 | } 11 | 12 | public String foo(String name, int number) { 13 | return foo(name, number, false); 14 | } 15 | 16 | public String foo(String name, boolean toUpperCase) { 17 | return foo(name, defaultNumber, toUpperCase); 18 | } 19 | 20 | public String foo(String name) { 21 | return foo(name, defaultNumber); 22 | } 23 | } -------------------------------------------------------------------------------- /src/i_introduction/_3_Default_Arguments/n03DefaultArguments.kt: -------------------------------------------------------------------------------- 1 | package i_introduction._3_Default_Arguments 2 | 3 | import util.TODO 4 | import util.doc2 5 | 6 | fun todoTask3(): Nothing = TODO( 7 | """ 8 | Task 3. 9 | Several overloaded 'foo' functions in the class 'JavaCode3' can be replaced with one function in Kotlin. 10 | Change the declaration of the function 'foo' in a way that makes the code using 'foo' compile. 11 | You have to add 'foo' parameters and implement its body. 12 | Uncomment the commented code and make it compile. 13 | """, 14 | documentation = doc2(), 15 | references = { name: String -> JavaCode3().foo(name); foo(name) }) 16 | 17 | fun foo(name: String): String = todoTask3() 18 | 19 | fun task3(): String { 20 | todoTask3() 21 | // return (foo("a") + 22 | // foo("b", number = 1) + 23 | // foo("c", toUpperCase = true) + 24 | // foo(name = "d", number = 2, toUpperCase = true)) 25 | } -------------------------------------------------------------------------------- /src/i_introduction/_4_Lambdas/JavaCode4.java: -------------------------------------------------------------------------------- 1 | package i_introduction._4_Lambdas; 2 | 3 | import com.google.common.base.Predicate; 4 | import com.google.common.collect.Iterables; 5 | import util.JavaCode; 6 | 7 | import java.util.Collection; 8 | 9 | public class JavaCode4 extends JavaCode { 10 | public boolean task4(Collection collection) { 11 | return Iterables.any(collection, new Predicate() { 12 | @Override 13 | public boolean apply(Integer element) { 14 | return element % 2 == 0; 15 | } 16 | }); 17 | } 18 | } -------------------------------------------------------------------------------- /src/i_introduction/_4_Lambdas/n04Lambdas.kt: -------------------------------------------------------------------------------- 1 | package i_introduction._4_Lambdas 2 | 3 | import util.TODO 4 | import util.doc4 5 | 6 | fun example() { 7 | 8 | val sum = { x: Int, y: Int -> x + y } 9 | val square: (Int) -> Int = { x -> x * x } 10 | 11 | sum(1, square(2)) == 5 12 | } 13 | 14 | fun todoTask4(collection: Collection): Nothing = TODO( 15 | """ 16 | Task 4. 17 | Rewrite 'JavaCode4.task4()' in Kotlin using lambdas: 18 | return true if the collection contains an even number. 19 | You can find the appropriate function to call on 'Collection' by using code completion. 20 | Don't use the class 'Iterables'. 21 | """, 22 | documentation = doc4(), 23 | references = { JavaCode4().task4(collection) }) 24 | 25 | fun task4(collection: Collection): Boolean = todoTask4(collection) -------------------------------------------------------------------------------- /src/i_introduction/_5_String_Templates/n05StringTemplates.kt: -------------------------------------------------------------------------------- 1 | package i_introduction._5_String_Templates 2 | 3 | import util.TODO 4 | import util.doc5 5 | 6 | fun example1(a: Any, b: Any) = 7 | "This is some text in which variables ($a, $b) appear." 8 | 9 | fun example2(a: Any, b: Any) = 10 | "You can write it in a Java way as well. Like this: " + a + ", " + b + "!" 11 | 12 | fun example3(c: Boolean, x: Int, y: Int) = "Any expression can be used: ${if (c) x else y}" 13 | 14 | fun example4() = 15 | """ 16 | You can use raw strings to write multiline text. 17 | There is no escaping here, so raw strings are useful for writing regex patterns, 18 | you don't need to escape a backslash by a backslash. 19 | String template entries (${42}) are allowed here. 20 | """ 21 | 22 | fun getPattern() = """\d{2}\.\d{2}\.\d{4}""" 23 | 24 | fun example() = "13.06.1992".matches(getPattern().toRegex()) //true 25 | 26 | val month = "(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)" 27 | 28 | fun todoTask5(): Nothing = TODO( 29 | """ 30 | Task 5. 31 | Copy the body of 'getPattern()' to the 'task5()' function below 32 | and rewrite it in such a way that it matches format: '13 JUN 1992'. 33 | Use the 'month' variable. 34 | """, 35 | documentation = doc5(), 36 | references = { getPattern(); month }) 37 | 38 | fun task5(): String = todoTask5() 39 | -------------------------------------------------------------------------------- /src/i_introduction/_6_Data_Classes/JavaCode6.java: -------------------------------------------------------------------------------- 1 | package i_introduction._6_Data_Classes; 2 | 3 | import util.JavaCode; 4 | 5 | public class JavaCode6 extends JavaCode { 6 | 7 | public static class Person { 8 | private final String name; 9 | private final int age; 10 | 11 | public Person(String name, int age) { 12 | this.name = name; 13 | this.age = age; 14 | } 15 | 16 | public String getName() { 17 | return name; 18 | } 19 | 20 | public int getAge() { 21 | return age; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/i_introduction/_6_Data_Classes/n06DataClasses.kt: -------------------------------------------------------------------------------- 1 | package i_introduction._6_Data_Classes 2 | 3 | import util.TODO 4 | import util.doc6 5 | 6 | fun todoTask6(): Nothing = TODO( 7 | """ 8 | Convert 'JavaCode6.Person' class to Kotlin. 9 | Then add a modifier `data` to the resulting class. 10 | This annotation means the compiler will generate a bunch of useful methods in this class: 11 | `equals`/`hashCode`, `toString` and some others. 12 | The `task6` function should return a list of persons. 13 | """, 14 | documentation = doc6(), 15 | references = { JavaCode6.Person("Alice", 29) } 16 | ) 17 | 18 | class Person 19 | 20 | fun task6(): List { 21 | todoTask6() 22 | return listOf(/*Person("Alice", 29), Person("Bob", 31)*/) 23 | } 24 | 25 | -------------------------------------------------------------------------------- /src/i_introduction/_7_Nullable_Types/JavaCode7.java: -------------------------------------------------------------------------------- 1 | package i_introduction._7_Nullable_Types; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | import org.jetbrains.annotations.Nullable; 5 | import util.JavaCode; 6 | 7 | public class JavaCode7 extends JavaCode { 8 | public void sendMessageToClient(@Nullable Client client, @Nullable String message, @NotNull Mailer mailer) { 9 | if (client == null || message == null) return; 10 | 11 | PersonalInfo personalInfo = client.getPersonalInfo(); 12 | if (personalInfo == null) return; 13 | 14 | String email = personalInfo.getEmail(); 15 | if (email == null) return; 16 | 17 | mailer.sendMessage(email, message); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/i_introduction/_7_Nullable_Types/n07NullableTypes.kt: -------------------------------------------------------------------------------- 1 | package i_introduction._7_Nullable_Types 2 | 3 | import util.TODO 4 | import util.doc7 5 | 6 | fun test() { 7 | val s: String = "this variable cannot store null references" 8 | val q: String? = null 9 | 10 | if (q != null) q.length // you have to check to dereference 11 | val i: Int? = q?.length // null 12 | val j: Int = q?.length ?: 0 // 0 13 | } 14 | 15 | fun todoTask7(client: Client?, message: String?, mailer: Mailer): Nothing = TODO( 16 | """ 17 | Task 7. 18 | Rewrite JavaCode7.sendMessageToClient in Kotlin, using only one 'if' expression. 19 | Declarations of Client, PersonalInfo and Mailer are given below. 20 | """, 21 | documentation = doc7(), 22 | references = { JavaCode7().sendMessageToClient(client, message, mailer) } 23 | ) 24 | 25 | fun sendMessageToClient( 26 | client: Client?, message: String?, mailer: Mailer 27 | ) { 28 | todoTask7(client, message, mailer) 29 | } 30 | 31 | class Client(val personalInfo: PersonalInfo?) 32 | class PersonalInfo(val email: String?) 33 | 34 | interface Mailer { 35 | fun sendMessage(email: String, message: String) 36 | } 37 | -------------------------------------------------------------------------------- /src/i_introduction/_8_Smart_Casts/JavaCode8.java: -------------------------------------------------------------------------------- 1 | package i_introduction._8_Smart_Casts; 2 | 3 | import util.JavaCode; 4 | 5 | public class JavaCode8 extends JavaCode { 6 | public int eval(Expr expr) { 7 | if (expr instanceof Num) { 8 | return ((Num) expr).getValue(); 9 | } 10 | if (expr instanceof Sum) { 11 | Sum sum = (Sum) expr; 12 | return eval(sum.getLeft()) + eval(sum.getRight()); 13 | } 14 | throw new IllegalArgumentException("Unknown expression"); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/i_introduction/_8_Smart_Casts/n08SmartCasts.kt: -------------------------------------------------------------------------------- 1 | package i_introduction._8_Smart_Casts 2 | 3 | import util.TODO 4 | import util.doc8 5 | 6 | // 'sealed' modifier restricts the type hierarchy: 7 | // all the subclasses must be declared in the same file 8 | sealed class Expr 9 | 10 | class Num(val value: Int) : Expr() 11 | class Sum(val left: Expr, val right: Expr) : Expr() 12 | 13 | fun eval(e: Expr): Int = 14 | when (e) { 15 | is Num -> todoTask8(e) 16 | is Sum -> todoTask8(e) 17 | } 18 | 19 | fun todoTask8(expr: Expr): Nothing = TODO( 20 | """ 21 | Task 8. 22 | Complete the implementation of the 'eval' function above using smart casts and 'when' expression. 23 | The 'JavaCode8.eval' method provides the similar functionality written in Java. 24 | """, 25 | documentation = doc8(), 26 | references = { JavaCode8().eval(expr) }) 27 | 28 | -------------------------------------------------------------------------------- /src/i_introduction/_9_Extension_Functions/JavaCode9.java: -------------------------------------------------------------------------------- 1 | package i_introduction._9_Extension_Functions; 2 | 3 | import util.JavaCode; 4 | 5 | public class JavaCode9 extends JavaCode { 6 | public void useExtension() { 7 | char c = N09ExtensionFunctionsKt.lastChar("abc"); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/i_introduction/_9_Extension_Functions/n09ExtensionFunctions.kt: -------------------------------------------------------------------------------- 1 | package i_introduction._9_Extension_Functions 2 | 3 | import util.TODO 4 | import util.doc9 5 | 6 | // declares an extension function that returns the last character 7 | fun String.lastChar() = this.get(this.length - 1) 8 | 9 | 10 | // 'this' refers to the receiver (String) and can be omitted 11 | fun String.lastChar1() = get(length - 1) 12 | 13 | fun useExtensionFunction() { 14 | // try Ctrl+Space "default completion" after the dot: lastChar() is visible 15 | "abc".lastChar() 16 | } 17 | 18 | // 'lastChar' is compiled to a static function in the class ExtensionFunctionsKt (see JavaCode9.useExtension) 19 | 20 | fun todoTask9(): Nothing = TODO( 21 | """ 22 | Task 9. 23 | Implement the extension functions Int.r(), Pair.r() 24 | to support the following manner of creating rational numbers: 25 | 1.r(), Pair(1, 2).r() 26 | """, 27 | documentation = doc9(), 28 | references = { 1.r(); Pair(1, 2).r(); RationalNumber(1, 9) }) 29 | 30 | data class RationalNumber(val numerator: Int, val denominator: Int) 31 | 32 | fun Int.r(): RationalNumber = todoTask9() 33 | fun Pair.r(): RationalNumber = todoTask9() 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/ii_collections/_24_JavaCode.java: -------------------------------------------------------------------------------- 1 | package ii_collections; 2 | 3 | import com.google.common.collect.Lists; 4 | import com.google.common.collect.Maps; 5 | import util.JavaCode; 6 | 7 | import java.util.Collection; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | public class _24_JavaCode extends JavaCode { 12 | public Collection doSomethingStrangeWithCollection(Collection collection) { 13 | Map> groupsByLength = Maps.newHashMap(); 14 | for (String s : collection) { 15 | List strings = groupsByLength.get(s.length()); 16 | if (strings == null) { 17 | strings = Lists.newArrayList(); 18 | groupsByLength.put(s.length(), strings); 19 | } 20 | strings.add(s); 21 | } 22 | 23 | int maximumSizeOfGroup = 0; 24 | for (List group : groupsByLength.values()) { 25 | if (group.size() > maximumSizeOfGroup) { 26 | maximumSizeOfGroup = group.size(); 27 | } 28 | } 29 | 30 | for (List group : groupsByLength.values()) { 31 | if (group.size() == maximumSizeOfGroup) { 32 | return group; 33 | } 34 | } 35 | return null; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/ii_collections/n13Introduction.kt: -------------------------------------------------------------------------------- 1 | package ii_collections 2 | 3 | import java.util.* 4 | 5 | /* 6 | * This part of workshop was inspired by: 7 | * https://github.com/goldmansachs/gs-collections-kata 8 | */ 9 | 10 | /* 11 | * There are many operations that help to transform one collection into another, starting with 'to' 12 | */ 13 | fun example0(list: List) { 14 | list.toSet() 15 | 16 | list.toCollection(HashSet()) 17 | } 18 | 19 | fun Shop.getSetOfCustomers(): Set { 20 | // Return a set containing all the customers of this shop 21 | todoCollectionTask() 22 | // return this.customers 23 | } 24 | 25 | -------------------------------------------------------------------------------- /src/ii_collections/n14FilterMap.kt: -------------------------------------------------------------------------------- 1 | package ii_collections 2 | 3 | fun example1(list: List) { 4 | 5 | // If a lambda has exactly one parameter, that parameter can be accessed as 'it' 6 | val positiveNumbers = list.filter { it > 0 } 7 | 8 | val squares = list.map { it * it } 9 | } 10 | 11 | fun Shop.getCitiesCustomersAreFrom(): Set { 12 | // Return the set of cities the customers are from 13 | todoCollectionTask() 14 | } 15 | 16 | fun Shop.getCustomersFrom(city: City): List { 17 | // Return a list of the customers who live in the given city 18 | todoCollectionTask() 19 | } 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/ii_collections/n15AllAnyAndOtherPredicates.kt: -------------------------------------------------------------------------------- 1 | package ii_collections 2 | 3 | fun example2(list: List) { 4 | 5 | val isZero: (Int) -> Boolean = { it == 0 } 6 | 7 | val hasZero: Boolean = list.any(isZero) 8 | 9 | val allZeros: Boolean = list.all(isZero) 10 | 11 | val numberOfZeros: Int = list.count(isZero) 12 | 13 | val firstPositiveNumber: Int? = list.firstOrNull { it > 0 } 14 | } 15 | 16 | fun Customer.isFrom(city: City): Boolean { 17 | // Return true if the customer is from the given city 18 | todoCollectionTask() 19 | } 20 | 21 | fun Shop.checkAllCustomersAreFrom(city: City): Boolean { 22 | // Return true if all customers are from the given city 23 | todoCollectionTask() 24 | } 25 | 26 | fun Shop.hasCustomerFrom(city: City): Boolean { 27 | // Return true if there is at least one customer from the given city 28 | todoCollectionTask() 29 | } 30 | 31 | fun Shop.countCustomersFrom(city: City): Int { 32 | // Return the number of customers from the given city 33 | todoCollectionTask() 34 | } 35 | 36 | fun Shop.findFirstCustomerFrom(city: City): Customer? { 37 | // Return the first customer who lives in the given city, or null if there is none 38 | todoCollectionTask() 39 | } 40 | -------------------------------------------------------------------------------- /src/ii_collections/n16FlatMap.kt: -------------------------------------------------------------------------------- 1 | package ii_collections 2 | 3 | fun example() { 4 | 5 | val result = listOf("abc", "12").flatMap { it.toList() } 6 | 7 | result == listOf('a', 'b', 'c', '1', '2') 8 | } 9 | 10 | val Customer.orderedProducts: Set 11 | get() { 12 | // Return all products this customer has ordered 13 | todoCollectionTask() 14 | } 15 | 16 | val Shop.allOrderedProducts: Set 17 | get() { 18 | // Return all products that were ordered by at least one customer 19 | todoCollectionTask() 20 | } 21 | -------------------------------------------------------------------------------- /src/ii_collections/n17MaxMin.kt: -------------------------------------------------------------------------------- 1 | package ii_collections 2 | 3 | fun example4() { 4 | val max = listOf(1, 42, 4).max() 5 | val longestString = listOf("a", "b").maxBy { it.length } 6 | } 7 | 8 | fun Shop.getCustomerWithMaximumNumberOfOrders(): Customer? { 9 | // Return a customer whose order count is the highest among all customers 10 | todoCollectionTask() 11 | } 12 | 13 | fun Customer.getMostExpensiveOrderedProduct(): Product? { 14 | // Return the most expensive product which has been ordered 15 | todoCollectionTask() 16 | } 17 | -------------------------------------------------------------------------------- /src/ii_collections/n18Sort.kt: -------------------------------------------------------------------------------- 1 | package ii_collections 2 | 3 | fun example5() { 4 | val result = listOf("a", "bbb", "cc").sortedBy { it.length } 5 | 6 | result == listOf("a", "cc", "bbb") 7 | } 8 | 9 | fun Shop.getCustomersSortedByNumberOfOrders(): List { 10 | // Return a list of customers, sorted by the ascending number of orders they made 11 | todoCollectionTask() 12 | } 13 | -------------------------------------------------------------------------------- /src/ii_collections/n19Sum.kt: -------------------------------------------------------------------------------- 1 | package ii_collections 2 | 3 | fun example6() { 4 | listOf(1, 3).sum() == 4 5 | listOf("a", "b", "cc").sumBy { it.length } == 4 6 | } 7 | 8 | fun Customer.getTotalOrderPrice(): Double { 9 | // Return the sum of prices of all products that a customer has ordered. 10 | // Note: a customer may order the same product several times. 11 | todoCollectionTask() 12 | } 13 | -------------------------------------------------------------------------------- /src/ii_collections/n20GroupBy.kt: -------------------------------------------------------------------------------- 1 | package ii_collections 2 | 3 | fun example7() { 4 | val result = listOf("a", "b", "ba", "ccc", "ad").groupBy { it.length } 5 | 6 | result == mapOf(1 to listOf("a", "b"), 2 to listOf("ba", "ad"), 3 to listOf("ccc")) 7 | } 8 | 9 | fun Shop.groupCustomersByCity(): Map> { 10 | // Return a map of the customers living in each city 11 | todoCollectionTask() 12 | } 13 | -------------------------------------------------------------------------------- /src/ii_collections/n21Partition.kt: -------------------------------------------------------------------------------- 1 | package ii_collections 2 | 3 | fun example8() { 4 | val numbers = listOf(1, 3, -4, 2, -11) 5 | 6 | // The details (how multi-assignment works) will be explained later in the 'Conventions' task 7 | val (positive, negative) = numbers.partition { it > 0 } 8 | 9 | positive == listOf(1, 3, 2) 10 | negative == listOf(-4, -11) 11 | } 12 | 13 | fun Shop.getCustomersWithMoreUndeliveredOrdersThanDelivered(): Set { 14 | // Return customers who have more undelivered orders than delivered 15 | todoCollectionTask() 16 | } 17 | -------------------------------------------------------------------------------- /src/ii_collections/n22Fold.kt: -------------------------------------------------------------------------------- 1 | package ii_collections 2 | 3 | fun example9() { 4 | val result = listOf(1, 2, 3, 4).fold(1, { partResult, element -> element * partResult }) 5 | result == 24 6 | } 7 | 8 | // The same as 9 | fun whatFoldDoes(): Int { 10 | var result = 1 11 | listOf(1, 2, 3, 4).forEach { element -> result = element * result } 12 | return result 13 | } 14 | 15 | fun Shop.getSetOfProductsOrderedByEachCustomer(): Set { 16 | // Return the set of products that were ordered by each of the customers 17 | return customers.fold(allOrderedProducts, { orderedByAll, customer -> 18 | todoCollectionTask() 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /src/ii_collections/n23CompoundTasks.kt: -------------------------------------------------------------------------------- 1 | package ii_collections 2 | 3 | fun Shop.getCustomersWhoOrderedProduct(product: Product): Set { 4 | // Return the set of customers who ordered the specified product 5 | todoCollectionTask() 6 | } 7 | 8 | fun Customer.getMostExpensiveDeliveredProduct(): Product? { 9 | // Return the most expensive product among all delivered products 10 | // (use the Order.isDelivered flag) 11 | todoCollectionTask() 12 | } 13 | 14 | fun Shop.getNumberOfTimesProductWasOrdered(product: Product): Int { 15 | // Return the number of times the given product was ordered. 16 | // Note: a customer may order the same product for several times. 17 | todoCollectionTask() 18 | } 19 | -------------------------------------------------------------------------------- /src/ii_collections/n24ExtensionsOnCollections.kt: -------------------------------------------------------------------------------- 1 | package ii_collections 2 | 3 | import util.TODO 4 | 5 | fun todoTask24(): Nothing = TODO( 6 | """ 7 | Task 24. 8 | The function should do the same as '_24_JavaCode.doSomethingStrangeWithCollection'. 9 | Replace all invocations of 'todoTask24()' with the appropriate code. 10 | """, 11 | references = { c: Collection -> _24_JavaCode().doSomethingStrangeWithCollection(c) } 12 | ) 13 | 14 | fun doSomethingStrangeWithCollection(collection: Collection): Collection? { 15 | val groupsByLength = collection.groupBy { s -> todoTask24() } 16 | 17 | return groupsByLength.values.maxBy { group -> todoTask24() } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /src/ii_collections/shop.kt: -------------------------------------------------------------------------------- 1 | package ii_collections 2 | 3 | data class Shop(val name: String, val customers: List) 4 | 5 | data class Customer(val name: String, val city: City, val orders: List) { 6 | override fun toString() = "$name from ${city.name}" 7 | } 8 | 9 | data class Order(val products: List, val isDelivered: Boolean) 10 | 11 | data class Product(val name: String, val price: Double) { 12 | override fun toString() = "'$name' for $price" 13 | } 14 | 15 | data class City(val name: String) { 16 | override fun toString() = name 17 | } 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/ii_collections/todoUtil.kt: -------------------------------------------------------------------------------- 1 | package ii_collections 2 | 3 | import util.TODO 4 | 5 | fun todoCollectionTask(): Nothing = TODO( 6 | """ 7 | Common task for working with collections. 8 | Look through the Shop API, which all the tasks are using. 9 | Each individual task is described in the function name and the comment. 10 | """, 11 | references = { shop: Shop -> shop.customers } 12 | ) 13 | -------------------------------------------------------------------------------- /src/iii_conventions/MyDate.kt: -------------------------------------------------------------------------------- 1 | package iii_conventions 2 | 3 | data class MyDate(val year: Int, val month: Int, val dayOfMonth: Int) 4 | 5 | operator fun MyDate.rangeTo(other: MyDate): DateRange = todoTask27() 6 | 7 | enum class TimeInterval { 8 | DAY, 9 | WEEK, 10 | YEAR 11 | } 12 | 13 | class DateRange(val start: MyDate, val endInclusive: MyDate) 14 | -------------------------------------------------------------------------------- /src/iii_conventions/MyDateUtil.kt: -------------------------------------------------------------------------------- 1 | package iii_conventions 2 | 3 | import iii_conventions.TimeInterval.DAY 4 | import java.util.* 5 | 6 | fun MyDate.nextDay() = addTimeIntervals(DAY, 1) 7 | 8 | fun MyDate.addTimeIntervals(timeInterval: TimeInterval, number: Int) = Calendar.getInstance().run { 9 | set(year, month, dayOfMonth) 10 | add(when (timeInterval) { 11 | TimeInterval.DAY -> Calendar.DAY_OF_MONTH 12 | TimeInterval.WEEK -> Calendar.WEEK_OF_MONTH 13 | TimeInterval.YEAR -> Calendar.YEAR 14 | }, number) 15 | MyDate(get(Calendar.YEAR), get(Calendar.MONTH), get(Calendar.DATE)) 16 | } -------------------------------------------------------------------------------- /src/iii_conventions/n25Comparison.kt: -------------------------------------------------------------------------------- 1 | package iii_conventions 2 | 3 | import util.TODO 4 | import util.doc25 5 | 6 | fun todoTask25(): Nothing = TODO( 7 | """ 8 | Task 25. 9 | Uncomment the commented line and make it compile. 10 | Make all the changes to the file MyDate.kt. 11 | 12 | Tips: Make the class 'MyDate' implement 'Comparable'. 13 | """, 14 | documentation = doc25(), 15 | references = { date: MyDate, comparable: Comparable -> } 16 | ) 17 | 18 | fun task25(date1: MyDate, date2: MyDate): Boolean { 19 | todoTask25() 20 | // return date1 < date2 21 | } 22 | 23 | -------------------------------------------------------------------------------- /src/iii_conventions/n26InRange.kt: -------------------------------------------------------------------------------- 1 | package iii_conventions 2 | 3 | import util.TODO 4 | import util.doc26 5 | 6 | fun todoTask26_(): Nothing = TODO( 7 | """ 8 | Task 26. 9 | Uncomment the commented line and make it compile. 10 | Make all the changes to the file MyDate.kt. 11 | 12 | Tips: In Kotlin 'in' checks are translated to the corresponding 'contains' calls. 13 | You can add a method 'fun contains(d: MyDate)' to the class 'DateRange' to allow 'in' checks with a range of dates. 14 | Alternatively, you can make the class 'DateRange' implement the 'ClosedRange' interface from the standard library. 15 | """, 16 | documentation = doc26(), 17 | references = { range: ClosedRange -> } 18 | ) 19 | 20 | fun checkInRange(date: MyDate, first: MyDate, last: MyDate): Boolean { 21 | todoTask26_() 22 | // return date in DateRange(first, last) 23 | } 24 | -------------------------------------------------------------------------------- /src/iii_conventions/n27RangeTo.kt: -------------------------------------------------------------------------------- 1 | package iii_conventions 2 | 3 | import util.TODO 4 | import util.doc26 5 | 6 | fun todoTask27(): Nothing = TODO( 7 | """ 8 | Task 27. 9 | Uncomment the commented code and make it compile. 10 | Make all the changes to the file MyDate.kt. 11 | 12 | Tips: To make '..' work implement a 'MyDate.rangeTo()' extension function returning 'DateRange'. 13 | """, 14 | documentation = doc26() 15 | ) 16 | 17 | fun checkInRange2(date: MyDate, first: MyDate, last: MyDate): Boolean { 18 | todoTask27() 19 | // return date in first..last 20 | } 21 | -------------------------------------------------------------------------------- /src/iii_conventions/n28ForLoop.kt: -------------------------------------------------------------------------------- 1 | package iii_conventions 2 | 3 | import util.TODO 4 | import util.doc28 5 | 6 | fun iterateOverCollection(collection: Collection) { 7 | for (element in collection) { 8 | } 9 | } 10 | 11 | fun iterateOverString() { 12 | // You can iterate over anything that has an 'iterator' method, member or extension. 13 | for (c in "abcd") { 14 | } 15 | "abcd".iterator() //library extension method 16 | } 17 | 18 | fun iterateOverRange() { 19 | for (i in 1..10) { 20 | } 21 | for (c in 'a'..'z') { 22 | } 23 | } 24 | 25 | fun todoTask28(): Nothing = TODO( 26 | """ 27 | Task 28. 28 | Uncomment the commented code and make it compile. 29 | Make all the changes to the file MyDate.kt. 30 | 31 | Tips: Make the class 'DateRange' implement 'Iterable'. 32 | You can use object expression or declare an extra class to implement 'Iterator'. 33 | Use the utility function 'MyDate.nextDay()'. 34 | """, 35 | documentation = doc28(), 36 | references = { date: MyDate -> DateRange(date, date.nextDay()) }) 37 | 38 | 39 | fun iterateOverDateRange(firstDate: MyDate, secondDate: MyDate, handler: (MyDate) -> Unit) { 40 | todoTask28() 41 | // for (date in firstDate..secondDate) { 42 | // handler(date) 43 | // } 44 | } 45 | -------------------------------------------------------------------------------- /src/iii_conventions/n29OperatorsOverloading.kt: -------------------------------------------------------------------------------- 1 | package iii_conventions 2 | 3 | import iii_conventions.TimeInterval.* 4 | import util.TODO 5 | 6 | fun todoTask29(): Nothing = TODO( 7 | """ 8 | Task 29. 9 | Implement a kind of date arithmetic. Support adding years, weeks and days to a date. 10 | Use classes 'MyDate' and 'TimeInterval'. 11 | Use the provided utility function 'MyDate.addTimeIntervals'. 12 | Uncomment the commented line and make it compile. 13 | 14 | (1). Add an extension function 'plus()' to MyDate, taking a TimeInterval as an argument. 15 | (2). Support adding several time intervals to a date. Add an extra class. 16 | If you have any problems, see the iii_conventions/_29_Tips.kt file. 17 | """, 18 | references = { date: MyDate, timeInterval: TimeInterval -> 19 | date.addTimeIntervals(timeInterval, 1) 20 | }) 21 | 22 | fun task29_1(today: MyDate): MyDate { 23 | todoTask29() 24 | // return today + YEAR + WEEK 25 | } 26 | 27 | fun task29_2(today: MyDate): MyDate { 28 | todoTask29() 29 | // return today + YEAR * 2 + WEEK * 3 + DAY * 5 30 | } 31 | 32 | -------------------------------------------------------------------------------- /src/iii_conventions/n29Tips.kt: -------------------------------------------------------------------------------- 1 | package iii_conventions 2 | 3 | fun todoTask29_2(): Nothing = TODO( 4 | """ 5 | Task29.2. 6 | Support adding several time intervals to a date. 7 | Add an extra class for storing the time interval and the number of intervals, 8 | e.g. 'class RepeatedTimeInterval(val ti: TimeInterval, val n: Int)'. 9 | Add an extension function 'times' to 'TimeInterval', constructing the value of this class. 10 | Add an extension function 'plus' to 'MyDate', taking a 'RepeatedTimeInterval' as an argument. 11 | """ 12 | ) 13 | -------------------------------------------------------------------------------- /src/iii_conventions/n30DestructuringDeclarations.kt: -------------------------------------------------------------------------------- 1 | package iii_conventions.multiAssignemnt 2 | 3 | import util.TODO 4 | import util.doc30 5 | 6 | fun todoTask30(): Nothing = TODO( 7 | """ 8 | Task 30. 9 | Read about destructuring declarations and make the following code compile by adding one 'data' modifier. 10 | """, 11 | documentation = doc30() 12 | ) 13 | 14 | class MyDate(val year: Int, val month: Int, val dayOfMonth: Int) 15 | 16 | fun isLeapDay(date: MyDate): Boolean { 17 | todoTask30() 18 | // val (year, month, dayOfMonth) = date 19 | // 20 | // // 29 February of a leap year 21 | // return isLeapYear(year) && month == 1 && dayOfMonth == 29 22 | } 23 | 24 | // Years which are multiples of four (with the exception of years divisible by 100 but not by 400) 25 | fun isLeapYear(year: Int): Boolean = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) -------------------------------------------------------------------------------- /src/iii_conventions/n31Invoke.kt: -------------------------------------------------------------------------------- 1 | package iii_conventions 2 | 3 | import util.TODO 4 | 5 | 6 | class Invokable 7 | 8 | fun todoTask31(): Nothing = TODO( 9 | """ 10 | Task 31. 11 | Change the class 'Invokable' to count the number of invocations: 12 | for 'invokable()()()()' it should be 4. 13 | """, 14 | references = { invokable: Invokable -> }) 15 | 16 | fun task31(invokable: Invokable): Int { 17 | todoTask31() 18 | // return invokable()()()().getNumberOfInvocations() 19 | } 20 | -------------------------------------------------------------------------------- /src/iv_properties/n32Properties.kt: -------------------------------------------------------------------------------- 1 | package iv_properties 2 | 3 | import util.TODO 4 | import util.doc32 5 | 6 | class PropertyExample() { 7 | var counter = 0 8 | var propertyWithCounter: Int? = todoTask32() 9 | } 10 | 11 | fun todoTask32(): Nothing = TODO( 12 | """ 13 | Task 32. 14 | Add a custom setter to 'PropertyExample.propertyWithCounter' so that 15 | the 'counter' property is incremented every time 'propertyWithCounter' is assigned to. 16 | """, 17 | documentation = doc32(), 18 | references = { PropertyExample() } 19 | ) 20 | -------------------------------------------------------------------------------- /src/iv_properties/n33LazyProperty.kt: -------------------------------------------------------------------------------- 1 | package iv_properties 2 | 3 | import util.TODO 4 | 5 | class LazyProperty(val initializer: () -> Int) { 6 | val lazy: Int = todoTask33() 7 | } 8 | 9 | fun todoTask33(): Nothing = TODO( 10 | """ 11 | Task 33. 12 | Add a custom getter to make the 'lazy' val really lazy. 13 | It should be initialized by the invocation of 'initializer()' 14 | at the moment of the first access. 15 | You can add as many additional properties as you need. 16 | Do not use delegated properties yet! 17 | """, 18 | references = { LazyProperty({ 42 }).lazy } 19 | ) 20 | -------------------------------------------------------------------------------- /src/iv_properties/n34DelegatesExamples.kt: -------------------------------------------------------------------------------- 1 | package iv_properties 2 | 3 | import util.TODO 4 | import util.doc34 5 | 6 | class LazyPropertyUsingDelegates(val initializer: () -> Int) { 7 | val lazyValue: Int by todoTask34() 8 | } 9 | 10 | fun todoTask34(): Lazy = TODO( 11 | """ 12 | Task 34. 13 | Read about delegated properties and make the property lazy by using delegates. 14 | """, 15 | documentation = doc34() 16 | ) 17 | -------------------------------------------------------------------------------- /src/iv_properties/n35HowDelegatesWork.kt: -------------------------------------------------------------------------------- 1 | package iv_properties 2 | 3 | import iii_conventions.MyDate 4 | import util.TODO 5 | import java.util.* 6 | import kotlin.properties.ReadWriteProperty 7 | import kotlin.reflect.KProperty 8 | 9 | fun todoTask35(): Nothing = TODO( 10 | """ 11 | Task 35. 12 | A delegate expression must have special 'get' and 'set' methods. 13 | You can see their signatures below as members of the 'ReadWriteProperty' interface. 14 | 15 | Implement the members of the class 'EffectiveDate' so it can be delegated to. 16 | Store only the time in milliseconds in 'timeInMillis' property. 17 | Use the extension functions 'MyDate.toMillis' and 'Long.toDate'. 18 | """, 19 | references = { date: MyDate -> date.toMillis().toDate() } 20 | ) 21 | 22 | class D { 23 | var date by EffectiveDate() 24 | // The property date$delegate of type EffectiveDate is created; 25 | // the generated 'get' and 'set' accessors for 'date' are delegated to it. 26 | // You can look at the bytecode (by calling "Show Kotlin Bytecode" action in IntelliJ IDEA) for details. 27 | } 28 | 29 | class EffectiveDate : ReadWriteProperty { 30 | var timeInMillis: Long? = null 31 | 32 | operator override fun getValue(thisRef: R, property: KProperty<*>): MyDate = todoTask35() 33 | operator override fun setValue(thisRef: R, property: KProperty<*>, value: MyDate) = todoTask35() 34 | } 35 | 36 | fun MyDate.toMillis(): Long { 37 | val c = Calendar.getInstance() 38 | c.set(year, month, dayOfMonth, 0, 0, 0) 39 | c.set(Calendar.MILLISECOND, 0) 40 | return c.timeInMillis 41 | } 42 | 43 | fun Long.toDate(): MyDate { 44 | val c = Calendar.getInstance() 45 | c.timeInMillis = this 46 | return MyDate(c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DATE)) 47 | } 48 | -------------------------------------------------------------------------------- /src/util/JavaCode.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | import com.google.common.collect.Sets; 4 | 5 | import java.util.Set; 6 | 7 | public class JavaCode { 8 | public static Set set = Sets.newHashSet(); 9 | 10 | public JavaCode() { 11 | set.add(this.getClass().getName()); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/util/LinksToDocumentation.kt: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | /** 4 | * @see Function syntax 5 | */ 6 | fun doc0() {} 7 | 8 | /** 9 | * @see Default and named arguments 10 | */ 11 | fun doc2() {} 12 | 13 | 14 | /** 15 | * @see Lambdas 16 | */ 17 | fun doc4() {} 18 | 19 | /** 20 | * @see String literals and string templates 21 | */ 22 | fun doc5() {} 23 | 24 | /** 25 | * @see Classes, 26 | Properties, 27 | Data classes 28 | */ 29 | fun doc6() {} 30 | 31 | /** 32 | * @see Null safety and safe calls 33 | */ 34 | fun doc7() {} 35 | 36 | /** 37 | * @see Smart casts, 38 | * When, 39 | * Sealed classes 40 | */ 41 | fun doc8() {} 42 | 43 | /** 44 | * @see Extension functions 45 | */ 46 | fun doc9() {} 47 | 48 | /** 49 | * @see Object expressions 50 | */ 51 | fun doc10() {} 52 | 53 | /** 54 | * @see SAM-conversions 55 | */ 56 | fun doc11() {} 57 | 58 | /** 59 | * @see Collections, 60 | * Hierarchy illustration 61 | */ 62 | fun doc12() {} 63 | 64 | /** 65 | * @see Operator overloading 66 | */ 67 | fun doc25() {} 68 | 69 | /** 70 | * @see For-loop 71 | */ 72 | fun doc28() {} 73 | 74 | /** 75 | * @see Ranges 76 | */ 77 | fun doc26() {} 78 | 79 | /** 80 | * @see Destructuring declarations 81 | */ 82 | fun doc30() {} 83 | 84 | /** 85 | * @see Properties 86 | */ 87 | fun doc32() {} 88 | 89 | /** 90 | * @see Delegated Properties 91 | */ 92 | fun doc34() {} 93 | 94 | /** 95 | * @see Function Literals with Receiver 96 | */ 97 | fun doc36() {} 98 | 99 | /** 100 | * @see Type-Safe Builders 101 | */ 102 | fun doc39() {} -------------------------------------------------------------------------------- /src/util/YourOldJavaCodeUsingRunnable.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | public class YourOldJavaCodeUsingRunnable { 4 | public static void run(Runnable runnable) { 5 | runnable.run(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/util/kotlinUtil.kt: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | @Suppress("UNUSED_PARAMETER") 4 | fun TODO( 5 | task: String, 6 | documentation: Unit = Unit, 7 | references: Function = {} 8 | ): Nothing = throw NotImplementedException(task) 9 | 10 | class NotImplementedException(message: String) : Exception(message) -------------------------------------------------------------------------------- /src/util/questions/Question.kt: -------------------------------------------------------------------------------- 1 | package util.questions 2 | 3 | enum class Answer { 4 | a, b, c 5 | } 6 | -------------------------------------------------------------------------------- /src/v_builders/data.kt: -------------------------------------------------------------------------------- 1 | package v_builders.data 2 | 3 | data class Product(val description: String, val price: Double, val popularity: Int) 4 | 5 | val cactus = Product("cactus", 11.2, 13) 6 | val cake = Product("cake", 3.2, 111) 7 | val camera = Product("camera", 134.5, 2) 8 | val car = Product("car", 30000.0, 0) 9 | val carrot = Product("carrot", 1.34, 5) 10 | val cellPhone = Product("cell phone", 129.9, 99) 11 | val chimney = Product("chimney", 190.0, 2) 12 | val certificate = Product("certificate", 99.9, 1) 13 | val cigar = Product("cigar", 8.0, 51) 14 | val coffee = Product("coffee", 8.0, 67) 15 | val coffeeMaker = Product("coffee maker", 201.2, 1) 16 | val cola = Product("cola", 4.0, 67) 17 | val cranberry = Product("cranberry", 4.1, 39) 18 | val crocs = Product("crocs", 18.7, 10) 19 | val crocodile = Product("crocodile", 20000.2, 1) 20 | val cushion = Product("cushion", 131.0, 0) 21 | 22 | fun getProducts() = listOf(cactus, cake, camera, car, carrot, cellPhone, chimney, certificate, cigar, coffee, coffeeMaker, 23 | cola, cranberry, crocs, crocodile, cushion) -------------------------------------------------------------------------------- /src/v_builders/html.kt: -------------------------------------------------------------------------------- 1 | package v_builders.htmlLibrary 2 | 3 | import java.util.* 4 | 5 | open class Tag(val name: String) { 6 | val children: MutableList = ArrayList() 7 | val attributes: MutableList = ArrayList() 8 | 9 | override fun toString(): String { 10 | return "<$name" + 11 | (if (attributes.isEmpty()) "" else attributes.joinToString(separator = " ", prefix = " ")) + ">" + 12 | (if (children.isEmpty()) "" else children.joinToString(separator = "")) + 13 | "" 14 | } 15 | } 16 | 17 | class Attribute(val name: String, val value: String) { 18 | override fun toString() = """$name="$value"""" 19 | } 20 | 21 | fun T.set(name: String, value: String?): T { 22 | if (value != null) { 23 | attributes.add(Attribute(name, value)) 24 | } 25 | return this 26 | } 27 | 28 | fun Tag.doInit(tag: T, init: T.() -> Unit): T { 29 | tag.init() 30 | children.add(tag) 31 | return tag 32 | } 33 | 34 | class Html : Tag("html") 35 | class Table : Tag("table") 36 | class Center : Tag("center") 37 | class TR : Tag("tr") 38 | class TD : Tag("td") 39 | class Text(val text: String) : Tag("b") { 40 | override fun toString() = text 41 | } 42 | 43 | fun html(init: Html.() -> Unit): Html = Html().apply(init) 44 | 45 | fun Html.table(init: Table.() -> Unit) = doInit(Table(), init) 46 | fun Html.center(init: Center.() -> Unit) = doInit(Center(), init) 47 | 48 | fun Table.tr(color: String? = null, init: TR.() -> Unit) = doInit(TR(), init).set("bgcolor", color) 49 | 50 | fun TR.td(color: String? = null, align: String = "left", init: TD.() -> Unit) = doInit(TD(), init).set("align", align).set("bgcolor", color) 51 | 52 | fun Tag.text(s: Any?) = doInit(Text(s.toString()), {}) 53 | 54 | -------------------------------------------------------------------------------- /src/v_builders/htmlDemo.kt: -------------------------------------------------------------------------------- 1 | package v_builders.demo 2 | 3 | import v_builders.renderProductTable 4 | import javax.swing.JFrame 5 | import javax.swing.JLabel 6 | import javax.swing.JScrollPane 7 | import javax.swing.SwingConstants.CENTER 8 | 9 | 10 | fun main(args: Array) { 11 | with(JFrame("Product popularity")) { 12 | setSize(600, 600) 13 | defaultCloseOperation = JFrame.EXIT_ON_CLOSE 14 | add(JScrollPane(JLabel(renderProductTable(), CENTER))) 15 | isVisible = true 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /src/v_builders/n36ExtensionFunctionLiterals.kt: -------------------------------------------------------------------------------- 1 | package v_builders 2 | 3 | import util.TODO 4 | import util.doc36 5 | 6 | fun todoTask36(): Nothing = TODO( 7 | """ 8 | Task 36. 9 | Read about extension function literals. 10 | You can declare `isEven` and `isOdd` as values, that can be called as extension functions. 11 | Complete the declarations below. 12 | """, 13 | documentation = doc36() 14 | ) 15 | 16 | fun task36(): List { 17 | val isEven: Int.() -> Boolean = { todoTask36() } 18 | val isOdd: Int.() -> Boolean = { todoTask36() } 19 | 20 | return listOf(42.isOdd(), 239.isOdd(), 294823098.isEven()) 21 | } 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/v_builders/n37StringAndMapBuilders.kt: -------------------------------------------------------------------------------- 1 | package v_builders 2 | 3 | import util.TODO 4 | 5 | fun buildStringExample(): String { 6 | fun buildString(build: StringBuilder.() -> Unit): String { 7 | val stringBuilder = StringBuilder() 8 | stringBuilder.build() 9 | return stringBuilder.toString() 10 | } 11 | 12 | return buildString { 13 | this.append("Numbers: ") 14 | for (i in 1..10) { 15 | // 'this' can be omitted 16 | append(i) 17 | } 18 | } 19 | } 20 | 21 | fun todoTask37(): Nothing = TODO( 22 | """ 23 | Task 37. 24 | Uncomment the commented code and make it compile. 25 | Add and implement function 'buildMap' with one parameter (of type extension function) creating a new HashMap, 26 | building it and returning it as a result. 27 | """ 28 | ) 29 | 30 | fun task37(): Map { 31 | todoTask37() 32 | // return buildMap { 33 | // put(0, "0") 34 | // for (i in 1..10) { 35 | // put(i, "$i") 36 | // } 37 | // } 38 | } 39 | -------------------------------------------------------------------------------- /src/v_builders/n38TheFunctionApply.kt: -------------------------------------------------------------------------------- 1 | package v_builders.examples 2 | 3 | fun todoTask38(): Nothing = TODO( 4 | """ 5 | Task 38. 6 | The previous examples can be rewritten with the library function 'apply' (see examples below). 7 | Write your own implementation of the function 'apply' named 'myApply'. 8 | """ 9 | ) 10 | 11 | fun T.myApply(f: T.() -> Unit): T { 12 | todoTask38() 13 | } 14 | 15 | fun buildString(): String { 16 | return StringBuilder().myApply { 17 | append("Numbers: ") 18 | for (i in 1..10) { 19 | append(i) 20 | } 21 | }.toString() 22 | } 23 | 24 | fun buildMap(): Map { 25 | return hashMapOf().myApply { 26 | put(0, "0") 27 | for (i in 1..10) { 28 | put(i, "$i") 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/v_builders/n39HtmlBuilders.kt: -------------------------------------------------------------------------------- 1 | package v_builders 2 | 3 | import util.TODO 4 | import util.doc39 5 | import v_builders.data.getProducts 6 | import v_builders.htmlLibrary.* 7 | 8 | fun getTitleColor() = "#b9c9fe" 9 | fun getCellColor(row: Int, column: Int) = if ((row + column) % 2 == 0) "#dce4ff" else "#eff2ff" 10 | 11 | fun todoTask39(): Nothing = TODO( 12 | """ 13 | Task 39. 14 | 1) Fill the table with the proper values from products. 15 | 2) Color the table like a chess board (using getTitleColor() and getCellColor() functions above). 16 | Pass a color as an argument to functions 'tr', 'td'. 17 | You can call the 'main' function in the 'htmlDemo.kt' to see the rendered table. 18 | """, 19 | documentation = doc39() 20 | ) 21 | 22 | fun renderProductTable(): String { 23 | return html { 24 | table { 25 | tr { 26 | td { 27 | text("Product") 28 | } 29 | td { 30 | text("Price") 31 | } 32 | td { 33 | text("Popularity") 34 | } 35 | } 36 | val products = getProducts() 37 | todoTask39() 38 | } 39 | }.toString() 40 | } 41 | -------------------------------------------------------------------------------- /src/v_builders/n40BuildersHowItWorks.kt: -------------------------------------------------------------------------------- 1 | package v_builders.builders 2 | 3 | import util.questions.Answer 4 | import util.questions.Answer.* 5 | 6 | fun todoTask40(): Nothing = TODO( 7 | """ 8 | Task 40. 9 | Look at the questions below and give your answers: 10 | change 'insertAnswerHere()' in task40's map to your choice (a, b or c). 11 | 12 | All the constants are imported via 'util.questions.Answer.*', so they can be accessed by name. 13 | 14 | """ 15 | ) 16 | 17 | fun insertAnswerHere(): Nothing = todoTask40() 18 | 19 | fun task40() = linkedMapOf( 20 | /* 21 | 1. In the Kotlin code 22 | tr { 23 | td { 24 | text("Product") 25 | } 26 | td { 27 | text("Popularity") 28 | } 29 | } 30 | 'td' is: 31 | a. special built-in syntactic construct 32 | b. function declaration 33 | c. function invocation 34 | */ 35 | 1 to insertAnswerHere(), 36 | 37 | /* 38 | 2. In the Kotlin code 39 | tr (color = "yellow") { 40 | td { 41 | text("Product") 42 | } 43 | td { 44 | text("Popularity") 45 | } 46 | } 47 | 'color' is: 48 | a. new variable declaration 49 | b. argument name 50 | c. argument value 51 | */ 52 | 2 to insertAnswerHere(), 53 | 54 | /* 55 | 3. The block 56 | { 57 | text("Product") 58 | } 59 | from the previous question is: 60 | a. block inside built-in syntax construction 'td' 61 | b. function literal (or "lambda") 62 | c. something mysterious 63 | 64 | */ 65 | 3 to insertAnswerHere(), 66 | 67 | /* 68 | 4. For the code 69 | tr (color = "yellow") { 70 | this.td { 71 | text("Product") 72 | } 73 | td { 74 | text("Popularity") 75 | } 76 | } 77 | which of the following is true: 78 | a. this code doesn't compile 79 | b. 'this' refers to an instance of an outer class 80 | c. 'this' refers to a receiver parameter TR of the function literal: 81 | tr (color = "yellow") { 82 | this@tr.td { 83 | text("Product") 84 | } 85 | } 86 | */ 87 | 4 to insertAnswerHere() 88 | ) 89 | -------------------------------------------------------------------------------- /src/vi_generics/n41GenericFunctions.kt: -------------------------------------------------------------------------------- 1 | package vi_generics 2 | 3 | import util.TODO 4 | import java.util.* 5 | 6 | fun task41(): Nothing = TODO( 7 | """ 8 | Task41. 9 | Add a 'partitionTo' function that splits a collection into two collections according to a predicate. 10 | Uncomment the commented invocations of 'partitionTo' below and make them compile. 11 | 12 | There is a 'partition()' function in the standard library that always returns two newly created lists. 13 | You should write a function that splits the collection into two collections given as arguments. 14 | The signature of the 'toCollection()' function from the standard library may help you. 15 | """, 16 | references = { l: List -> 17 | l.partition { it > 0 } 18 | l.toCollection(HashSet()) 19 | } 20 | ) 21 | 22 | fun List.partitionWordsAndLines(): Pair, List> { 23 | task41() 24 | // return partitionTo(ArrayList(), ArrayList()) { s -> !s.contains(" ") } 25 | } 26 | 27 | fun Set.partitionLettersAndOtherSymbols(): Pair, Set> { 28 | task41() 29 | // return partitionTo(HashSet(), HashSet()) { c -> c in 'a'..'z' || c in 'A'..'Z'} 30 | } -------------------------------------------------------------------------------- /test/i_introduction/_0_Hello_World/N00StartKtTest.kt: -------------------------------------------------------------------------------- 1 | package i_introduction._0_Hello_World 2 | 3 | import org.junit.jupiter.api.Assertions.assertEquals 4 | import org.junit.jupiter.api.Test 5 | 6 | class N00StartKtTest { 7 | @Test 8 | fun testOk() { 9 | assertEquals("OK", task0()) 10 | } 11 | } -------------------------------------------------------------------------------- /test/i_introduction/_10_Object_Expressions/N10ObjectExpressionsKtTest.kt: -------------------------------------------------------------------------------- 1 | package i_introduction._10_Object_Expressions 2 | 3 | import org.junit.jupiter.api.Assertions.assertEquals 4 | import org.junit.jupiter.api.Test 5 | 6 | class N10ObjectExpressionsKtTest { 7 | @Test 8 | fun testSort() { 9 | assertEquals(listOf(5, 2, 1), task10()) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/i_introduction/_11_SAM_Conversions/N11SAMConversionsKtTest.kt: -------------------------------------------------------------------------------- 1 | package i_introduction._11_SAM_Conversions 2 | 3 | import org.junit.jupiter.api.Assertions.assertEquals 4 | import org.junit.jupiter.api.Test 5 | 6 | class N11SAMConversionsKtTest { 7 | @Test 8 | fun testSort() { 9 | assertEquals(listOf(5, 2, 1), task11()) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/i_introduction/_12_Extensions_On_Collections/N12ExtensionsOnCollectionsKtTest.kt: -------------------------------------------------------------------------------- 1 | package i_introduction._12_Extensions_On_Collections 2 | 3 | import org.junit.jupiter.api.Assertions.assertEquals 4 | import org.junit.jupiter.api.Test 5 | 6 | class N12ExtensionsOnCollectionsKtTest { 7 | @Test 8 | fun testSort() { 9 | assertEquals(listOf(5, 2, 1), task12()) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/i_introduction/_1_Java_To_Kotlin_Converter/N01JavaToKotlinConverterKtTest.kt: -------------------------------------------------------------------------------- 1 | package i_introduction._1_Java_To_Kotlin_Converter 2 | 3 | import org.junit.jupiter.api.Assertions.assertEquals 4 | import org.junit.jupiter.api.Test 5 | 6 | class N01JavaToKotlinConverterKtTest { 7 | @Test 8 | fun collection() { 9 | assertEquals("{1, 2, 3, 42, 555}", task1(listOf(1, 2, 3, 42, 555))) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/i_introduction/_2_Named_Arguments/N02NamedArgumentsKtTest.kt: -------------------------------------------------------------------------------- 1 | package i_introduction._2_Named_Arguments 2 | 3 | import org.junit.jupiter.api.Assertions.assertEquals 4 | import org.junit.jupiter.api.Test 5 | 6 | class N02NamedArgumentsKtTest { 7 | 8 | @Test 9 | fun testJoinToString() { 10 | assertEquals("{1, 2, 3, 42, 555}", task2(listOf(1, 2, 3, 42, 555))) 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /test/i_introduction/_3_Default_Arguments/N03DefaultArgumentsKtTest.kt: -------------------------------------------------------------------------------- 1 | package i_introduction._3_Default_Arguments 2 | 3 | import org.junit.jupiter.api.Assertions.assertEquals 4 | import org.junit.jupiter.api.Test 5 | 6 | class N03DefaultArgumentsKtTest { 7 | 8 | @Test 9 | fun testDefaultAndNamedParams() { 10 | assertEquals("a42b1C42D2", task3()) 11 | } 12 | } -------------------------------------------------------------------------------- /test/i_introduction/_4_Lambdas/N04LambdasKtTest.kt: -------------------------------------------------------------------------------- 1 | package i_introduction._4_Lambdas 2 | 3 | import org.junit.jupiter.api.Assertions.assertFalse 4 | import org.junit.jupiter.api.Assertions.assertTrue 5 | import org.junit.jupiter.api.Test 6 | 7 | class N04LambdasKtTest { 8 | @Test 9 | fun contains() { 10 | assertTrue(task4(listOf(1, 2, 3))) 11 | } 12 | 13 | @Test 14 | fun notContains() { 15 | assertFalse(task4(listOf(1, 3, 5))) 16 | } 17 | } -------------------------------------------------------------------------------- /test/i_introduction/_5_String_Templates/N05StringTemplatesKtTest.kt: -------------------------------------------------------------------------------- 1 | package i_introduction._5_String_Templates 2 | 3 | import org.junit.jupiter.api.Assertions.assertFalse 4 | import org.junit.jupiter.api.Assertions.assertTrue 5 | import org.junit.jupiter.api.Test 6 | 7 | class N05StringTemplatesKtTest { 8 | @Test 9 | fun match() { 10 | assertTrue("11 MAR 1952".matches(task5().toRegex())) 11 | } 12 | 13 | @Test 14 | fun match1() { 15 | assertTrue("24 AUG 1957".matches(task5().toRegex())) 16 | } 17 | 18 | @Test 19 | fun doNotMatch() { 20 | assertFalse("24 RRR 1957".matches(task5().toRegex())) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/i_introduction/_6_Data_Classes/N06DataClassesKtTest.kt: -------------------------------------------------------------------------------- 1 | package i_introduction._6_Data_Classes 2 | 3 | import org.junit.jupiter.api.Assertions.assertEquals 4 | import org.junit.jupiter.api.Test 5 | 6 | 7 | class N06DataClassesKtTest { 8 | @Test 9 | fun testListOfPeople() { 10 | assertEquals("[Person(name=Alice, age=29), Person(name=Bob, age=31)]", task6().toString()) 11 | } 12 | } -------------------------------------------------------------------------------- /test/i_introduction/_7_Nullable_Types/N07NullableTypesKtTest.kt: -------------------------------------------------------------------------------- 1 | package i_introduction._7_Nullable_Types 2 | 3 | import org.junit.jupiter.api.Assertions.assertEquals 4 | import org.junit.jupiter.api.Test 5 | 6 | class N07NullableTypesKtTest { 7 | private fun testSendMessageToClient( 8 | client: Client?, 9 | message: String?, 10 | email: String? = null, 11 | shouldBeInvoked: Boolean = false 12 | ) { 13 | var invoked = false 14 | sendMessageToClient(client, message, object : Mailer { 15 | override fun sendMessage(actualEmail: String, actualMessage: String) { 16 | invoked = true 17 | assertEquals(message, actualMessage, "The message is not as expected:") 18 | assertEquals(email, actualEmail, "The email is not as expected:") 19 | } 20 | }) 21 | assertEquals(shouldBeInvoked, invoked, 22 | "The function 'sendMessage' should${if (shouldBeInvoked) "" else "n't"} be invoked") 23 | } 24 | 25 | @Test 26 | fun everythingIsOk() { 27 | testSendMessageToClient(Client(PersonalInfo("bob@gmail.com")), 28 | "Hi Bob! We have an awesome proposition for you...", 29 | "bob@gmail.com", 30 | true) 31 | } 32 | 33 | @Test 34 | fun noMessage() { 35 | testSendMessageToClient(Client(PersonalInfo("bob@gmail.com")), null) 36 | } 37 | 38 | @Test 39 | fun noEmail() { 40 | testSendMessageToClient(Client(PersonalInfo(null)), "Hi Bob! We have an awesome proposition for you...") 41 | } 42 | 43 | @Test 44 | fun noPersonalInfo() { 45 | testSendMessageToClient(Client(null), "Hi Bob! We have an awesome proposition for you...") 46 | } 47 | 48 | @Test 49 | fun noClient() { 50 | testSendMessageToClient(null, "Hi Bob! We have an awesome proposition for you...") 51 | } 52 | } -------------------------------------------------------------------------------- /test/i_introduction/_8_Smart_Casts/N08SmartCastsKtTest.kt: -------------------------------------------------------------------------------- 1 | package i_introduction._8_Smart_Casts 2 | 3 | import org.junit.jupiter.api.Assertions.assertEquals 4 | import org.junit.jupiter.api.Test 5 | 6 | class N08SmartCastsKtTest { 7 | @Test 8 | fun testNum() { 9 | assertEquals(2, eval(Num(2)), "'eval' on Num should work:") 10 | } 11 | 12 | @Test 13 | fun testSum() { 14 | assertEquals(3, eval(Sum(Num(2), Num(1))), "'eval' on Sum should work:") 15 | } 16 | 17 | @Test 18 | fun testRecursion() { 19 | assertEquals(6, eval(Sum(Sum(Num(1), Num(2)), Num(3))), "'eval' should work recursively:") 20 | } 21 | } -------------------------------------------------------------------------------- /test/i_introduction/_9_Extension_Functions/N09ExtensionFunctionsKtTest.kt: -------------------------------------------------------------------------------- 1 | package i_introduction._9_Extension_Functions 2 | 3 | import org.junit.jupiter.api.Assertions.assertEquals 4 | import org.junit.jupiter.api.Test 5 | 6 | class N09ExtensionFunctionsKtTest { 7 | @Test 8 | fun testIntExtension() { 9 | assertEquals(RationalNumber(4, 1), 4.r(), "Rational number creation error: ") 10 | } 11 | 12 | @Test 13 | fun testPairExtension() { 14 | assertEquals(RationalNumber(2, 3), Pair(2, 3).r(), "Rational number creation error: ") 15 | } 16 | } -------------------------------------------------------------------------------- /test/ii_collections/N13IntroductionKtTest.kt: -------------------------------------------------------------------------------- 1 | package ii_collections 2 | 3 | import ii_collections.data.customers 4 | import ii_collections.data.shop 5 | import org.junit.jupiter.api.Assertions.assertEquals 6 | import org.junit.jupiter.api.Test 7 | 8 | class N13IntroductionKtTest { 9 | @Test 10 | fun testSetOfCustomers() { 11 | assertEquals(customers.values.toSet(), shop.getSetOfCustomers()) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/ii_collections/N14FilterMapKtTest.kt: -------------------------------------------------------------------------------- 1 | package ii_collections 2 | 3 | import ii_collections.data.* 4 | import org.junit.jupiter.api.Assertions.assertEquals 5 | import org.junit.jupiter.api.Test 6 | 7 | class N14FilterMapKtTest { 8 | @Test 9 | fun testCitiesCustomersAreFrom() { 10 | assertEquals(setOf(Canberra, Vancouver, Budapest, Ankara, Tokyo), shop.getCitiesCustomersAreFrom()) 11 | } 12 | 13 | /** 14 | * Returns the list of the customers who live in the city 'city' 15 | */ 16 | @Test 17 | fun testCustomersFromCity() { 18 | assertEquals(listOf(customers[lucas], customers[cooper]), shop.getCustomersFrom(Canberra)) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/ii_collections/N15AllAnyAndOtherPredicatesKtTest.kt: -------------------------------------------------------------------------------- 1 | package ii_collections 2 | 3 | import ii_collections.data.* 4 | import org.junit.jupiter.api.Assertions.* 5 | import org.junit.jupiter.api.Test 6 | 7 | class N15AllAnyAndOtherPredicatesKtTest { 8 | @Test 9 | fun testCustomerIsFromCity() { 10 | assertTrue(customers[lucas]!!.isFrom(Canberra)) 11 | assertFalse(customers[lucas]!!.isFrom(Budapest)) 12 | } 13 | 14 | @Test 15 | fun testAllCustomersAreFromCity() { 16 | assertFalse(shop.checkAllCustomersAreFrom(Canberra)) 17 | } 18 | 19 | @Test 20 | fun testAnyCustomerIsFromCity() { 21 | assertTrue(shop.hasCustomerFrom(Canberra)) 22 | } 23 | 24 | @Test 25 | fun testCountCustomersFromCity() { 26 | assertEquals(2, shop.countCustomersFrom(Canberra)) 27 | } 28 | 29 | @Test 30 | fun testFirstCustomerFromCity() { 31 | assertEquals(customers[lucas], shop.findFirstCustomerFrom(Canberra)) 32 | assertEquals(null, shop.findFirstCustomerFrom(City("Chicago"))) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/ii_collections/N16FlatMapKtTest.kt: -------------------------------------------------------------------------------- 1 | package ii_collections 2 | 3 | import ii_collections.data.* 4 | import org.junit.jupiter.api.Assertions.assertEquals 5 | import org.junit.jupiter.api.Test 6 | 7 | class N16FlatMapKtTest { 8 | @Test 9 | fun testGetOrderedProductsSet() { 10 | assertEquals(setOf(idea), customers[reka]!!.orderedProducts) 11 | } 12 | 13 | @Test 14 | fun testGetAllOrderedProducts() { 15 | assertEquals(orderedProducts, shop.allOrderedProducts) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test/ii_collections/N17MaxMinKtTest.kt: -------------------------------------------------------------------------------- 1 | package ii_collections 2 | 3 | import ii_collections.data.* 4 | import org.junit.jupiter.api.Assertions.assertEquals 5 | import org.junit.jupiter.api.Test 6 | 7 | class N17MaxMinKtTest { 8 | @Test 9 | fun testCustomerWithMaximumNumberOfOrders() { 10 | assertEquals(customers[reka], shop.getCustomerWithMaximumNumberOfOrders()) 11 | } 12 | 13 | @Test 14 | fun testTheMostExpensiveOrderedProduct() { 15 | assertEquals(rubyMine, customers[nathan]!!.getMostExpensiveOrderedProduct()) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test/ii_collections/N18SortKtTest.kt: -------------------------------------------------------------------------------- 1 | package ii_collections 2 | 3 | import ii_collections.data.shop 4 | import ii_collections.data.sortedCustomers 5 | import org.junit.jupiter.api.Assertions.assertEquals 6 | import org.junit.jupiter.api.Test 7 | 8 | class N18SortKtTest { 9 | @Test 10 | fun testGetCustomersSortedByNumberOfOrders() { 11 | assertEquals(sortedCustomers, shop.getCustomersSortedByNumberOfOrders()) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/ii_collections/N19SumKtTest.kt: -------------------------------------------------------------------------------- 1 | package ii_collections 2 | 3 | import ii_collections.data.customers 4 | import ii_collections.data.lucas 5 | import ii_collections.data.nathan 6 | import org.junit.jupiter.api.Assertions.assertEquals 7 | import org.junit.jupiter.api.Test 8 | 9 | class N19SumKtTest { 10 | @Test 11 | fun testGetTotalOrderPrice() { 12 | assertEquals(148.0, customers[nathan]!!.getTotalOrderPrice(), 0.001) 13 | } 14 | 15 | @Test 16 | fun testTotalPriceForRepeatedProducts() { 17 | assertEquals(586.0, customers[lucas]!!.getTotalOrderPrice(), 0.001) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/ii_collections/N20GroupByKtTest.kt: -------------------------------------------------------------------------------- 1 | package ii_collections 2 | 3 | import ii_collections.data.groupedByCities 4 | import ii_collections.data.shop 5 | import org.junit.jupiter.api.Assertions.assertEquals 6 | import org.junit.jupiter.api.Test 7 | 8 | class N20GroupByKtTest { 9 | @Test 10 | fun testGroupCustomersByCity() { 11 | assertEquals(groupedByCities, shop.groupCustomersByCity()) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/ii_collections/N21PartitionKtTest.kt: -------------------------------------------------------------------------------- 1 | package ii_collections 2 | 3 | import ii_collections.data.customers 4 | import ii_collections.data.reka 5 | import ii_collections.data.shop 6 | import org.junit.jupiter.api.Assertions.assertEquals 7 | import org.junit.jupiter.api.Test 8 | 9 | class N21PartitionKtTest { 10 | @Test 11 | fun testGetCustomersWhoHaveMoreUndeliveredOrdersThanDelivered() { 12 | assertEquals(setOf(customers[reka]), shop.getCustomersWithMoreUndeliveredOrdersThanDelivered()) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/ii_collections/N22FoldKtTest.kt: -------------------------------------------------------------------------------- 1 | package ii_collections 2 | 3 | import ii_collections.data.* 4 | import org.junit.jupiter.api.Assertions.assertEquals 5 | import org.junit.jupiter.api.Test 6 | 7 | 8 | class N22FoldKtTest { 9 | @Test 10 | fun testGetProductsOrderedByAllCustomers() { 11 | val testShop = shop("test shop for 'fold'", 12 | customer(lucas, Canberra, 13 | order(idea), 14 | order(webStorm) 15 | ), 16 | customer(reka, Budapest, 17 | order(idea), 18 | order(youTrack) 19 | ) 20 | ) 21 | assertEquals(setOf(idea), testShop.getSetOfProductsOrderedByEachCustomer()) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/ii_collections/N23CompoundTasksKtTest.kt: -------------------------------------------------------------------------------- 1 | package ii_collections 2 | 3 | import ii_collections.data.* 4 | import org.junit.jupiter.api.Assertions.assertEquals 5 | import org.junit.jupiter.api.Test 6 | 7 | class N23CompoundTasksKtTest { 8 | @Test 9 | fun testGetCustomersWhoOrderedProduct() { 10 | assertEquals(setOf(customers[reka], customers[asuka]), shop.getCustomersWhoOrderedProduct(idea)) 11 | } 12 | 13 | @Test 14 | fun testMostExpensiveDeliveredProduct() { 15 | val testShop = shop("test shop for 'most expensive delivered product'", 16 | customer(lucas, Canberra, 17 | order(idea, isDelivered = false), 18 | order(reSharper) 19 | ) 20 | ) 21 | assertEquals(reSharper, testShop.customers[0].getMostExpensiveDeliveredProduct()) 22 | } 23 | 24 | @Test 25 | fun testNumberOfTimesEachProductWasOrdered() { 26 | assertEquals(4, shop.getNumberOfTimesProductWasOrdered(idea)) 27 | } 28 | 29 | @Test 30 | fun testNumberOfTimesEachProductWasOrderedForRepeatedProduct() { 31 | assertEquals(3, shop.getNumberOfTimesProductWasOrdered(reSharper), "A customer may order a product for several times") 32 | } 33 | 34 | @Test 35 | fun testNumberOfTimesEachProductWasOrderedForRepeatedInOrderProduct() { 36 | assertEquals(3, shop.getNumberOfTimesProductWasOrdered(phpStorm), "An order may contain a particular product more than once") 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test/ii_collections/N24ExtensionsOnCollectionsKtTest.kt: -------------------------------------------------------------------------------- 1 | package ii_collections 2 | 3 | import org.junit.jupiter.api.Assertions.assertEquals 4 | import org.junit.jupiter.api.Test 5 | 6 | class N24ExtensionsOnCollectionsKtTest { 7 | @Test 8 | fun testCollectionOfOneElement() { 9 | doTest(listOf("a"), listOf("a")) 10 | } 11 | 12 | @Test 13 | fun testEmptyCollection() { 14 | doTest(null, listOf()) 15 | } 16 | 17 | @Test 18 | fun testSimpleCollection() { 19 | doTest(listOf("a", "c"), listOf("a", "bb", "c")) 20 | } 21 | 22 | @Test 23 | fun testCollectionWithEmptyStrings() { 24 | doTest(listOf("", "", "", ""), listOf("", "", "", "", "a", "bb", "ccc", "dddd")) 25 | } 26 | 27 | @Test 28 | fun testCollectionWithTwoGroupsOfMaximalSize() { 29 | doTest(listOf("a", "c"), listOf("a", "bb", "c", "dd")) 30 | } 31 | 32 | private fun doTest(expected: Collection?, argument: Collection) { 33 | assertEquals(expected, doSomethingStrangeWithCollection(argument), "The function 'doSomethingStrangeWithCollection' should do at least something with a collection:") 34 | } 35 | } -------------------------------------------------------------------------------- /test/ii_collections/TestShop.kt: -------------------------------------------------------------------------------- 1 | package ii_collections.data 2 | 3 | import ii_collections.* 4 | 5 | //products 6 | val idea = Product("IntelliJ IDEA Ultimate", 199.0) 7 | val reSharper = Product("ReSharper", 149.0) 8 | val dotTrace = Product("DotTrace", 159.0) 9 | val dotMemory = Product("DotMemory", 129.0) 10 | val dotCover = Product("DotCover", 99.0) 11 | val appCode = Product("AppCode", 99.0) 12 | val phpStorm = Product("PhpStorm", 99.0) 13 | val pyCharm = Product("PyCharm", 99.0) 14 | val rubyMine = Product("RubyMine", 99.0) 15 | val webStorm = Product("WebStorm", 49.0) 16 | val teamCity = Product("TeamCity", 299.0) 17 | val youTrack = Product("YouTrack", 500.0) 18 | 19 | //customers 20 | val lucas = "Lucas" 21 | val cooper = "Cooper" 22 | val nathan = "Nathan" 23 | val reka = "Reka" 24 | val bajram = "Bajram" 25 | val asuka = "Asuka" 26 | val riku = "Riku" 27 | 28 | //cities 29 | val Canberra = City("Canberra") 30 | val Vancouver = City("Vancouver") 31 | val Budapest = City("Budapest") 32 | val Ankara = City("Ankara") 33 | val Tokyo = City("Tokyo") 34 | 35 | fun customer(name: String, city: City, vararg orders: Order) = Customer(name, city, orders.toList()) 36 | fun order(vararg products: Product, isDelivered: Boolean = true) = Order(products.toList(), isDelivered) 37 | fun shop(name: String, vararg customers: Customer) = Shop(name, customers.toList()) 38 | 39 | val shop = shop("jb test shop", 40 | customer(lucas, Canberra, 41 | order(reSharper), 42 | order(reSharper, dotMemory, dotTrace) 43 | ), 44 | customer(cooper, Canberra), 45 | customer(nathan, Vancouver, 46 | order(rubyMine, webStorm) 47 | ), 48 | customer(reka, Budapest, 49 | order(idea, isDelivered = false), 50 | order(idea, isDelivered = false), 51 | order(idea) 52 | ), 53 | customer(bajram, Ankara, 54 | order(reSharper) 55 | ), 56 | customer(asuka, Tokyo, 57 | order(idea) 58 | ), 59 | customer(riku, Tokyo, 60 | order(phpStorm, phpStorm), 61 | order(phpStorm) 62 | ) 63 | 64 | ) 65 | 66 | val customers: Map = shop.customers.fold(hashMapOf(), { map, customer -> 67 | map[customer.name] = customer 68 | map 69 | }) 70 | 71 | val orderedProducts = setOf(idea, reSharper, dotTrace, dotMemory, rubyMine, webStorm, phpStorm) 72 | 73 | val sortedCustomers = listOf(cooper, nathan, bajram, asuka, lucas, riku, reka).map { customers[it] } 74 | 75 | val groupedByCities = mapOf( 76 | Canberra to listOf(lucas, cooper), 77 | Vancouver to listOf(nathan), 78 | Budapest to listOf(reka), 79 | Ankara to listOf(bajram), 80 | Tokyo to listOf(asuka, riku) 81 | ).mapValues { it.value.map { name -> customers[name] } } 82 | -------------------------------------------------------------------------------- /test/iii_conventions/DateUtil.kt: -------------------------------------------------------------------------------- 1 | package iii_conventions.test 2 | 3 | import iii_conventions.MyDate 4 | 5 | val MyDate.s: String get() = "($year-$month-$dayOfMonth)" -------------------------------------------------------------------------------- /test/iii_conventions/N25ComparisonKtTest.kt: -------------------------------------------------------------------------------- 1 | package iii_conventions 2 | 3 | import iii_conventions.test.s 4 | import org.junit.jupiter.api.Assertions.assertTrue 5 | import org.junit.jupiter.api.Test 6 | 7 | class N25ComparisonKtTest { 8 | /* Month numbering starts with 0 (0-Jan, 1-Feb, ... 11-Dec) */ 9 | @Test 10 | fun testDateComparison() { 11 | assertTrue(task25(MyDate(2014, 1, 1), MyDate(2014, 1, 2))) 12 | } 13 | 14 | @Test 15 | fun testBefore() { 16 | val first = MyDate(2014, 5, 10) 17 | val second = MyDate(2014, 7, 11) 18 | assertTrue(first < second, "The date ${first.s} should be before ${second.s}") 19 | } 20 | 21 | @Test 22 | fun testAfter() { 23 | val first = MyDate(2014, 10, 20) 24 | val second = MyDate(2014, 7, 11) 25 | assertTrue(first > second, "The date ${first.s} should be after ${second.s}") 26 | } 27 | 28 | /* If you declare 'compareTo' as an extension function, remove this one to make the code compile */ 29 | operator fun MyDate.compareTo(other: MyDate): Int = todoTask25() 30 | } -------------------------------------------------------------------------------- /test/iii_conventions/N26InRangeKtTest.kt: -------------------------------------------------------------------------------- 1 | package iii_conventions 2 | 3 | import iii_conventions.test.s 4 | import org.junit.jupiter.api.Assertions.assertEquals 5 | import org.junit.jupiter.api.Test 6 | 7 | class N26InRangeKtTest { 8 | fun doTest(date: MyDate, first: MyDate, last: MyDate, shouldBeInRange: Boolean) { 9 | val message = "The date ${date.s} should${if (shouldBeInRange) "" else "n't"} be in range: ${first.s}..${last.s}" 10 | assertEquals(shouldBeInRange, checkInRange(date, first, last), message) 11 | } 12 | 13 | /* Month numbering starts with 0 (0-Jan, 1-Feb, ... 11-Dec) */ 14 | @Test 15 | fun testInRange() { 16 | doTest(MyDate(2014, 3, 22), MyDate(2014, 1, 1), MyDate(2015, 1, 1), shouldBeInRange = true) 17 | } 18 | 19 | @Test 20 | fun testBefore() { 21 | doTest(MyDate(2013, 3, 22), MyDate(2014, 1, 1), MyDate(2015, 1, 1), shouldBeInRange = false) 22 | } 23 | 24 | @Test 25 | fun testAfter() { 26 | doTest(MyDate(2015, 3, 22), MyDate(2014, 1, 1), MyDate(2015, 1, 1), shouldBeInRange = false) 27 | } 28 | 29 | @Test 30 | fun testEqualsToBegin() { 31 | doTest(MyDate(2014, 3, 22), MyDate(2014, 3, 22), MyDate(2015, 1, 1), shouldBeInRange = true) 32 | } 33 | 34 | @Test 35 | fun testEqualsToEnd() { 36 | doTest(MyDate(2015, 1, 1), MyDate(2014, 3, 22), MyDate(2015, 1, 1), shouldBeInRange = true) 37 | } 38 | 39 | @Test 40 | fun testInOneDayRange() { 41 | doTest(MyDate(2015, 1, 1), MyDate(2015, 1, 1), MyDate(2015, 1, 1), shouldBeInRange = true) 42 | } 43 | 44 | @Test 45 | fun testInvalidRange() { 46 | doTest(MyDate(2014, 2, 1), MyDate(2015, 1, 1), MyDate(2014, 1, 1), shouldBeInRange = false) 47 | } 48 | 49 | @Test 50 | fun testInvalidRangeEqualsToBegin() { 51 | doTest(MyDate(2015, 1, 1), MyDate(2015, 1, 1), MyDate(2014, 1, 1), shouldBeInRange = false) 52 | } 53 | 54 | @Test 55 | fun testInvalidRangeEqualsToEnd() { 56 | doTest(MyDate(2014, 1, 1), MyDate(2015, 1, 1), MyDate(2014, 1, 1), shouldBeInRange = false) 57 | } 58 | } -------------------------------------------------------------------------------- /test/iii_conventions/N27RangeToKtTest.kt: -------------------------------------------------------------------------------- 1 | package iii_conventions 2 | 3 | import iii_conventions.test.s 4 | import org.junit.jupiter.api.Assertions.assertEquals 5 | import org.junit.jupiter.api.Test 6 | 7 | class N27RangeToKtTest { 8 | fun doTest(date: MyDate, first: MyDate, last: MyDate, shouldBeInRange: Boolean) { 9 | val message = "The date ${date.s} should${if (shouldBeInRange) "" else "n't"} be in range: ${first.s}..${last.s}" 10 | assertEquals(shouldBeInRange, checkInRange2(date, first, last), message) 11 | } 12 | 13 | /* Month numbering starts with 0 (0-Jan, 1-Feb, ... 11-Dec) */ 14 | @Test 15 | fun testInRange() { 16 | doTest(MyDate(2014, 3, 22), MyDate(2014, 1, 1), MyDate(2015, 1, 1), shouldBeInRange = true) 17 | } 18 | 19 | @Test 20 | fun testBefore() { 21 | doTest(MyDate(2013, 3, 22), MyDate(2014, 1, 1), MyDate(2015, 1, 1), shouldBeInRange = false) 22 | } 23 | 24 | @Test 25 | fun testAfter() { 26 | doTest(MyDate(2015, 3, 22), MyDate(2014, 1, 1), MyDate(2015, 1, 1), shouldBeInRange = false) 27 | } 28 | } -------------------------------------------------------------------------------- /test/iii_conventions/N28ForLoopKtTest.kt: -------------------------------------------------------------------------------- 1 | package iii_conventions 2 | 3 | import org.junit.jupiter.api.Assertions.assertEquals 4 | import org.junit.jupiter.api.Assertions.assertFalse 5 | import org.junit.jupiter.api.Test 6 | import java.util.* 7 | 8 | class N28ForLoopKtTest { 9 | /* Month numbering starts with 0 (0-Jan, 1-Feb, ... 11-Dec) */ 10 | 11 | @Test 12 | fun testIterateOverDateRange() { 13 | val actualDateRange = ArrayList() 14 | iterateOverDateRange(MyDate(2014, 5, 1), MyDate(2014, 5, 5)) { date: MyDate -> 15 | actualDateRange.add(date) 16 | } 17 | val expectedDateRange = arrayListOf( 18 | MyDate(2014, 5, 1), MyDate(2014, 5, 2), MyDate(2014, 5, 3), MyDate(2014, 5, 4), MyDate(2014, 5, 5)) 19 | assertEquals(expectedDateRange, actualDateRange, "Incorrect iteration over five nice spring dates") 20 | } 21 | 22 | @Test 23 | fun testIterateOverEmptyRange() { 24 | var invoked = false 25 | iterateOverDateRange(MyDate(2014, 1, 1), MyDate(2013, 1, 1), { invoked = true }) 26 | assertFalse(invoked, "Handler was invoked on an empty range") 27 | } 28 | 29 | @Test 30 | fun testIterateOverLeapYearEndOfFebruary() { 31 | val actualDateRange = ArrayList() 32 | iterateOverDateRange(MyDate(2016, 1, 26), MyDate(2016, 2, 1)) { date: MyDate -> 33 | actualDateRange.add(date) 34 | } 35 | val expectedDateRange = arrayListOf( 36 | MyDate(2016, 1, 26), MyDate(2016, 1, 27), MyDate(2016, 1, 28), MyDate(2016, 1, 29), MyDate(2016, 2, 1)) 37 | assertEquals(expectedDateRange, actualDateRange, "Incorrect iteration over nice end of February of Leap-Year") 38 | } 39 | 40 | @Test 41 | fun testIterateOverTheNewYear() { 42 | val actualDateRange = ArrayList() 43 | iterateOverDateRange(MyDate(2016, 11, 31), MyDate(2017, 0, 1)) { date: MyDate -> 44 | actualDateRange.add(date) 45 | } 46 | val expectedDateRange = arrayListOf( 47 | MyDate(2016, 11, 31), MyDate(2017, 0, 1)) 48 | assertEquals(expectedDateRange, actualDateRange, "Incorrect iteration over the end of the year") 49 | } 50 | } -------------------------------------------------------------------------------- /test/iii_conventions/N29OperatorsOverloadingKtTest.kt: -------------------------------------------------------------------------------- 1 | package iii_conventions 2 | 3 | import iii_conventions.TimeInterval.* 4 | import org.junit.jupiter.api.Assertions.assertEquals 5 | import org.junit.jupiter.api.Test 6 | 7 | class N29OperatorsOverloadingKtTest { 8 | /* Month numbering starts with 0 (0-Jan, 1-Feb, ... 11-Dec) */ 9 | @Test 10 | fun testAddTimeIntervals() { 11 | assertEquals(MyDate(2014, 5, 22), MyDate(1983, 5, 22).addTimeIntervals(YEAR, 31)) 12 | assertEquals(MyDate(1983, 5, 29), MyDate(1983, 5, 22).addTimeIntervals(DAY, 7)) 13 | assertEquals(MyDate(1983, 5, 29), MyDate(1983, 5, 22).addTimeIntervals(WEEK, 1)) 14 | } 15 | 16 | @Test 17 | fun testAddOneTimeInterval() { 18 | assertEquals(MyDate(2015, 5, 8), task29_1(MyDate(2014, 5, 1))) 19 | } 20 | 21 | @Test 22 | fun testOneMonth() { 23 | assertEquals(MyDate(2016, 0, 27), task29_2(MyDate(2014, 0, 1))) 24 | } 25 | 26 | @Test 27 | fun testMonthChange() { 28 | assertEquals(MyDate(2016, 1, 20), task29_2(MyDate(2014, 0, 25))) 29 | } 30 | } -------------------------------------------------------------------------------- /test/iii_conventions/N30DestructuringDeclarationsKtTest.kt: -------------------------------------------------------------------------------- 1 | package iii_conventions.multiAssignemnt 2 | 3 | import org.junit.jupiter.api.Assertions.assertFalse 4 | import org.junit.jupiter.api.Assertions.assertTrue 5 | import org.junit.jupiter.api.Test 6 | 7 | class N30DestructuringDeclarationsKtTest { 8 | @Test 9 | fun testIsLeapDay() { 10 | assertTrue(isLeapDay(MyDate(2016, 1, 29))) 11 | assertFalse(isLeapDay(MyDate(2015, 1, 29))) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/iii_conventions/N31InvokeKtTest.kt: -------------------------------------------------------------------------------- 1 | package iii_conventions 2 | 3 | import org.junit.jupiter.api.Assertions.assertEquals 4 | import org.junit.jupiter.api.Test 5 | 6 | class N31InvokeKtTest { 7 | @Test 8 | fun testTask17() { 9 | assertEquals(4, task31(Invokable())) 10 | } 11 | 12 | @Test 13 | fun testNumberOfInvocations() { 14 | val message = "The number of invocations is incorrect" 15 | fun testInvokable(numberOfInvocations: Int, invokeSeveralTimes: (Invokable) -> Invokable) { 16 | val invokable = Invokable() 17 | assertEquals(numberOfInvocations, invokeSeveralTimes(invokable).getNumberOfInvocations(), message) 18 | } 19 | 20 | testInvokable(1) { it() } 21 | testInvokable(5) { it()()()()() } 22 | testInvokable(0) { it } 23 | } 24 | 25 | operator fun Invokable.invoke(): Nothing = todoTask31() 26 | fun Invokable.getNumberOfInvocations(): Nothing = todoTask31() 27 | } 28 | -------------------------------------------------------------------------------- /test/iv_properties/N32PropertiesKtTest.kt: -------------------------------------------------------------------------------- 1 | package iv_properties 2 | 3 | import org.junit.jupiter.api.Assertions.assertEquals 4 | import org.junit.jupiter.api.Test 5 | 6 | class N32PropertiesKtTest { 7 | @Test 8 | fun testPropertyWithCounter() { 9 | val q = PropertyExample() 10 | q.propertyWithCounter = 14 11 | q.propertyWithCounter = 21 12 | q.propertyWithCounter = 32 13 | assertEquals(3, q.counter, "The property q.counter should contain the number of assignments to q.propertyWithCounter:") 14 | // Here we have to use !! due to false smart cast impossible 15 | assertEquals(32, q.propertyWithCounter!!, "The property q.propertyWithCounter should be set:") 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /test/iv_properties/N33LazyPropertyKtTest.kt: -------------------------------------------------------------------------------- 1 | package iv_properties 2 | 3 | import org.junit.jupiter.api.Assertions.* 4 | import org.junit.jupiter.api.Test 5 | 6 | class N33LazyPropertyKtTest { 7 | @Test 8 | fun testLazy() { 9 | var initialized = false 10 | val lazyProperty = LazyProperty { initialized = true; 42 } 11 | assertFalse(initialized, "Property shouldn't be initialized before access") 12 | val result: Int = lazyProperty.lazy 13 | assertTrue(initialized, "Property should be initialized after access") 14 | assertEquals(42, result) 15 | } 16 | 17 | @Test 18 | fun initializedOnce() { 19 | var initialized = 0 20 | val lazyProperty = LazyProperty({ initialized++; 42 }) 21 | lazyProperty.lazy 22 | lazyProperty.lazy 23 | assertEquals(1, initialized, "Lazy property should be initialized once") 24 | 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/iv_properties/N34DelegatesExamplesKtTest.kt: -------------------------------------------------------------------------------- 1 | package iv_properties 2 | 3 | import org.junit.jupiter.api.Assertions.* 4 | import org.junit.jupiter.api.Test 5 | 6 | class N34DelegatesExamplesKtTest { 7 | @Test 8 | fun testLazy() { 9 | var initialized = false 10 | val lazyProperty = LazyPropertyUsingDelegates { initialized = true; 42 } 11 | assertFalse(initialized, "Property shouldn't be initialized before access") 12 | val result: Int = lazyProperty.lazyValue 13 | assertTrue(initialized, "Property should be initialized after access") 14 | assertEquals(42, result) 15 | } 16 | 17 | @Test 18 | fun initializedOnce() { 19 | var initialized = 0 20 | val lazyProperty = LazyPropertyUsingDelegates { initialized++; 42 } 21 | lazyProperty.lazyValue 22 | lazyProperty.lazyValue 23 | assertEquals(1, initialized, "Lazy property should be initialized once") 24 | 25 | } 26 | } -------------------------------------------------------------------------------- /test/iv_properties/N35HowDelegatesWorkKtTest.kt: -------------------------------------------------------------------------------- 1 | package iv_properties 2 | 3 | import iii_conventions.MyDate 4 | import org.junit.jupiter.api.Assertions.assertEquals 5 | import org.junit.jupiter.api.Test 6 | 7 | class N35HowDelegatesWorkKtTest { 8 | @Test 9 | fun testDate() { 10 | val d = D() 11 | /* Month numbering starts with 0 (0-Jan, 1-Feb, ... 11-Dec) */ 12 | d.date = MyDate(2014, 1, 13) 13 | assertEquals(2014, d.date.year) 14 | assertEquals(1, d.date.month) 15 | assertEquals(13, d.date.dayOfMonth) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test/util/AdditionalTest.kt: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import i_introduction._1_Java_To_Kotlin_Converter.N01JavaToKotlinConverterKtTest 4 | import i_introduction._3_Default_Arguments.N03DefaultArgumentsKtTest 5 | import i_introduction._4_Lambdas.N04LambdasKtTest 6 | import i_introduction._7_Nullable_Types.N07NullableTypesKtTest 7 | import i_introduction._8_Smart_Casts.N08SmartCastsKtTest 8 | import ii_collections.N24ExtensionsOnCollectionsKtTest 9 | import org.junit.jupiter.api.Assertions.assertTrue 10 | import org.junit.jupiter.api.Test 11 | 12 | class AdditionalTest { 13 | @Test 14 | fun test() { 15 | invokeTests( 16 | { N01JavaToKotlinConverterKtTest().collection() }, 17 | { N03DefaultArgumentsKtTest().testDefaultAndNamedParams() }, 18 | { N04LambdasKtTest().contains() }, 19 | { N07NullableTypesKtTest().everythingIsOk() }, 20 | { N08SmartCastsKtTest().testNum() }, 21 | { N24ExtensionsOnCollectionsKtTest().testCollectionOfOneElement() } 22 | ) 23 | assertTrue(JavaCode.set.isEmpty(), "${JavaCode.set}") 24 | } 25 | 26 | private fun invokeTests(vararg tests: () -> Unit) { 27 | for (test in tests) { 28 | try { 29 | test() 30 | } catch (e: NotImplementedException) { 31 | } 32 | } 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /test/v_builders/N36ExtensionFunctionLiteralsKtTest.kt: -------------------------------------------------------------------------------- 1 | package v_builders 2 | 3 | import org.junit.jupiter.api.Assertions.assertEquals 4 | import org.junit.jupiter.api.Test 5 | 6 | class N36ExtensionFunctionLiteralsKtTest { 7 | @Test 8 | fun testIsOddAndIsEven() { 9 | val result = task36() 10 | assertEquals(listOf(false, true, true), result, "The functions 'isOdd' and 'isEven' should be implemented correctly") 11 | 12 | } 13 | } -------------------------------------------------------------------------------- /test/v_builders/N37StringAndMapBuildersKtTest.kt: -------------------------------------------------------------------------------- 1 | package v_builders 2 | 3 | import org.junit.jupiter.api.Assertions.assertEquals 4 | import org.junit.jupiter.api.Test 5 | import java.util.* 6 | 7 | class N37StringAndMapBuildersKtTest { 8 | @Test 9 | fun testBuildMap() { 10 | val map = task37() 11 | val expected = HashMap() 12 | for (i in 0..10) { 13 | expected[i] = "$i" 14 | } 15 | assertEquals(expected, map, "Map should be filled with the right values") 16 | } 17 | } -------------------------------------------------------------------------------- /test/v_builders/N38TheFunctionApplyKtTest.kt: -------------------------------------------------------------------------------- 1 | package v_builders.examples 2 | 3 | import org.junit.jupiter.api.Assertions.assertEquals 4 | import org.junit.jupiter.api.Test 5 | import java.util.* 6 | 7 | class N38TheFunctionApplyKtTest { 8 | @Test 9 | fun testBuildString() { 10 | val expected = StringBuilder().apply { 11 | append("Numbers: ") 12 | for (i in 1..10) { 13 | append(i) 14 | } 15 | }.toString() 16 | val actual = buildString() 17 | assertEquals(expected, actual, "String should be built:") 18 | } 19 | 20 | @Test 21 | fun testBuildMap() { 22 | val expected = HashMap().apply { 23 | put(0, "0") 24 | for (i in 1..10) { 25 | put(i, "$i") 26 | } 27 | } 28 | val actual = buildMap() 29 | assertEquals(expected, actual, "Map should be filled with the right values") 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/v_builders/N39HtmlBuildersKtTest.kt: -------------------------------------------------------------------------------- 1 | package v_builders 2 | 3 | import org.junit.jupiter.api.Assertions.assertTrue 4 | import org.junit.jupiter.api.Test 5 | 6 | class N39HtmlBuildersKtTest { 7 | @Test 8 | fun productTableIsFilled() { 9 | val result = renderProductTable() 10 | assertTrue(result.contains("cactus"), "Product table should contain corresponding data") 11 | } 12 | 13 | @Test 14 | fun productTableIsColored() { 15 | val result = renderProductTable() 16 | assertTrue(result.contains("bgcolor"), "Product table should be colored") 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/v_builders/N40BuildersHowItWorksKtTest.kt: -------------------------------------------------------------------------------- 1 | package v_builders 2 | 3 | import org.junit.jupiter.api.Test 4 | import org.junit.jupiter.api.fail 5 | import util.questions.Answer.b 6 | import util.questions.Answer.c 7 | import v_builders.builders.task40 8 | 9 | class N40BuildersHowItWorksKtTest { 10 | @Test 11 | fun testBuildersQuiz() { 12 | val answers = task40() 13 | if (answers.values.toSet() == setOf(null)) { 14 | fail("Please specify your answers!") 15 | } 16 | val correctAnswers = mapOf(22 - 20 to b, 1 + 3 to c, 11 - 8 to b, 79 - 78 to c) 17 | if (correctAnswers != answers) { 18 | val incorrect = (1..4).filter { answers[it] != correctAnswers[it] } 19 | fail("Your answers are incorrect! $incorrect") 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /test/vi_generics/N41GenericFunctionsKtTest.kt: -------------------------------------------------------------------------------- 1 | package vi_generics 2 | 3 | import org.junit.jupiter.api.Assertions.assertEquals 4 | import org.junit.jupiter.api.Test 5 | 6 | class N41GenericFunctionsKtTest { 7 | @Test 8 | fun testPartitionWordsAndLines() { 9 | val (words, lines) = listOf("a", "a b", "c", "d e").partitionWordsAndLines() 10 | assertEquals(listOf("a", "c"), words) 11 | assertEquals(listOf("a b", "d e"), lines) 12 | } 13 | 14 | @Test 15 | fun testPartitionLettersAndOtherSymbols() { 16 | val (letters, other) = setOf('a', '%', 'r', '}').partitionLettersAndOtherSymbols() 17 | assertEquals(setOf('a', 'r'), letters) 18 | assertEquals(setOf('%', '}'), other) 19 | } 20 | } --------------------------------------------------------------------------------