├── .gitignore ├── .idea ├── gradle.xml ├── misc.xml ├── modules.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── smarttoolfactory │ │ └── kotlintutorials │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── values-night │ │ └── themes.xml │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ └── test │ └── java │ └── com │ └── smarttoolfactory │ └── kotlintutorials │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── local.properties ├── settings.gradle └── tutorial ├── .gitignore ├── build.gradle └── src └── main └── java └── com └── smarttoolfactory └── tutorial ├── Animal.java ├── EqualityAndHashCode.java ├── MySingleton.kt ├── RegularExpressionTest.java ├── TestStringConcatenation.java ├── chapter1Basics ├── Tutorial1_1Basics.kt ├── Tutorial1_2Arrays.kt ├── Tutorial1_3Collections1List.kt ├── Tutorial1_3Collections2Set.kt ├── Tutorial1_3Collections3Map.kt ├── Tutorial1_4Ranges.kt ├── Tutorial1_5ControlFlow.kt ├── Tutorial1_6Function1.kt └── Tutorial1_6Function2.kt ├── chapter2OOP ├── Tutorial2_10Generics.kt ├── Tutorial2_11Enums.kt ├── Tutorial2_13ObjectExpressions.kt ├── Tutorial2_14Delegation.kt ├── Tutorial2_14Delegation2.kt ├── Tutorial2_14Delegation3StandartDelegation.kt ├── Tutorial2_14DelegationComposeRememberMutableState.kt ├── Tutorial2_15Singleton.kt ├── Tutorial2_1Constructors.kt ├── Tutorial2_2NestedClasses.kt ├── Tutorial2_3OtherClasses.kt ├── Tutorial2_4Inheritance.kt ├── Tutorial2_4Inheritance2.kt ├── Tutorial2_5Properties.kt ├── Tutorial2_6Interfaces.kt ├── Tutorial2_7VisibilityModifiers.kt ├── Tutorial2_8Extensions.kt ├── Tutorial2_9DataClasses.kt ├── Tutorial2_9DataClasses2Inheritance.kt ├── model │ ├── ConstructorModels.kt │ ├── InheritanceModels.kt │ └── InheritanceModels2.kt └── mvp │ ├── BaseComponents.kt │ ├── BaseContract.kt │ ├── UserContract.kt │ ├── UserFragmentMain.kt │ └── user │ └── UserFragment.kt ├── chapter3Other ├── Tutorial3_1Destructuring.kt ├── Tutorial3_2TypeChecks.kt ├── Tutorial3_3Equality.kt ├── Tutorial3_4NullSafety.kt ├── Tutorial3_5Exceptions.kt ├── Tutorial3_6Annotation.kt ├── Tutorial3_7Reflection.kt ├── Tutorial3_8FunctionLiteralsWithReceiver.kt └── Tutorial3_8ScopeFunctions.kt ├── chapter4Functions ├── Tutorial4_1HigOrderFunctions.kt ├── Tutorial4_2FunctionTypes.kt ├── Tutorial4_3LambdaExpressions.kt └── Tutorial4_4InlineFunctions.kt ├── chapter5Coroutines ├── Tutorial5_1Basics.kt ├── Tutorial5_2CancellationAndTimeouts.kt ├── Tutorial5_3ComposingSuspendingFunctions.kt ├── Tutorial5_4CoroutineContextAndDispatchers.kt ├── Tutorial5_5Flow1.kt ├── Tutorial5_5Flow2.kt ├── Tutorial5_5Flow3.kt └── Tutorial5_7ExceptionHandling.kt ├── chapter6Advanced ├── DSLWithLambda.kt ├── DSLWithLambda2.kt ├── DelegatedProperty.kt ├── GenericJava.java ├── Generics.kt ├── Generics2PECS.kt ├── Generics3Methods.kt ├── HashCodeEqualsHashMap.kt ├── LambdaWithReceiver.kt ├── OperatorOverloading.kt ├── ReferencesAndObjects.kt ├── RegularExpressions.kt └── Reified.kt ├── chapter7Threading ├── ReentrantLock.kt ├── Tutorial7_1Thread.kt ├── Tutorial7_2Synchronized.kt ├── Tutorial7_3SynchronizedBlock.kt └── Tutorial7_4CountdownLatch.kt └── chapter8fileIO └── Test.java /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.len 3 | .idea/* 4 | */.idea/* 5 | /build/ 6 | /.gradle/vcs-1/ 7 | /.gradle/ -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tutorials/Playground for learning Kotlin and Coroutines 2 | 3 | This is a series tutorials or playground for learning Kotlin and Coroutines based on official 4 | Kotlin documents which contains more samples and examples than original documentation has -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'kotlin-android' 4 | } 5 | 6 | android { 7 | compileSdkVersion 35 8 | 9 | defaultConfig { 10 | applicationId "com.smarttoolfactory.kotlintutorials" 11 | minSdkVersion 21 12 | targetSdkVersion 35 13 | versionCode 1 14 | versionName "1.0" 15 | 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | compileOptions { 26 | sourceCompatibility = JavaVersion.VERSION_17 27 | targetCompatibility = JavaVersion.VERSION_17 28 | } 29 | kotlinOptions { 30 | jvmTarget = '17' 31 | } 32 | namespace 'com.smarttoolfactory.kotlintutorials' 33 | } 34 | 35 | dependencies { 36 | 37 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 38 | implementation 'androidx.core:core-ktx:1.16.0' 39 | implementation 'androidx.appcompat:appcompat:1.7.0' 40 | implementation 'com.google.android.material:material:1.12.0' 41 | testImplementation 'junit:junit:4.13.2' 42 | androidTestImplementation 'androidx.test.ext:junit:1.2.1' 43 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' 44 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/com/smarttoolfactory/kotlintutorials/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.kotlintutorials 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.smarttoolfactory.kotlintutorials", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartToolFactory/Kotlin-Tutorials/e6d413609d7903a7e51f66edc48d19f084685823/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartToolFactory/Kotlin-Tutorials/e6d413609d7903a7e51f66edc48d19f084685823/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartToolFactory/Kotlin-Tutorials/e6d413609d7903a7e51f66edc48d19f084685823/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartToolFactory/Kotlin-Tutorials/e6d413609d7903a7e51f66edc48d19f084685823/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartToolFactory/Kotlin-Tutorials/e6d413609d7903a7e51f66edc48d19f084685823/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartToolFactory/Kotlin-Tutorials/e6d413609d7903a7e51f66edc48d19f084685823/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartToolFactory/Kotlin-Tutorials/e6d413609d7903a7e51f66edc48d19f084685823/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartToolFactory/Kotlin-Tutorials/e6d413609d7903a7e51f66edc48d19f084685823/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartToolFactory/Kotlin-Tutorials/e6d413609d7903a7e51f66edc48d19f084685823/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartToolFactory/Kotlin-Tutorials/e6d413609d7903a7e51f66edc48d19f084685823/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Kotlin Tutorials 3 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /app/src/test/java/com/smarttoolfactory/kotlintutorials/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.kotlintutorials 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | ext.kotlin_version = "2.1.0" 4 | repositories { 5 | google() 6 | mavenCentral() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:8.9.1' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | 12 | // NOTE: Do not place your application dependencies here; they belong 13 | // in the individual module build.gradle files 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | google() 20 | mavenCentral() 21 | // Warning: this repository is going to shut down soon 22 | } 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | android.defaults.buildfeatures.buildconfig=true 21 | android.nonTransitiveRClass=false 22 | android.nonFinalResIds=false -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartToolFactory/Kotlin-Tutorials/e6d413609d7903a7e51f66edc48d19f084685823/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /local.properties: -------------------------------------------------------------------------------- 1 | ## This file must *NOT* be checked into Version Control Systems, 2 | # as it contains information specific to your local configuration. 3 | # 4 | # Location of the SDK. This is only used by Gradle. 5 | # For customization when using a Version Control System, please read the 6 | # header note. 7 | #Sat Mar 25 15:51:30 TRT 2023 8 | sdk.dir=/Users/sunflowar/Library/Android/sdk 9 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = "Kotlin Tutorials" 2 | include ':app' 3 | include ':tutorial' 4 | -------------------------------------------------------------------------------- /tutorial/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /tutorial/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | id 'kotlin' 4 | } 5 | 6 | java { 7 | sourceCompatibility JavaVersion.VERSION_21 8 | targetCompatibility JavaVersion.VERSION_21 9 | } 10 | 11 | dependencies { 12 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 13 | 14 | implementation 'io.reactivex.rxjava2:rxjava:2.2.19' 15 | implementation("io.reactivex.rxjava2:rxkotlin:2.2.0") 16 | 17 | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2") 18 | 19 | // Reflection 20 | implementation("org.jetbrains.kotlin:kotlin-reflect:2.1.0") 21 | 22 | // (Required) Writing and executing Unit Tests on the JUnit Platform 23 | testImplementation "org.junit.jupiter:junit-jupiter-api:5.10.1" 24 | testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.10.1" 25 | 26 | testImplementation 'junit:junit:4.13.2' 27 | 28 | // (Optional) If you need "Parameterized Tests" 29 | testImplementation "org.junit.jupiter:junit-jupiter-params:5.10.1" 30 | 31 | // (Optional) If you also have JUnit 4-based tests 32 | testImplementation "junit:junit:4.13.2" 33 | testRuntimeOnly "org.junit.vintage:junit-vintage-engine:5.10.1" 34 | 35 | // Test Coroutines 36 | testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.10.2' 37 | 38 | // Truth 39 | testImplementation "com.google.truth:truth:1.4.2" 40 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/Animal.java: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial; /** 2 | * Class for displaying inheritance when a field exists in both super and derived type 3 | * 4 | * If static type of object is super(Animal cat) then field from super type is used to 5 | * fetch and modify. 6 | * 7 | * * When a method is overridden by a derived class then derived class' method is called 8 | */ 9 | public class Animal { 10 | 11 | String name; 12 | 13 | public Animal(String name) { 14 | this.name = name; 15 | } 16 | 17 | public void printName() { 18 | System.out.println("Animal.printName() -> Animal name: " + name); 19 | } 20 | 21 | public String getCatName() { 22 | return name; 23 | } 24 | 25 | public static void main(String[] args) { 26 | 27 | Animal cat = new Cat("Cat"); 28 | 29 | cat.name = "Felix"; 30 | cat.printName(); 31 | 32 | // Cat name is from Animal but overridden method is from Cat dynamic type 33 | System.out.println("CAT NAME: " + cat.name +", CAT.getName(): " + cat.getCatName()); 34 | 35 | /* 36 | Prints: 37 | 38 | Cat name: Cat, Animal name: Felix 39 | CAT NAME: Felix, CAT.getName(): Cat 40 | */ 41 | 42 | } 43 | } 44 | 45 | 46 | class Cat extends Animal { 47 | 48 | // 🔥If type is Animal this is shadowed, name from Animal is set and get when called 49 | String name; 50 | 51 | public Cat(String name) { 52 | super("Felis Domesticus"); 53 | this.name = name; 54 | } 55 | 56 | public String getCatName() { 57 | return name; 58 | } 59 | 60 | public void printName() { 61 | System.out.println("Cat.printName() -> Cat name: " + name + ", Animal(super.name) name: " + super.name); 62 | } 63 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/EqualityAndHashCode.java: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial; 2 | 3 | import java.util.HashMap; 4 | import java.util.Iterator; 5 | import java.util.LinkedHashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | public class EqualityAndHashCode { 10 | 11 | static class Currency { 12 | 13 | String currencyCode; 14 | 15 | Currency(String currencyCode) { 16 | this.currencyCode = currencyCode; 17 | } 18 | 19 | @Override 20 | public boolean equals(Object obj) { 21 | 22 | if (obj == null) return false; 23 | 24 | // if (obj.getClass().equals(getClass())) { 25 | // Currency currency = (Currency) obj; 26 | // return currency.currencyCode.equals(this.currencyCode); 27 | // } 28 | 29 | if (obj instanceof Currency) { 30 | Currency currency = (Currency) obj; 31 | return currency.currencyCode.equals(this.currencyCode); 32 | } 33 | return false; 34 | } 35 | 36 | @Override 37 | public int hashCode() { 38 | return currencyCode.hashCode(); 39 | } 40 | } 41 | 42 | 43 | public static void main(String[] args) { 44 | 45 | Currency currency1 = new Currency("USD"); 46 | 47 | Currency currency2 = new Currency("USD"); 48 | 49 | System.out.println("Currency1: " + currency1 + ", hashCode: " + currency1.hashCode()); 50 | System.out.println("Currency2: " + currency2 + ", hashCode: " + currency2.hashCode()); 51 | 52 | System.out.println("Currency1 equals Currency2: " + currency1.equals(currency2)); 53 | // 🔥 This never returns TRUE even when both equals method and hashcode is overridden to be same 54 | // Only when both objects are same 55 | System.out.println("Currency1 == Currency2: " + (currency1 == currency2)); 56 | 57 | 58 | Map rates = new HashMap<>(); 59 | 60 | rates.put(currency1, 1.1); 61 | 62 | // 🔥🔥🔥Hash Map KEYS should both meet the conditions of EQUALS and same HASH CODE 63 | /* 64 | HashMap uses hashCode(), == and equals() for entry lookup. 65 | The lookup sequence for a given key k is as follows: 66 | 67 | Use k.hashCode() to determine which bucket the entry is stored, if any 68 | If found, for each entry's key k1 in that bucket, if k == k1 || k.equals(k1), then return k1's entry 69 | Any other outcomes, no corresponding entry 70 | */ 71 | Double result = rates.get(currency2); 72 | 73 | System.out.println("Result: " + result); 74 | 75 | 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/MySingleton.kt: -------------------------------------------------------------------------------- 1 | class MySingleton { 2 | 3 | var type = "Singleton" 4 | 5 | companion object { 6 | 7 | private var INSTANCE: MySingleton? = null 8 | val instance: MySingleton? 9 | get() { 10 | if (INSTANCE == null) { 11 | INSTANCE = MySingleton() 12 | } 13 | return INSTANCE 14 | } 15 | } 16 | } 17 | 18 | fun main() { 19 | MySingleton.instance 20 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/RegularExpressionTest.java: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial; 2 | 3 | import java.util.regex.Matcher; 4 | import java.util.regex.Pattern; 5 | 6 | public class RegularExpressionTest { 7 | 8 | /* 9 | Here is the complete list of various character classes constructs: 10 | [abc]: It would match with text if the text is having either one of them(a,b or c) and only once. 11 | [^abc]: Any single character except a, b, or c (^ denote negation) 12 | [a-zA-Z]: a through z, or A through Z, inclusive (range) 13 | [a-d[m-p]]: a through d, or m through p: [a-dm-p] (union) 14 | [a-z&&[def]]: Any one of them (d, e, or f) 15 | [a-z&&[^bc]]: a through z, except for b and c: [ad-z] (subtraction) 16 | [a-z&&[^m-p]]: a through z, and not m through p: [a-lq-z] (subtraction) 17 | 18 | Predefined Character Classes – Metacharacters 19 | These are like short codes which you can use while writing regex. 20 | 21 | Construct Description 22 | . -> Any character (may or may not match line terminators) 23 | \d -> A digit: [0-9] 24 | \D -> A non-digit: [^0-9] 25 | \s -> A whitespace character: [ \t\n\x0B\f\r] 26 | \S -> A non-whitespace character: [^\s] 27 | \w -> A word character: [a-zA-Z_0-9] 28 | \W -> A non-word character: [^\w] 29 | 30 | Boundary Matchers 31 | ^ Matches the beginning of a line. 32 | $ Matches then end of a line. 33 | \b Matches a word boundary. 34 | \B Matches a non-word boundary. 35 | \A Matches the beginning of the input text. 36 | \G Matches the end of the previous match 37 | \Z Matches the end of the input text except the final terminator if any. 38 | \z Matches the end of the input text. 39 | */ 40 | 41 | public void testRegex1() { 42 | 43 | // String str = "123Hello World"; 44 | // 45 | // Pattern pattern = Pattern.compile("[^\\d]"); 46 | // Matcher matcher = pattern.matcher(str); 47 | // 48 | // if (matcher.find()) { 49 | // System.out.println(""); 50 | // } 51 | 52 | String text = "This island is beautiful"; 53 | 54 | Pattern p = Pattern.compile("\\bis\\b"); 55 | 56 | Matcher matcher = p.matcher(text); 57 | 58 | while (matcher.find()) { 59 | System.out.printf("%s at %d", matcher.group(), matcher.start()); 60 | } 61 | 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/TestStringConcatenation.java: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial; 2 | 3 | public class TestStringConcatenation { 4 | 5 | public static void main(String[] args) { 6 | 7 | long startTime = System.nanoTime(); 8 | testStringConcat(); 9 | long totalTime = System.nanoTime() - startTime; 10 | 11 | System.out.println("Total time for String concat: " + totalTime); 12 | 13 | startTime = System.nanoTime(); 14 | testStringBuilder(); 15 | totalTime = System.nanoTime() - startTime; 16 | System.out.println("Total time for StringBuilder: " + totalTime); 17 | 18 | } 19 | 20 | private static String testStringConcat() { 21 | return "ByteCode:ldc pushes a one-word constant onto the operand stack. ldc takes a single parameter, , which is the value to push." 22 | + "ByteCode:ldc pushes a one-word constant onto the operand stack. ldc takes a single parameter, , which is the value to push." 23 | + "ByteCode:ldc pushes a one-word constant onto the operand stack. ldc takes a single parameter, , which is the value to push." 24 | + "ByteCode:ldc pushes a one-word constant onto the operand stack. ldc takes a single parameter, , which is the value to push." 25 | + "ByteCode:ldc pushes a one-word constant onto the operand stack. ldc takes a single parameter, , which is the value to push." 26 | + "Lorem Ipsum "; 27 | 28 | } 29 | 30 | 31 | private static String testStringBuilder() { 32 | 33 | StringBuilder stringBuilder = new StringBuilder(); 34 | 35 | stringBuilder.append("ByteCode:ldc pushes a one-word constant onto the operand stack. ldc takes a single parameter, , which is the value to push."); 36 | stringBuilder.append("ByteCode:ldc pushes a one-word constant onto the operand stack. ldc takes a single parameter, , which is the value to push."); 37 | stringBuilder.append("ByteCode:ldc pushes a one-word constant onto the operand stack. ldc takes a single parameter, , which is the value to push."); 38 | stringBuilder.append("ByteCode:ldc pushes a one-word constant onto the operand stack. ldc takes a single parameter, , which is the value to push."); 39 | stringBuilder.append("ByteCode:ldc pushes a one-word constant onto the operand stack. ldc takes a single parameter, , which is the value to push."); 40 | 41 | stringBuilder.append("Lorem Ipsum"); 42 | 43 | return stringBuilder.toString(); 44 | 45 | } 46 | 47 | 48 | interface Account { 49 | 50 | } 51 | 52 | static class BankAccount implements Account { 53 | 54 | } 55 | 56 | static class InterestAccount implements Account { 57 | 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter1Basics/Tutorial1_1Basics.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter1Basics 2 | 3 | fun main() { 4 | 5 | var a: Int = 1000; 6 | 7 | a += 10 8 | println("a: $a") 9 | 10 | if (true is Boolean) { 11 | print("true is boolean\n") 12 | } 13 | 14 | /* 15 | *** Characters *** 16 | */ 17 | var letterGrade: Char = 'A' 18 | 19 | // instance of -> is 20 | println("BaseClassA is a Char : ${letterGrade is Char} ") 21 | 22 | // Get ASCII code of character 23 | println("BaseClassA ascii: ${letterGrade.toInt()}") 24 | 25 | println("3.14 to int ${3.14.toInt()}") 26 | 27 | /* 28 | *** Strings *** 29 | */ 30 | 31 | println("********** Strings **********") 32 | 33 | 34 | var rawString: String = "I am Raw String!" 35 | val escapedString: String = "I am escaped String!\n" 36 | 37 | // Escaped String has end of line character 38 | println("Hello!$escapedString") 39 | println("Hey!!$rawString") 40 | 41 | 42 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter1Basics/Tutorial1_2Arrays.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter1Basics 2 | 3 | 4 | fun main() { 5 | 6 | // INFO Initializing Arrays Method 1 7 | val numbers: IntArray = intArrayOf(1, 2, 3, 4, 5) 8 | println("Hey!! I am array Example numbers[2]: " + numbers[2]) 9 | 10 | // Creates a new array of the specified size, with all elements initialized to zero. 11 | val myArray: IntArray = IntArray(3) 12 | myArray[0] = 1 13 | myArray[1] = 2 14 | myArray[2] = 3 15 | 16 | // For loop 17 | for (i in myArray) { 18 | println("myArray i: $i") 19 | } 20 | 21 | // Iterator 22 | val iterator = myArray.iterator() 23 | 24 | while (iterator.hasNext()) { 25 | println("Item: ${iterator.nextInt()}") 26 | } 27 | 28 | iterator.forEach { 29 | println("Item: $it") 30 | } 31 | 32 | // Array's foreach operator 33 | myArray.forEach { i: Int -> println("Foreach i: $i") } 34 | 35 | // Set value of item in index2 to 15 36 | myArray.set(2, 15) // myArray[2] = 15 37 | println("MyArray " + myArray.get(2)) 38 | 39 | // Get average of values in array 40 | println("MyArray average: " + myArray.average()) 41 | 42 | // INFO Initializing Arrays Method 2 43 | // 🔥 WARNING This is array with 3 null elements 44 | val stringArray: Array = arrayOfNulls(3) 45 | 46 | stringArray[0] = "Hello World" 47 | stringArray.forEach { s -> println("arrayOfNulls: $s") } 48 | 49 | // INFO Initializing Arrays Method 3 50 | // Creates an array where both items "Empty" with length of 4 51 | val anotherStringArray: Array = Array(4) { "Empty" } 52 | anotherStringArray.forEach { s -> println("Another array: $s") } 53 | 54 | // INFO Initializing Arrays Method 4 55 | val sqrArray: IntArray = IntArray(5) { x -> x * x } 56 | sqrArray.forEach { it -> println("Sqr : $it") } 57 | 58 | 59 | // INFO Other Initialization types 60 | val size = 10 61 | 62 | // Primitive arrays 63 | val arrayOfZeros = IntArray(6) //equivalent in Java: new int[size] 64 | val numbersFromOne = IntArray(6) { it + 1 } 65 | val myInts: IntArray = intArrayOf(1, 1, 2, 3, 5, 8, 13, 21) 66 | 67 | // Alternative 1 for loop 68 | for (i in 0 until myInts.size) { 69 | println("i: ${myInts[i]}") 70 | } 71 | // Alternative 2 for loop 72 | for (element in myInts) { 73 | println("i: $element") 74 | } 75 | 76 | // Non primitive-arrays 77 | val boxedInts: Array = arrayOfNulls(size) // 🔥 equivalent in Java: new Integer[size] 78 | val boxedZeros: Array = Array(size) { 0 } 79 | 80 | val nulls: Array = arrayOfNulls(size) // 🔥 equivalent in Java: new String[size] 81 | val strings: Array = Array(size) { "n = $it" } 82 | val myStrings: Array = arrayOf("foo", "bar", "baz") 83 | 84 | // 👍 This is valid 85 | boxedInts[0] = 12 86 | println("BoxedInt[0]: ${boxedInts[0]}") 87 | 88 | // 👍 This is valid 89 | nulls[0] = "Hey" 90 | println("nulls[0]: ${nulls[0]}") 91 | 92 | // WARNING 🔥 This is an EMPTY ARRAY with 0 length, no value can be assigned to this array. 93 | // Returns ArrayIndexOutOfBoundsException 94 | val emptyStringArray: Array = arrayOf() 95 | // emptyStringArray[0] = "TestStringConcatenation" 96 | 97 | 98 | // 🔥 2D Arrays 99 | val coordinates: Array> = arrayOf( 100 | arrayOf(1, 2), 101 | arrayOf(2, 3), 102 | arrayOf(3, 4), 103 | arrayOf(4, 5), 104 | arrayOf(5, 6), 105 | arrayOf(6, 7) 106 | ) 107 | 108 | val arr = Array>(6) { arrayOf(it) } 109 | arr[0] = arrayOf(1, 2) 110 | arr[1] = arrayOf(2, 3) 111 | arr[2] = arrayOf(3, 4) 112 | arr[3] = arrayOf(4, 5) 113 | arr[4] = arrayOf(5, 6) 114 | arr[5] = arrayOf(6, 7) 115 | 116 | coordinates.forEach { 117 | println("Array in array: $it") 118 | } 119 | 120 | val solution = Solution() 121 | 122 | solution.checkStraightLine(coordinates) 123 | } 124 | 125 | 126 | class Solution { 127 | 128 | fun checkStraightLine(coordinates: Array>): Boolean { 129 | 130 | val y2 = coordinates[1][1].toDouble() 131 | val x2 = coordinates[1][0].toDouble() 132 | val y1 = coordinates[0][1].toDouble() 133 | val x1 = coordinates[0][0].toDouble() 134 | 135 | var m = 0.0 136 | 137 | if (y2 != y1) { 138 | m = ((y2 - y1) / (x2 - x1)) 139 | } 140 | 141 | for (i in 2 until coordinates.size) { 142 | val y = coordinates[i][1] - coordinates[i - 1][1] 143 | val x = coordinates[i][0] - coordinates[i - 1][0] 144 | 145 | if (x == 0) { 146 | return false 147 | } else if (m != (y / x).toDouble()) { 148 | return false 149 | } 150 | } 151 | 152 | return true 153 | } 154 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter1Basics/Tutorial1_3Collections2Set.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter1Basics 2 | 3 | fun main() { 4 | 5 | /* 6 | ***** SETs ***** 7 | */ 8 | 9 | /* 10 | * INFO Instantiation 11 | */ 12 | // Immutable(Read-Only) 13 | val setImmutable1 = setOf(3, 5, 1) // [3, 5, 1] 14 | 15 | // Mutable 16 | val setHash = hashSetOf(3, 5, 1) // [5, 1, 3] 17 | val setLinkedHash = linkedSetOf(3, 5, 1) // [3, 5, 1] 18 | val setTree = sortedSetOf(3, 5, 1) // [1, 3, 5] 19 | val setMutable = mutableSetOf(3, 5, 1) // [3, 5, 1] 20 | 21 | setImmutable1.forEach { it -> println("setImmutable1 Item $it") } 22 | setHash.forEach { it -> println("setHash Item $it") } 23 | setLinkedHash.forEach { it -> println("setLinkedHash Item $it") } 24 | setTree.forEach { it -> println("setTree Item $it") } 25 | 26 | // When we put same object with hash code and equals it's replaced with previous one 27 | // If objects are not treated as same object from hashmap perspective new object is added 28 | // Hashset adds items as keys of inner hashMap as 29 | /* 30 | Object PRESENT = Object() 31 | mySet(person1) -> map.put(person1, PRESENT) 32 | */ 33 | val person1 = Person("jon", 0) 34 | val person2 = Person("jon", 2) 35 | val mySet: HashSet = hashSetOf() 36 | val result1 = mySet.add(person1) 37 | val result2 = mySet.add(person2) 38 | println("Result1: $result1, result2: $result2") 39 | 40 | 41 | 42 | } 43 | 44 | class Person( 45 | val name: String, val id: Int 46 | ) { 47 | // override fun equals(other: Any?): Boolean { 48 | // if (other !is Person) return false 49 | // if (other.name == this.name) return true 50 | // return false 51 | // } 52 | // 53 | // override fun hashCode(): Int { 54 | // return name.hashCode() 55 | // } 56 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter1Basics/Tutorial1_3Collections3Map.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter1Basics 2 | 3 | fun main() { 4 | 5 | /* 6 | ***** MAPs ***** 7 | */ 8 | 9 | /* 10 | * INFO Instantiation 11 | */ 12 | val map1 = mapOf("b" to 2, "a" to 1) // {b=2, a=1} 13 | val mapHash = hashMapOf("b" to 2, "a" to 1) // {a=1, b=2} 14 | val mapLinked = linkedMapOf("b" to 2, "a" to 1) // {b=2, a=1} 15 | val mapSorted = sortedMapOf("b" to 2, "a" to 1) // {a=1, b=2} 16 | val mapMutable = mutableMapOf("b" to 2, "a" to 1) // {b=2, a=1} 17 | 18 | 19 | val keys: MutableSet = mapMutable.keys 20 | val values: MutableCollection = mapMutable.values 21 | val entries: MutableSet> = mapMutable.entries 22 | val iterator = mapMutable.iterator() 23 | 24 | mapMutable.put("c",3) 25 | 26 | // Iterator contains Entry, and gets iterator.hasNext() and iterates to next item if available 27 | 28 | /* 29 | * INFO Looping 30 | */ 31 | 32 | // INFO Method1: Key & Values 33 | 34 | for (k in keys) { 35 | println("Map Method1a: K: $k") 36 | } 37 | 38 | for (v in values) { 39 | println("Map Method1b: V: $v") 40 | } 41 | 42 | for ((key, value) in mapMutable) { 43 | println("Map Method1c: K: $key, V: $value") 44 | } 45 | println("****************************") 46 | 47 | // INFO Method2: EntrySet 48 | entries.forEach { (k, v) -> println("Map Method2: $k, V: $v") } 49 | println("****************************") 50 | 51 | // INFO Method3: Iterator & Entry 52 | while (iterator.hasNext()) { 53 | val entry = iterator.next() 54 | println("Map Method3: K: ${entry.key}, V: ${entry.value}") 55 | } 56 | println("****************************") 57 | 58 | // INFO Method4: Loop iterator with foreach method 59 | iterator.forEach { println("Map Method4 K: ${it.key}, V: ${it.value}") } 60 | println("****************************") 61 | 62 | // INFO Method5: Looping Keys and Values directly 63 | mapMutable.forEach { (key, value) -> println("Map Method5 K: $key , V: $value") } 64 | println("****************************") 65 | 66 | /* 67 | * INFO Immutable(Read-Only) Maps 68 | */ 69 | 70 | 71 | // 🔥 WARNING 72 | 73 | 74 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter1Basics/Tutorial1_4Ranges.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter1Basics 2 | 3 | fun main() { 4 | 5 | // ----- RANGES ----- 6 | // You define ranges by providing a starting and ending 7 | // value 8 | 9 | val oneTo10 = 1..10 10 | val alpha = "BaseClassA".."Z" 11 | 12 | // Use in to search a Range 13 | println("R in alpha : ${"R" in alpha}") 14 | 15 | // Create ranges that decrement 16 | val tenTo1 = 10.downTo(1) 17 | 18 | // Create array up to a value 19 | val twoTo20 = 2.rangeTo(20) 20 | 21 | // Step through an array while adding 3 22 | val rng3 = oneTo10.step(3) 23 | 24 | // Cycle through a range and print 25 | for (x in rng3) println("rng3 : $x") 26 | 27 | // Closed range 28 | for (i in 1 until 10) { 29 | // i in [1, 10), 10 is excluded 30 | println(i) 31 | } 32 | 33 | // Reverse a range 34 | for (x in tenTo1.reversed()) println("Reverse : $x") 35 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter1Basics/Tutorial1_5ControlFlow.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter1Basics 2 | 3 | import java.util.* 4 | 5 | fun main() { 6 | 7 | // 🔥 INFO Prints from 0 to 5 8 | for (i in 0..5) { 9 | println("Index: $i") 10 | } 11 | 12 | // This list does not have remove and methods 13 | val daysOfWeek: List = 14 | listOf("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday") 15 | 16 | println("List item get 3rd: ${daysOfWeek.get(2)}") 17 | 18 | 19 | // For each like loop 20 | for (day in daysOfWeek) { 21 | println("Day: $day") 22 | } 23 | 24 | // 🔥 INFO For loop with INDEX and VALUES 25 | for ((index, value) in daysOfWeek.withIndex()) { 26 | println("Day withIndex: #$index: $value") 27 | } 28 | 29 | // ----- LOOPING ----- 30 | // You can use for loops to cycle through arrays 31 | // ranges, or anything else that implements the 32 | // iterator function 33 | 34 | // ends at 10 35 | for (x in 1..10) { 36 | println("Loop in range 1..10 : $x") 37 | } 38 | 39 | // ends at 9 40 | for (x in 1 until 10) { 41 | println("Loop in range 1 until 10 : $x") 42 | 43 | } 44 | 45 | // Generate a random number from 1 to 50 46 | val rand = Random() 47 | val magicNum = rand.nextInt(50) + 1 48 | 49 | // While loops while a condition is true 50 | var guess = 0 51 | 52 | while (magicNum != guess) { 53 | guess += 1 54 | } 55 | 56 | println("Magic num is $magicNum and you guessed $guess") 57 | 58 | for (x in 1..20) { 59 | 60 | println("Loop before continue $x") 61 | if (x % 2 == 0) { 62 | // Continue jumps back to the top of the loop 63 | continue 64 | } 65 | 66 | println("Odd : $x") 67 | 68 | // Break jumps out of the loop and stops looping 69 | if (x == 15) break 70 | 71 | } 72 | 73 | /* 74 | Loop through Arrays 75 | */ 76 | val array: Array = arrayOf(3, 6, 9) 77 | 78 | // Iterate for indexes 79 | for (i in array.indices) { 80 | println("Mult 3 : ${array[i]}") 81 | } 82 | 83 | // Output indexes 84 | for ((index, value) in array.withIndex()) { 85 | println("Index : $index & Value : $value") 86 | } 87 | 88 | val testArray = IntArray(4) 89 | 90 | testArray.forEachIndexed { index, value -> 91 | println("forEachIndexed: index: $index, value: $value") 92 | } 93 | 94 | /* 95 | Returns and jumps 96 | */ 97 | 98 | // 🔥 Break and continue labels 99 | 100 | breakFunction() 101 | 102 | 103 | returnUnreachableFun() 104 | 105 | returnReachableFun() 106 | 107 | returnReachableFun2() 108 | } 109 | 110 | private fun breakFunction() { 111 | loop@ for (i in 1..100) { 112 | for (j in 1..100) { 113 | if (j == 10) break@loop 114 | println("Loop break i: $i, j: $j") 115 | } 116 | } 117 | 118 | println("END OF breakFunction()") 119 | } 120 | private fun returnUnreachableFun() { 121 | listOf(1, 2, 3, 4, 5).forEach { 122 | if (it == 3) return // non-local return directly to the caller of foo() 123 | println("returnUnreachableFun() it: $it") 124 | } 125 | println("this point is unreachable") 126 | } 127 | 128 | private fun returnReachableFun() { 129 | listOf(1, 2, 3, 4, 5).forEach { 130 | // This is equivalent of continue for 131 | if (it == 3) return@forEach // local return to the caller of the lambda - the forEach loop 132 | println("returnReachableFun() it: $it") 133 | } 134 | println(" done with implicit label") 135 | } 136 | 137 | private fun returnReachableFun2() { 138 | run lit@{ 139 | listOf(1, 2, 3, 4, 5).forEach { 140 | // This is equivalent of break for 141 | if (it == 3) return@lit // local return to the caller of the lambda - the forEach loop 142 | println("Inside run lit@{} it: $it") 143 | } 144 | } 145 | println("🔥 done with implicit label") 146 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter1Basics/Tutorial1_6Function1.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter1Basics 2 | 3 | fun main() { 4 | 5 | // INFO Default Argument Functions 6 | println(myFunction("Earth")) 7 | println(myFunction2()) 8 | 9 | foo(1) { println("hello") } // Uses the default value baz = 1 10 | foo(qux = { println("hello") }) // Uses both default values bar = 0 and baz = 1 11 | foo { println("hello") } // Uses both default values bar = 0 and baz = 1 12 | 13 | // INFO Named Function 14 | namedFunc(number = 4) 15 | 16 | // INFO Single Expression Function 17 | 18 | // INFO Vararg Function 19 | val myList = asList("1", "2", "3") 20 | myList.forEach { it -> println("List $it") } 21 | 22 | // INFO Infix Notation Function 23 | infix fun Int.shl(x: Int): Int { 24 | return x * 2; 25 | } 26 | 27 | // calling the function using the infix notation 28 | 1 shl 2 29 | // is the same as 30 | 1.shl(2) 31 | 32 | infix fun String.sameAs(x: String): Boolean { 33 | return this == x 34 | } 35 | 36 | val isThisTrue = "Obj" sameAs "Obj" 37 | 38 | println("🎃 Infix result $isThisTrue") 39 | 40 | // INFO Tail Recursive Function 41 | 42 | println("Factorial result: ${fact(5)}") 43 | 44 | } 45 | 46 | /** 47 | * myFunction has x as parameter and returns a String defined with : String as return type 48 | */ 49 | fun myFunction(x: String): String { 50 | val c: String = "Hey!! Welcome To ---" 51 | return (c + x) 52 | } 53 | 54 | // INFO Default Arguments 55 | /** 56 | * Function parameters can have default values, which are used when a corresponding argument is omitted. 57 | * This allows for a reduced number of overloads compared to other languages: 58 | */ 59 | fun myFunction2(x: String = "Universe"): String { 60 | val c: String = "Hey!! Welcome To ---" 61 | return (c + x) 62 | } 63 | 64 | fun read(b: Array, off: Int = 0, len: Int = b.size) { 65 | // Do some stuff... 66 | } 67 | 68 | // bar = 0, and baz = 1 are the default values. 69 | fun foo(bar: Int = 0, baz: Int = 1, qux: () -> Unit) { 70 | println("foo() bar: $bar, baz: $baz") 71 | } 72 | 73 | // INFO Named Arguments 74 | fun namedFunc(number: Int = 5, text: String = "Empty") { 75 | println("Number $number, text: $text") 76 | } 77 | 78 | // INFO Single Expression 79 | fun double(x: Int): Int = x * 2 80 | 81 | // Explicitly declaring the return type is optional when this can be inferred by the compiler: 82 | fun double2(x: Int) = x * 2 83 | 84 | // INFO Vargarg Arguments 85 | fun asList(vararg ts: T): List { 86 | val result = ArrayList() 87 | for (t in ts) // ts is an Array 88 | result.add(t) 89 | return result 90 | } 91 | 92 | 93 | // INFO Tail Recursive 94 | 95 | // Recursive function that is optimized with tailRec keyword 96 | fun fact(x: Int): Int { 97 | 98 | tailrec fun factTail(y: Int, z: Int): Int { 99 | println("FactTail x: $x, y: $y, z: $z") 100 | return if (y == 1) z else factTail(y - 1, y * z) 101 | } 102 | 103 | return factTail(x, 1) 104 | 105 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter1Basics/Tutorial1_6Function2.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter1Basics 2 | 3 | /* 4 | Lambda expression takes parameters from when it's invoked 5 | definition uses that argument in codeBody 6 | 7 | val lambdaName : Type = { argumentList -> codeBody } 8 | or 9 | val lambdaName: Type = {argument: TypeOfArgument -> codeBody } 10 | 11 | *** Instantiating a function type *** 12 | There are several ways to obtain an instance of a function type: 13 | 14 | Using a code block within a function literal, in one of the forms: 15 | a lambda expression: { a, b -> a + b }, 16 | 17 | an anonymous function: fun(s: String): Int { return s.toIntOrNull() ?: 0 } 18 | Function literals with receiver can be used as values of function types with receiver. 19 | 20 | Using a callable reference to an existing declaration: 21 | a top-level, local, member, or extension function: ::isOdd, String::toInt, 22 | 23 | a top-level, member, or extension property: List::size, 24 | a constructor: ::Regex 25 | These include bound callable references that point to a member of a particular 26 | instance: foo::toString. 27 | */ 28 | fun main() { 29 | 30 | // INFO 🔥 Lambdas 31 | 32 | // Lambda 33 | val myLambda: (String) -> Unit = { s -> println(s) } 34 | val v: String = "Tutorials" 35 | // Invocation of lambda 36 | myLambda(v) 37 | 38 | // Lambda 39 | val compLambda: (Int, Int) -> Boolean = { a, b -> 40 | a == b 41 | } 42 | // Invocation of lambda 43 | val resLambda: Boolean = compLambda(2, 4) 44 | compLambda.invoke(2, 4) 45 | 46 | // Lambda 47 | val comp2Lambda = { a: Int, b: Int -> 48 | a == b 49 | } 50 | // Invocation of lambda 51 | val res2Lambda: Boolean = comp2Lambda(2, 4) 52 | 53 | // INFO 🔥 Lambda Functions 54 | // 🔥 myFun is a function that is (String) -> String = { str -> str.reversed() } 55 | val myFun: (String) -> String = bar() // or bar()("Hello world") 56 | println(myFun("Hello world")) 57 | 58 | 59 | val isEven: (Int) -> Boolean = modulo(2) 60 | // 🔥 k = 2, 'it' = 3, and isItEven = (Int) -> Boolean = { it % k == 0 } 61 | val isItEven = isEven(3) 62 | val myList = arrayListOf(1, 2, 3) 63 | val result = myList.filter(isEven) 64 | 65 | 66 | val myFunction: () -> Int = lambdaFunctionWithParam(13) 67 | val test = myFunction() 68 | println("Test String Concatenation $test") 69 | 70 | 71 | val totalSum: Int = highOrderSum(3, { 4 }) 72 | println("totalSum: $totalSum") 73 | 74 | val totalSum2: Int = highOrderSum2(3) { 75 | it * 2 76 | } 77 | println("totalSum2: $totalSum2") 78 | 79 | 80 | // Lambda with String parameter that returns a String 81 | val anotherLambda: (String) -> String = { s -> s.uppercase() } 82 | val lambdaResult = anotherLambda("Hello World") 83 | println(lambdaResult) 84 | 85 | val swapLambda: (Int, Int, MutableList) -> List = { index1, index2, list -> 86 | 87 | require(!(index1 < 0 || index2 < 0)) { "Index cannot be smaller than zero" } 88 | require(!(index1 > list.size - 1 || index2 > list.size - 1)) { "Index cannot be bigger than size of the list" } 89 | val temp = list[index1] 90 | list[index1] = list[index2] 91 | list[index2] = temp 92 | list 93 | } 94 | 95 | val myL = listOf(1,2,3) 96 | 97 | val listNumber = mutableListOf(1, 2, 3) 98 | 99 | val resList = swapLambda(0, 1, listNumber) 100 | 101 | println("Result List: $resList") 102 | 103 | // Both implementation works 104 | // Alternative 1 105 | val lambdaFuncVar = concatLambdaFunction(13) 106 | val lambdaFuncResult = lambdaFuncVar("Try this") 107 | println(lambdaFuncResult) 108 | 109 | // Alternative 2 110 | val lambdaFuncResult2 = concatLambdaFunction(12)("Test") 111 | println(lambdaFuncResult2) 112 | 113 | // INFO 🔥 High-order functions 114 | // Alternative 1 to pass function to high order functions 115 | highOrderFunction(3) { 116 | parameterFunction(it) 117 | } 118 | 119 | // Alternative 2 to pass function to high order functions 120 | highOrderFunction(3, ::parameterFunction) 121 | 122 | "Hello World".highTes { 123 | it.reversed() 124 | } 125 | } 126 | 127 | fun String.highTes(action: (String) -> String) { 128 | action(this) 129 | } 130 | 131 | // INFO 🔥 Lambda Functions 132 | 133 | fun bar(): (String) -> String = { str -> str.reversed() } 134 | 135 | fun modulo(k: Int): (Int) -> Boolean = { it % k == 0 } 136 | 137 | /** 138 | * Lambda function that takes num Int as parameter and returns a function that takes 139 | * a String as parameter and returns a String 140 | */ 141 | fun concatLambdaFunction(num: Int): (String) -> String = { 142 | "$num + $it" 143 | } 144 | 145 | /** 146 | * Lambda function that takes args and returns a lambda 147 | */ 148 | fun lambdaFunctionWithParam(arg: Int): () -> Int = { 149 | val message = "I was created by CreateFunction()" 150 | println("createFunction() message: $message") 151 | arg * 2 152 | } 153 | 154 | // INFO 🔥 High-order functions 155 | private fun highOrderSum(a: Int, predicate: () -> Int): Int { 156 | return a + predicate() 157 | } 158 | 159 | private fun highOrderSum2(a: Int, predicate: (Int) -> Int): Int { 160 | return a + predicate(a) 161 | } 162 | 163 | fun highOrderFunction(num: Int, action: (Int) -> Boolean): Boolean { 164 | return action(num) 165 | } 166 | 167 | fun parameterFunction(num: Int): Boolean { 168 | return num % 2 == 0 169 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter2OOP/Tutorial2_10Generics.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter2OOP 2 | 3 | fun main() { 4 | val box = Box(1) // 1 has type Int, so the compiler figures out that we are talking about Box 5 | 6 | 7 | test(12) 8 | test(12.4f) 9 | } 10 | 11 | 12 | class Box(t: T) { 13 | var value = t 14 | } 15 | 16 | fun test(num: T) { 17 | 18 | } 19 | 20 | -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter2OOP/Tutorial2_11Enums.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter2OOP 2 | 3 | fun main() { 4 | val direction = Direction.NORTH 5 | 6 | val waiting = ProtocolState.WAITING 7 | println("WAITING signal(): ${ waiting.signal()}") 8 | 9 | var state = ProtocolState.WAITING 10 | repeat(4) { 11 | state = state.signal() 12 | 13 | println("state: $state") 14 | } 15 | 16 | val redColor = Color.RED 17 | 18 | println("Color: $redColor, rgb: ${redColor.rgb}") 19 | 20 | val printerType = EnumPrinterType.DOTMATRIX 21 | 22 | println("printerType: $printerType, pageAmount: ${printerType.pageAmount}, price: ${printerType.price}") 23 | 24 | 25 | } 26 | 27 | // 🔥 INFO Enum Classes 28 | enum class Direction { 29 | NORTH, SOUTH, WEST, EAST 30 | } 31 | 32 | // 🔥 INFO Initialization 33 | enum class Color(val rgb: Int) { 34 | 35 | RED(0xFF0000), 36 | GREEN(0x00FF00), 37 | BLUE(0x0000FF) 38 | 39 | } 40 | 41 | // 🔥 INFO Anonymous Classes 42 | enum class ProtocolState { 43 | 44 | WAITING { 45 | override fun signal() = TALKING 46 | }, 47 | 48 | TALKING { 49 | override fun signal() = WALKING 50 | }, 51 | 52 | WALKING { 53 | override fun signal() = WAITING 54 | }; 55 | 56 | abstract fun signal(): ProtocolState 57 | } 58 | 59 | // 🔥 INFO Working with Enum Constants 60 | 61 | 62 | // 🔥 Enum constructors are private, enums can not be instantiated 63 | enum class EnumPrinterType(var pageAmount: Int, internal var price: String) { 64 | 65 | DOTMATRIX(3, "cheap"), INKJET(5, "expensive"), LASER(7, "very expensive") 66 | 67 | } 68 | 69 | -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter2OOP/Tutorial2_14Delegation.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter2OOP 2 | 3 | 4 | fun main() { 5 | 6 | // INFO 🔥 Implementation by Delegation 7 | val b = BaseImpl(10) 8 | Derived(b).print() 9 | 10 | // INFO 🔥 Overriding a member of an interface implemented by delegation 11 | val b2 = BaseImpl2(10) 12 | Derived2(b2).printMessage() // prints abc 13 | Derived2(b2).printMessageLine() // prints 10 14 | 15 | // INFO 🔥 Members overridden in derived class 16 | val b3 = BaseImpl3(10) 17 | val derived = Derived3(b3) 18 | derived.print() // prints BaseImpl: x = 10 19 | println("Derived message: ${derived.message}") // prints Message of Derived 20 | 21 | } 22 | 23 | // INFO 🔥 Implementation by Delegation 24 | interface Base { 25 | fun print() 26 | } 27 | 28 | 29 | class BaseImpl(val x: Int) : Base { 30 | override fun print() { 31 | println(x) 32 | } 33 | } 34 | 35 | /** 36 | * The by-clause in the supertype list for Derived indicates that b will be stored internally in objects 37 | * of Derived and the compiler will generate all the methods of Base that forward to b . 38 | */ 39 | class Derived(b: Base) : Base by b 40 | 41 | // INFO 🔥 Overriding a member of an interface implemented by delegation 42 | 43 | interface Base2 { 44 | fun printMessage() 45 | fun printMessageLine() 46 | } 47 | 48 | class BaseImpl2(val x: Int) : Base2 { 49 | override fun printMessage() { 50 | println(x) 51 | } 52 | 53 | override fun printMessageLine() { 54 | println(x) 55 | } 56 | } 57 | 58 | //Overrides work as you might expect: the compiler will use your override implementations 59 | // instead of those in the delegate object. 60 | class Derived2(b: Base2) : Base2 by b { 61 | override fun printMessage() { 62 | println("abc") 63 | } 64 | } 65 | 66 | // INFO 🔥 Members overridden in derived class 67 | interface Base3 { 68 | val message: String 69 | fun print() 70 | } 71 | 72 | class BaseImpl3(val x: Int) : Base3 { 73 | override val message = "BaseImpl: x = $x" 74 | override fun print() { 75 | println(message) 76 | } 77 | } 78 | 79 | class Derived3(b: Base3) : Base3 by b { 80 | // 🔥 ⚠️ This property is not accessed from b's implementation of `print` 81 | override val message = "Message of Derived" 82 | } 83 | 84 | -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter2OOP/Tutorial2_14Delegation2.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter2OOP 2 | 3 | import kotlin.properties.ReadWriteProperty 4 | import kotlin.reflect.KProperty 5 | 6 | fun main() { 7 | 8 | // // INFO 🔥 Delegated Properties 9 | // val example = Example() 10 | // // getValue() function of Delegate class is called 11 | // println(example.p) 12 | // // setValue() function of Delegate class is called 13 | // example.p = "NEW" 14 | // println(example.p) 15 | // 16 | // val derivedUser = DerivedUser() 17 | // derivedUser.todayTasks = "BUY SOMETHING" 18 | // println("PRINT: ${derivedUser.todayTasks}") 19 | // 20 | // // INFO 🔥 ReadWriteProperty 21 | // val upperCaseString = UpperCaseString() 22 | // upperCaseString.str = "Hello " 23 | // println("Uppercase String: ${upperCaseString.str}") 24 | 25 | 26 | // INFO 🔥 Delegating to another property 27 | val myDelegateClass = MyDelegateClass(10, ClassWithDelegate(5)) 28 | 29 | myDelegateClass.delegatedToTopLevel = 100 30 | myDelegateClass.delegatedToMember = 34 31 | myDelegateClass.delegatedAnotherClass = 13 32 | 33 | println("Top level: $topLevelInt, " + 34 | "memberInt: ${myDelegateClass.memberInt}, " + 35 | "anotherClassInstance.anotherClassInt: ${myDelegateClass.anotherClassInstance.anotherClassInt}") 36 | /* 37 | Prints: 38 | Top level: 100, memberInt: 34, anotherClassInstance.anotherClassInt: 13 39 | 40 | 🔥 By delegating top level int to MyDelegate.delegatedToTopLevel any change on this 41 | property also changes topLevelInt 42 | */ 43 | 44 | 45 | // INFO 🔥 Delegating to another property for Deprecating property 46 | val myClass = ClassDeprecateWithDelegate() 47 | // Notification: 'oldName: Int' is deprecated. 48 | // Use 'newName' instead 49 | myClass.oldName = 42 50 | println(myClass.newName) // 42 51 | 52 | myClass.newName = 100 53 | println("Old: ${myClass.oldName}, new: ${myClass.newName}") 54 | // Old: 100, new: 100 55 | 56 | } 57 | 58 | 59 | // INFO 🔥 Delegated Properties 60 | 61 | class Delegate { 62 | 63 | // 🔥 first parameter is the object you read p from 64 | // second parameter holds a description of p itself (for example, you can take its name). 65 | operator fun getValue(thisRef: Any?, property: KProperty<*>): String { 66 | return "$thisRef, thank you for delegating '${property.name}' to me!" 67 | } 68 | 69 | operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) { 70 | println("$value has been assigned to '${property.name}' in $thisRef.") 71 | } 72 | } 73 | 74 | class Example { 75 | var p: String by Delegate() 76 | } 77 | 78 | 79 | class DelegatedUser { 80 | 81 | private var value: String? = null 82 | 83 | // INFO 🔥 thisRef: DerivedUser object, property is todayTasks of DerivedUser class 84 | operator fun getValue(thisRef: Any?, property: KProperty<*>): String { 85 | return "DelegatedUser getValue() property type: ${property.name}, value: $value" 86 | } 87 | 88 | operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) { 89 | this.value = value 90 | println("DelegatedUser setValue() thisRef: $thisRef, property: $property, property.type: ${property.name}, value: $value") 91 | } 92 | 93 | } 94 | 95 | class DerivedUser { 96 | var todayTasks: String by DelegatedUser() 97 | } 98 | 99 | // INFO 🔥 ReadWriteProperty 100 | class UpperCaseString { 101 | var str by UpperCaseStringDelegate() 102 | } 103 | 104 | class UpperCaseStringDelegate : ReadWriteProperty { 105 | 106 | private var value: String = "" 107 | 108 | override fun getValue(thisRef: Any, property: KProperty<*>): String { 109 | return value 110 | } 111 | 112 | override fun setValue(thisRef: Any, property: KProperty<*>, value: String) { 113 | this.value = value.uppercase() 114 | } 115 | 116 | } 117 | 118 | // INFO 🔥 Delegating to another property 119 | 120 | var topLevelInt: Int = 2 121 | 122 | class ClassWithDelegate(var anotherClassInt: Int) 123 | 124 | class MyDelegateClass(var memberInt: Int, val anotherClassInstance: ClassWithDelegate) { 125 | 126 | var delegatedToMember: Int by this::memberInt 127 | var delegatedToTopLevel: Int by ::topLevelInt 128 | 129 | var delegatedAnotherClass: Int by anotherClassInstance::anotherClassInt 130 | } 131 | 132 | var MyDelegateClass.extDelegated: Int by ::topLevelInt 133 | 134 | 135 | // INFO 🔥 Delegating to another property for Deprecating property 136 | class ClassDeprecateWithDelegate { 137 | var newName: Int = 0 138 | @Deprecated("Use 'newName' instead", ReplaceWith("newName")) 139 | var oldName: Int by this::newName 140 | } 141 | 142 | -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter2OOP/Tutorial2_14Delegation3StandartDelegation.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter2OOP 2 | 3 | import kotlin.properties.Delegates 4 | 5 | fun main() { 6 | 7 | // INFO 🔥 Standard Delegates 8 | // INFO 🔥 Lazy 9 | println(lazyValue) 10 | println(lazyValue) 11 | 12 | // INFO 🔥 Observable 13 | val user = UserObservable() 14 | user.name = "first" 15 | user.name = "second" 16 | 17 | // INFO 🔥 Vetoable 18 | println(max) // 0 19 | max = 10 20 | println(max) // 10 21 | 22 | max = 5 // will fail with IllegalArgumentException 23 | 24 | // INFO 🔥 Storing Properties in a Map 25 | val userMapDelegate = ImmutableUser( 26 | mapOf( 27 | "type" to "John Doe", 28 | "age" to 25 29 | ) 30 | ) 31 | 32 | println(userMapDelegate.name) // Prints "John Doe" 33 | println(userMapDelegate.age) // Prints 25 34 | } 35 | 36 | // INFO 🔥 Standard Delegates 37 | 38 | // INFO 🔥 Lazy 39 | // WARNING The lazy {...} delegate can only be used for val properties 40 | val lazyValue: String by lazy { 41 | println("Invoked only the first time lazy initialized!") 42 | "Hello" 43 | } 44 | 45 | 46 | // INFO 🔥 Observable 47 | 48 | class UserObservable { 49 | var name: String by Delegates.observable("") { prop, old, new -> 50 | println("$old -> $new") 51 | } 52 | } 53 | 54 | // INFO 🔥 Vetoable 55 | var max: Int by Delegates.vetoable(0) { property, oldValue, newValue -> 56 | 57 | println("vetoable property: $property, oldValue: $oldValue, newValue: $newValue") 58 | if (newValue > oldValue) true 59 | else throw IllegalArgumentException("New value must be larger than old value.") 60 | } 61 | 62 | 63 | // INFO 🔥 Storing Properties in a Map 64 | 65 | class ImmutableUser(val map: Map) { 66 | val name: String by map 67 | val age: Int by map 68 | } 69 | 70 | 71 | class MutableUser(val map: MutableMap) { 72 | var name: String by map 73 | var age: Int by map 74 | } 75 | -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter2OOP/Tutorial2_14DelegationComposeRememberMutableState.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter2OOP 2 | 3 | import java.util.LinkedHashMap 4 | import kotlin.reflect.KProperty 5 | 6 | fun main() { 7 | 8 | val isSelected: MutableState = remember { mutableStateOf(true) } 9 | isSelected.value = false 10 | 11 | var selected by remember { mutableStateOf(false) } 12 | selected = false 13 | } 14 | 15 | 16 | /* 17 | * MutableState 18 | */ 19 | interface MutableState : State { 20 | override var value: T 21 | } 22 | 23 | // Delegation Functions for setting and getting value 24 | operator fun State.getValue(thisObj: Any?, property: KProperty<*>): T = value 25 | 26 | operator fun MutableState.setValue(thisObj: Any?, property: KProperty<*>, value: T) { 27 | this.value = value 28 | } 29 | 30 | interface State { 31 | val value: T 32 | } 33 | 34 | class MutableStateImpl(value: T) : MutableState { 35 | override var value: T = value 36 | } 37 | 38 | fun mutableStateOf(value: T): MutableState = MutableStateImpl(value) 39 | 40 | /* 41 | * Remember 42 | */ 43 | 44 | inline fun remember(calculation: () -> T): T { 45 | return calculation() 46 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter2OOP/Tutorial2_15Singleton.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter2OOP 2 | 3 | import java.lang.Thread.sleep 4 | 5 | 6 | fun main() { 7 | 8 | val firstSingleton = Singleton 9 | sleep(1200) 10 | firstSingleton.printName() 11 | val secondSingleton = Singleton 12 | secondSingleton.name = "Hello World" 13 | firstSingleton.printName() 14 | 15 | println( 16 | "firstSingleton: $firstSingleton, secondSingleton: $secondSingleton," + 17 | " firstSingleton == secondSingleton ${firstSingleton == secondSingleton}" 18 | ) 19 | 20 | /* 21 | Prints: 22 | 23 | Singleton class invoked. 24 | Kotlin Objects 25 | Hello World 26 | firstSingleton: chapter2OOP.Singleton@1d44bcfa, secondSingleton: chapter2OOP.Singleton@1d44bcfa, firstSingleton == secondSingleton true 27 | */ 28 | 29 | val mySingleton1 = SingletonWithCompanion.createRandomClazz() 30 | val mySingleton2 = SingletonWithCompanion.createRandomClazz() 31 | 32 | println( 33 | "firstSingleton: $mySingleton1, secondSingleton: $mySingleton2," + 34 | " firstSingleton == secondSingleton ${mySingleton1 == mySingleton2}" 35 | ) 36 | /* 37 | Prints: 38 | 39 | firstSingleton: chapter2OOP.RandomClass@7852e922, secondSingleton: chapter2OOP.RandomClass@4e25154f, firstSingleton == secondSingleton false 40 | */ 41 | 42 | } 43 | 44 | fun otherMethod() { 45 | Singleton.name = "Test" 46 | } 47 | fun someMethod() { 48 | val s = Singleton 49 | } 50 | 51 | object Singleton { 52 | 53 | init { 54 | println("Singleton class invoked.") 55 | } 56 | 57 | var name = "Kotlin Objects" 58 | 59 | @JvmStatic 60 | fun printName() { 61 | println(name) 62 | } 63 | } 64 | 65 | /* 66 | JAVA counterpart from decompiling 67 | 68 | public final class Singleton { 69 | 70 | static { 71 | Singleton var0 = new Singleton(); 72 | INSTANCE = var0; 73 | String var1 = "Singleton class invoked."; 74 | boolean var2 = false; 75 | System.out.println(var1); 76 | name = "Kotlin Objects"; 77 | } 78 | 79 | @NotNull 80 | private static String name; 81 | public static final Singleton INSTANCE; 82 | 83 | @NotNull 84 | public final String getName() { 85 | return name; 86 | } 87 | 88 | public final void setName(@NotNull String var1) { 89 | Intrinsics.checkParameterIsNotNull(var1, ""); 90 | name = var1; 91 | } 92 | 93 | @JvmStatic 94 | public static final void printName() { 95 | String var1 = name; 96 | boolean var2 = false; 97 | System.out.println(var1); 98 | } 99 | 100 | private Singleton() { 101 | } 102 | } 103 | */ 104 | 105 | class SingletonWithCompanion { 106 | 107 | companion object Factory { 108 | 109 | /** 110 | * This method does not create Singleton objects 111 | */ 112 | fun createRandomClazz(): RandomClass = RandomClass() 113 | } 114 | } 115 | 116 | 117 | /* 118 | public final class SingletonWithCompanion { 119 | @NotNull 120 | public static final SingletonWithCompanion.Factory Factory = new SingletonWithCompanion.Factory((DefaultConstructorMarker)null); 121 | 122 | 123 | public static final class Factory { 124 | @NotNull 125 | public final RandomClass createRandomClazz() { 126 | return new RandomClass(); 127 | } 128 | 129 | private Factory() { 130 | } 131 | 132 | // $FF: synthetic method 133 | public Factory(DefaultConstructorMarker $constructor_marker) { 134 | this(); 135 | } 136 | } 137 | } 138 | 139 | */ 140 | 141 | class RandomClass -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter2OOP/Tutorial2_1Constructors.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter2OOP 2 | 3 | import com.smarttoolfactory.tutorial.chapter2OOP.model.* 4 | 5 | fun main() { 6 | 7 | val a: String? = null 8 | val b: String = "" 9 | println(a == b) 10 | 11 | // create obj object of myClass class 12 | // val obj = MyClass() 13 | // obj.printMe() 14 | // 15 | val person1 = Person("Alex", "Smith", 29) 16 | val person2 = Person("Jane", "Smith", null) 17 | // 18 | println("${person1.firstName},${person1.lastName} is ${person1.age} years old") 19 | // println("${person2.firstName},${person2.lastName} is ${person2.age?.toString() ?: "?"} years old") 20 | // 21 | val personType2 = Person2( 22 | "June", 23 | "Smith", 24 | 23 25 | ) 26 | 27 | // println("Name: ${personType2.getType()}, age: ${personType2.getAge()}") 28 | 29 | 30 | 31 | // INFO Init Blocks 32 | val initOrder = InitOrderDemo("Demo") 33 | 34 | // // INFO Secondary Constructors 35 | val person3 = Person4("Jane", "White") 36 | val person4 = Person4("Jake", "White") 37 | 38 | val constructors = Constructors(5) 39 | 40 | val auto1 = Auto(5, "Honda") 41 | val auto2 = 42 | Auto(name = "Ferrari") 43 | val auto3 = Auto(2, "BMW") 44 | 45 | 46 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter2OOP/Tutorial2_2NestedClasses.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter2OOP 2 | 3 | fun main() { 4 | 5 | // TODO Static Nested Classes 6 | val line = BasicGraph.Line(1, 0, -2, 0) 7 | line.draw() 8 | 9 | // nested class must be initialized 10 | println(OuterOfNestedClass.NestedClass().description) // accessing property 11 | val objNested = OuterOfNestedClass.NestedClass() // object creation 12 | objNested.foo() // access member function 13 | 14 | // TODO Inner Classes 15 | 16 | val lineWithInner = BasicGraphWithInner("Graph with Inner").InnerLine(1, 2, 3, 4) 17 | lineWithInner.draw() 18 | 19 | println(OuterOfInnerClass().InnerClass().description) // accessing property 20 | val objInner = OuterOfInnerClass().InnerClass() // object creation 21 | objInner.foo() // access member function 22 | 23 | } 24 | 25 | // 🔥 INFO: Equivalent of Java's STATIC Nested class 26 | 27 | /* 28 | * INFO Nested class can NOT access to any members of outer class 29 | */ 30 | class BasicGraph(val name: String) { 31 | 32 | class Line(val x1: Int, val y1: Int, val x2: Int, val y2: Int) { 33 | fun draw(): Unit { 34 | println("Drawing Line from ($x1:$y1) to ($x2, $y2)") 35 | } 36 | } 37 | 38 | fun draw(): Unit { 39 | println("Drawing the graph $name") 40 | } 41 | } 42 | 43 | class OuterOfNestedClass { 44 | 45 | private var name: String = "Ashley" 46 | 47 | class NestedClass { 48 | 49 | var description: String = "code inside nested class" 50 | private var id: Int = 101 51 | 52 | fun foo() { 53 | // 🔥 INFO cannot access the outer class member 54 | // print("type is ${type}") 55 | println("Id is ${id}") 56 | } 57 | } 58 | 59 | } 60 | 61 | /* 62 | Java Equivalent 63 | */ 64 | 65 | /* 66 | public final class OuterOfNestedClass { 67 | private String name = "Ashu"; 68 | 69 | public static final class NestedClass { 70 | @NotNull 71 | private String description = "code inside nested class"; 72 | private int id = 101; 73 | 74 | @NotNull 75 | public final String getDescription() { 76 | return this.description; 77 | } 78 | 79 | public final void setDescription(@NotNull String var1) { 80 | Intrinsics.checkNotNullParameter(var1, ""); 81 | this.description = var1; 82 | } 83 | 84 | public final void foo() { 85 | String var1 = "Id is " + this.id; 86 | boolean var2 = false; 87 | System.out.println(var1); 88 | } 89 | } 90 | } 91 | 92 | */ 93 | 94 | 95 | // 🔥 INFO: Equivalent of Java's INNER class 96 | /** 97 | * Inner class which has inner tag in front of class name, of an access field of outer class unlike nested class 98 | */ 99 | class BasicGraphWithInner(graphName: String) { 100 | 101 | private val name: String 102 | 103 | init { 104 | name = graphName 105 | } 106 | 107 | /** 108 | * Classes with inner declaration can access to members of outer class even if they are private 109 | */ 110 | inner class InnerLine(val x1: Int, val y1: Int, val x2: Int, val y2: Int) { 111 | fun draw(): Unit { 112 | println("Drawing Line from ($x1:$y1) to ($x2, $y2) for graph $name ") 113 | } 114 | } 115 | 116 | fun draw(): Unit { 117 | println("Drawing the graph $name") 118 | } 119 | 120 | } 121 | 122 | 123 | class OuterOfInnerClass { 124 | private var name: String = "Ash" 125 | 126 | inner class InnerClass { 127 | 128 | var description: String = "code inside inner class" 129 | private var id: Int = 101 130 | 131 | fun foo() { 132 | // 🔥 INFO ACCESS the outer class member even when it's private 133 | println("type is $name") 134 | println("Id is $id") 135 | } 136 | } 137 | } 138 | 139 | /* 140 | public final class OuterOfInnerClass { 141 | private String name = "Ash"; 142 | 143 | public final class InnerClass { 144 | @NotNull 145 | private String description = "code inside inner class"; 146 | private int id = 101; 147 | 148 | @NotNull 149 | public final String getDescription() { 150 | return this.description; 151 | } 152 | 153 | public final void setDescription(@NotNull String var1) { 154 | Intrinsics.checkNotNullParameter(var1, ""); 155 | this.description = var1; 156 | } 157 | 158 | public final void foo() { 159 | String var1 = "type is " + OuterOfInnerClass.this.name; 160 | boolean var2 = false; 161 | System.out.println(var1); 162 | var1 = "Id is " + this.id; 163 | var2 = false; 164 | System.out.println(var1); 165 | } 166 | } 167 | } 168 | */ 169 | 170 | 171 | class A { 172 | 173 | private val somefield: Int = 1 174 | 175 | inner class B { 176 | 177 | private val somefield: Int = 1 178 | 179 | fun foo(s: String) { 180 | println("Field from InterfaceB" + this.somefield) 181 | 182 | // 🔥 this with @ annotation points to instance of defined class 183 | println("Field from InterfaceB" + this@B.somefield) 184 | println("Field from BaseClassA" + this@A.somefield) 185 | } 186 | } 187 | } 188 | 189 | -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter2OOP/Tutorial2_3OtherClasses.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter2OOP 2 | 3 | fun main() { 4 | 5 | // Companion Object 6 | val car = Car.makeCar(150) 7 | println(Car.cars.size) 8 | 9 | // Data class object 10 | val customer = Customer(1, "John", "Elm Street") 11 | 12 | var shape = Rectangle(1, 3) 13 | 14 | 15 | } 16 | 17 | // INFO Companion Object Class 18 | class Car(val horsepowers: Int) { 19 | 20 | 21 | companion object Factory { 22 | val cars = mutableListOf() 23 | 24 | fun makeCar(horsepowers: Int): Car { 25 | val car = Car(horsepowers) 26 | cars.add(car) 27 | return car 28 | } 29 | } 30 | } 31 | 32 | 33 | // INFO Data Class 34 | /* 35 | * 🔥 36 | */ 37 | data class Customer(val id: Int, val name: String, var address: String) 38 | 39 | 40 | // INFO Enum Classes 41 | enum class Day { 42 | MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, 43 | SATURDAY, SUNDAY 44 | } 45 | 46 | public enum class Planet(val mass: Double, val radius: Double) { 47 | MERCURY(3.303e+23, 2.4397e6), 48 | VENUS(4.869e+24, 6.0518e6), 49 | EARTH(5.976e+24, 6.37814e6), 50 | MARS(6.421e+23, 3.3972e6), 51 | JUPITER(1.9e+27, 7.1492e7), 52 | SATURN(5.688e+26, 6.0268e7), 53 | URANUS(8.686e+25, 2.5559e7), 54 | NEPTUNE(1.024e+26, 2.4746e7); 55 | 56 | } 57 | 58 | // INFO Abstract Class 59 | 60 | abstract class Shape protected constructor() { 61 | 62 | // IMPORTANT 🔥🔥 val, and var are NOT ALLOWED with SECOND constructor 63 | constructor(x: Int, Y: Int) : this() { 64 | xLocation = x 65 | yLocation = Y 66 | } 67 | 68 | abstract var xLocation: Int 69 | abstract var yLocation: Int 70 | 71 | var width: Double = 0.0 72 | var height: Double = 0.0 73 | 74 | abstract fun isHit(x: Int, y: Int): Boolean 75 | } 76 | 77 | /* 78 | public abstract class Shape { 79 | private double width; 80 | private double height; 81 | 82 | protected Shape() { 83 | 84 | } 85 | 86 | public Shape(int x, int Y) { 87 | this(); 88 | } 89 | 90 | public abstract int getXLocation(); 91 | public abstract void setXLocation(int var1); 92 | public abstract int getYLocation(); 93 | public abstract void setYLocation(int var1); 94 | 95 | public final double getWidth() { 96 | return this.width; 97 | } 98 | 99 | public final void setWidth(double var1) { 100 | this.width = var1; 101 | } 102 | 103 | public final double getHeight() { 104 | return this.height; 105 | } 106 | 107 | public final void setHeight(double var1) { 108 | this.height = var1; 109 | } 110 | 111 | public abstract boolean isHit(int var1, int var2); 112 | } 113 | */ 114 | 115 | class Ellipsis(xBase: Int, yBase: Int) : Shape(xBase, yBase) { 116 | 117 | override var xLocation: Int = 0 118 | override var yLocation: Int = 0 119 | 120 | override fun isHit(x: Int, y: Int): Boolean { 121 | 122 | val xRadius = width / 2 123 | val yRadius = height / 2 124 | val centerX = xLocation + xRadius 125 | val centerY = yLocation + yRadius 126 | 127 | if (xRadius == 0.0 || yRadius == 0.0) 128 | return false 129 | 130 | val normalizedX = centerX - xLocation 131 | val normalizedY = centerY - yLocation 132 | return (normalizedX * normalizedX) / (xRadius * xRadius) + 133 | (normalizedY * normalizedY) / (yRadius * yRadius) <= 1.0 134 | } 135 | } 136 | 137 | /* 138 | public final class Ellipsis extends Shape { 139 | private int xLocation; 140 | private int yLocation; 141 | 142 | public Ellipsis(int xBase, int yBase) { 143 | super(xBase, yBase); 144 | } 145 | 146 | public int getXLocation() { 147 | return this.xLocation; 148 | } 149 | 150 | public void setXLocation(int var1) { 151 | this.xLocation = var1; 152 | } 153 | 154 | public int getYLocation() { 155 | return this.yLocation; 156 | } 157 | 158 | public void setYLocation(int var1) { 159 | this.yLocation = var1; 160 | } 161 | 162 | public boolean isHit(int x, int y) { 163 | double xRadius = this.getWidth() / (double)2; 164 | double yRadius = this.getHeight() / (double)2; 165 | double centerX = (double)this.getXLocation() + xRadius; 166 | double centerY = (double)this.getYLocation() + yRadius; 167 | if (xRadius != 0.0D && yRadius != 0.0D) { 168 | double normalizedX = centerX - (double)this.getXLocation(); 169 | double normalizedY = centerY - (double)this.getYLocation(); 170 | return normalizedX * normalizedX / (xRadius * xRadius) + normalizedY * normalizedY / (yRadius * yRadius) <= 1.0D; 171 | } else { 172 | return false; 173 | } 174 | } 175 | } 176 | */ 177 | 178 | class Rectangle(override var xLocation: Int, override var yLocation: Int) : Shape() { 179 | 180 | override fun isHit(x: Int, y: Int): Boolean { 181 | return x >= xLocation && x <= (xLocation + width) && y >= 182 | yLocation && y <= (yLocation + height) 183 | } 184 | } 185 | 186 | /* 187 | public final class Rectangle extends Shape { 188 | private int xLocation; 189 | private int yLocation; 190 | 191 | public Rectangle(int xLocation, int yLocation) { 192 | this.xLocation = xLocation; 193 | this.yLocation = yLocation; 194 | } 195 | 196 | public boolean isHit(int x, int y) { 197 | return x >= this.getXLocation() && (double)x <= (double)this.getXLocation() + this.getWidth() && y >= this.getYLocation() && (double)y <= (double)this.getYLocation() + this.getHeight(); 198 | } 199 | 200 | public int getXLocation() { 201 | return this.xLocation; 202 | } 203 | 204 | public void setXLocation(int var1) { 205 | this.xLocation = var1; 206 | } 207 | 208 | public int getYLocation() { 209 | return this.yLocation; 210 | } 211 | 212 | public void setYLocation(int var1) { 213 | this.yLocation = var1; 214 | } 215 | } 216 | */ 217 | -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter2OOP/Tutorial2_4Inheritance.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter2OOP 2 | 3 | 4 | fun main() { 5 | 6 | // val derived = Derived("Foo", "Bar") 7 | 8 | // Class with interface and base class 9 | // val c = C() 10 | // // INFO 🔥 f() method calls super method of Class BaseClassA and Interface InterfaceB 11 | // c.f() 12 | 13 | val sportsCar = SportsCar() 14 | 15 | println("SportsCar type: ${sportsCar.type}, manufacturer: ${sportsCar.manufacturer}") 16 | 17 | val resultSuccess = Result.Success(3) 18 | val resultError = Result.Error(IllegalArgumentException("Exception")) 19 | 20 | val resultBoxed = getResult("Hello World", 2) 21 | 22 | when (resultBoxed) { 23 | is Result.Success -> println("Result: ${resultBoxed.data}") 24 | is Result.Error -> println("Error: ${resultBoxed.exception.message}") 25 | } 26 | 27 | val bus = Bus("Ford") 28 | 29 | bus.printMaker() 30 | /* 31 | Prints Ford 32 | */ 33 | 34 | println("Manufacturer: ${(bus as Vehicle).manufacturer}") 35 | /* 36 | Prints Ford 37 | */ 38 | 39 | } 40 | 41 | fun getResult(data: T, index: Int): Result { 42 | return if (index < 0) { 43 | Result.Error(IllegalAccessException("Number cannot be smaller than 0")) 44 | } else { 45 | Result.Success(data) 46 | } 47 | } 48 | 49 | 50 | sealed class VehicleType { 51 | data class CarType(val type: Int) : VehicleType() 52 | data class BusType(val type: Int) : VehicleType() 53 | } 54 | 55 | sealed class Result { 56 | data class Success(val data: T) : Result() 57 | data class Error(val exception: Exception) : Result() 58 | } 59 | 60 | open class Vehicle(var type: VehicleType, open var manufacturer: String) 61 | 62 | class SportsCar : Vehicle(VehicleType.CarType(1), "Tesla") 63 | 64 | /** 65 | * 66 | */ 67 | class Bus(override var manufacturer: String) : 68 | Vehicle(VehicleType.BusType(2), "$manufacturer + Bus") { 69 | 70 | fun printMaker() { 71 | println("Maker: $manufacturer, super manufacturer: ${super.manufacturer}") 72 | } 73 | } 74 | 75 | /* 76 | @NotNull 77 | private String manufacturer; 78 | 79 | public final void printMaker() { 80 | String var1 = "Maker: " + this.getManufacturer(); 81 | boolean var2 = false; 82 | System.out.println(var1); 83 | } 84 | 85 | @NotNull 86 | public String getManufacturer() { 87 | return this.manufacturer; 88 | } 89 | 90 | public void setManufacturer(@NotNull String var1) { 91 | this.manufacturer = var1; 92 | } 93 | 94 | public Bus(@NotNull String manufacturer) { 95 | super((VehicleType)(new VehicleType.BusType(2)), manufacturer + " + Bus"); 96 | this.manufacturer = manufacturer; 97 | } 98 | */ -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter2OOP/Tutorial2_4Inheritance2.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter2OOP 2 | 3 | import com.smarttoolfactory.tutorial.chapter2OOP.model.BusinessAccount 4 | import com.smarttoolfactory.tutorial.chapter2OOP.model.PrivateAccount 5 | import com.smarttoolfactory.tutorial.chapter2OOP.model.Snake 6 | import com.smarttoolfactory.tutorial.chapter2OOP.model.UnionAccount 7 | 8 | fun main() { 9 | 10 | // val privateAccount = PrivateAccount(22.5) 11 | // println("Private baseAmount: ${privateAccount.baseAmount} ") 12 | // privateAccount.baseAmount = 130.0 13 | // println("Private AFTER baseAmount: ${privateAccount.baseAmount} ") 14 | // privateAccount.displayValue() 15 | // 16 | // val businessAccount = BusinessAccount(50.0) 17 | // println("Business baseAmount: ${businessAccount.baseAmount} ") 18 | // businessAccount.baseAmount = 130.0 19 | // println("Business AFTER baseAmount: ${businessAccount.baseAmount} ") 20 | // businessAccount.displayValue() 21 | 22 | val unionAccount = UnionAccount(61.8) 23 | unionAccount.setBase(12.54) 24 | unionAccount.displayValue() 25 | 26 | unionAccount.unionProperty = "Test" 27 | 28 | println("Union property: ${unionAccount.unionProperty}") 29 | 30 | /* 31 | Private baseAmount: 22.5 32 | Private AFTER baseAmount: 130.0 33 | PrivateAccount Parent super.baseAmount: 130.0, derived: 130.0 34 | 35 | Business baseAmount: 0.0 36 | Business AFTER baseAmount: 390.0 37 | BusinessAccount Parent super.baseAmount: 50.0, derived: 390.0 38 | 39 | UnionAccount Parent super.baseAmount: 61.8, derived: 12.54 40 | Union property: Test 12.54 41 | */ 42 | 43 | 44 | // val snake = Snake() 45 | // println("Snake Sound: ${snake.makeSound()}") 46 | // println("Snake Move: ${snake.doMove()}") 47 | // println("Snake Max Age: ${snake.MAX_AGE}") 48 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter2OOP/Tutorial2_5Properties.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter2OOP 2 | 3 | fun main() { 4 | 5 | // INFO Setters and getters 6 | // val setterAndGetterWrapper = SetterAndGetters() 7 | // println(setterAndGetterWrapper.stringRepresentation) 8 | // setterAndGetterWrapper.stringRepresentation = "Hello World" 9 | 10 | // INFO Backing Field 11 | val user = User("Jackson", "Hitch") 12 | user.myName = "Johnny" 13 | 14 | // INFO 🔥⚠️ Throws StackOverflow exception when get() called 15 | // println("User type ${user.myName}") 16 | 17 | // INFO Backing Property 18 | val humanWithBackingProperty = HumanWithBackingProperty() 19 | println("Human age: ${humanWithBackingProperty.age}") 20 | humanWithBackingProperty.age = 15 21 | println("Human age after set(): ${humanWithBackingProperty.age}") 22 | // Lambda method 23 | humanWithBackingProperty.printAge() 24 | 25 | 26 | } 27 | 28 | // INFO Setters and getters 29 | class SetterAndGetters { 30 | 31 | var stringRepresentation: String 32 | get() = this.toString() 33 | set(value) { 34 | setDataFromString(value) // parses the string and assigns values to other properties 35 | } 36 | 37 | // INFO 🔥🔥🔥⚠️ Using property = with a set() function calls set recursively and causes StackOverflow exception 38 | private fun setDataFromString(value: String) { 39 | // 🔥 !!! Causes recursive call to set() from this method 40 | // stringRepresentation = "New Assignment" 41 | println("🤨 SettersAndGetters setDataFromString $stringRepresentation") 42 | } 43 | } 44 | 45 | 46 | const val PREFIX = "[ABC]" 47 | 48 | 49 | // 🔥 INFO BACKING FIELDS 50 | class Person { 51 | 52 | // set: if value set to first type have length < 1 => throw error else add prefix "ABC" to the type 53 | // get: if type is not empty -> trim for remove whitespace and add '.' else return default type 54 | var lastName: String = "" 55 | get() { 56 | if (field.isNotEmpty()) { 57 | return field.trim() + "." 58 | } 59 | return field 60 | } 61 | set(value) { 62 | if (value.length > 1) { 63 | field = PREFIX + value 64 | } else { 65 | throw IllegalArgumentException("Last type too short") 66 | } 67 | } 68 | } 69 | 70 | /* 71 | public final class Person { 72 | @NotNull 73 | private String lastName = ""; 74 | 75 | @NotNull 76 | public final String getLastName() { 77 | CharSequence var1 = (CharSequence)this.lastName; 78 | 79 | if (var1.length() > 0) { 80 | 81 | StringBuilder var10000 = new StringBuilder(); 82 | 83 | String var3 = this.lastName; 84 | 85 | if (var3 == null) { 86 | throw new NullPointerException("null cannot be cast to non-null type kotlin.CharSequence"); 87 | } else { 88 | return var10000.append(StringsKt.trim((CharSequence)var3).toString()).append(".").toString(); 89 | } 90 | } else { 91 | return this.lastName; 92 | } 93 | } 94 | 95 | public final void setLastName(@NotNull String value) { 96 | if (value.length() > 1) { 97 | this.lastName = "[ABC]" + value; 98 | } else { 99 | throw (Throwable)(new IllegalArgumentException("Last type too short")); 100 | } 101 | } 102 | } 103 | */ 104 | 105 | 106 | // INFO 🔥⚠️ Throws StackOverflow exception when get() called 107 | class User(private var name: String, private var surName: String) { 108 | 109 | var myName: String = name 110 | get() { 111 | return myName.substring(0, 4) 112 | } 113 | 114 | val fullName: String 115 | get() = "name: $name, surname: $surName" 116 | } 117 | 118 | 119 | // INFO 🔥⚠️ Throws StackOverflow exceptions when get() or set() called for firstName or LastName 120 | class User2 { 121 | var firstName: String //backing field generated 122 | get() = firstName 123 | set(value) { 124 | firstName = value 125 | } 126 | var lastName: String //backing field generated 127 | get() = lastName 128 | set(value) { 129 | lastName = value 130 | } 131 | val name: String //no backing field generated 132 | get() = "{$firstName $lastName}" 133 | var address: String = "XYZ" //^because there is no default //^implementation of an accessor 134 | 135 | } 136 | 137 | 138 | class Human { 139 | val age = 20 140 | get() { 141 | println("Age is: $field") 142 | return field 143 | } 144 | } 145 | 146 | // INFO Java Equivalent of Kotlin code for backing field. Note: This won't work in Kotlin class 147 | //public final class Human { 148 | // private final int age = 20; 149 | // 150 | // public final int getAge() { 151 | // String var1 = "Age is: " + this.age; 152 | // System.out.println(var1); 153 | // return this.age; 154 | // } 155 | //} 156 | 157 | // INFO 🔥🔥🔥 Backing Properties 158 | 159 | class HumanWithBackingProperty { 160 | 161 | private var _age: Int = 20 162 | 163 | var age: Int 164 | get() { 165 | println("HumanWithBackingProperty get()") 166 | return _age 167 | } 168 | set(value) { 169 | println("HumanWithBackingProperty set()") 170 | _age = value 171 | } 172 | 173 | val printAge: () -> Unit = { 174 | println("Age is: $_age") 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter2OOP/Tutorial2_6Interfaces.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter2OOP 2 | 3 | fun main() { 4 | 5 | val child = Child() 6 | println("Child propertyWithImplementation ${child.propertyWithImplementation}") 7 | child.foo() 8 | child.prop = 2 9 | child.foo() 10 | 11 | /* 12 | Prints: 13 | Child propertyWithImplementation fooBar 14 | 29 15 | 2 16 | */ 17 | } 18 | 19 | interface MyInterface { 20 | fun bar() 21 | fun foo() { 22 | // optional body 23 | println("MyInterface foo()") 24 | } 25 | } 26 | 27 | /* 28 | public interface MyInterface { 29 | void bar(); 30 | 31 | void foo(); 32 | 33 | public static final class DefaultImpls { 34 | public static void foo(@NotNull MyInterface $this) { 35 | String var1 = "MyInterface foo()"; 36 | boolean var2 = false; 37 | System.out.println(var1); 38 | } 39 | } 40 | } 41 | */ 42 | 43 | interface MyInterface2 { 44 | 45 | var prop: Int // abstract 46 | 47 | 48 | // INFO 🔥⚠️ This is NOT allowed: Property initializers are not allowed in interfaces 49 | // val propertyWithImplementation: String = "fooBar" 50 | 51 | // INFO 🔥⚠️ This CANNOT be var -> Property in interface cannot have a backing field 52 | // Creates a static nested class with static methodDefaultImpls.getPropertyWithImplementation 53 | val propertyWithImplementation: String 54 | get() = "fooBar" 55 | 56 | fun foo() { 57 | println(prop) 58 | } 59 | 60 | } 61 | 62 | /* 63 | public interface MyInterface2 { 64 | int getProp(); 65 | 66 | void setProp(int var1); 67 | 68 | @NotNull 69 | String getPropertyWithImplementation(); 70 | 71 | void foo(); 72 | 73 | 74 | public static final class DefaultImpls { 75 | 76 | @NotNull 77 | public static String getPropertyWithImplementation(@NotNull MyInterface2 $this) { 78 | return "fooBar"; 79 | } 80 | 81 | public static void foo(@NotNull MyInterface2 $this) { 82 | int var1 = $this.getProp(); 83 | boolean var2 = false; 84 | System.out.println(var1); 85 | } 86 | } 87 | } 88 | */ 89 | 90 | class Child : MyInterface2 { 91 | override var prop: Int = 29 92 | } 93 | 94 | interface Named { 95 | val name:String 96 | } 97 | 98 | interface PersonNamed:Named { 99 | 100 | val firstName:String 101 | val secondName: String 102 | 103 | override val name: String 104 | get() = "$firstName, lastName: $secondName" 105 | 106 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter2OOP/Tutorial2_7VisibilityModifiers.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter2OOP 2 | 3 | fun main() { 4 | val subclass = Subclass() 5 | // INFO ⚠️ only c, and d properties are visible to instance of derived class 6 | println("Subclass c ${subclass.c}, d ${subclass.d}") 7 | 8 | } 9 | 10 | open class ParentClass { 11 | 12 | private val a = 1 13 | protected open val b = 2 14 | internal val c = 3 15 | val d = 4 // public by default 16 | 17 | protected class Nested { 18 | public val e: Int = 5 19 | } 20 | 21 | } 22 | 23 | class Subclass : ParentClass() { 24 | 25 | // a is not visible 26 | // b, c and d are visible // Nested and e are visible 27 | override val b = 5 // 'b' is protected } 28 | 29 | fun test() { 30 | println("b $b, c $c, d $d") 31 | val nested = Nested() 32 | 33 | } 34 | } 35 | 36 | class Unrelated(o: ParentClass) { 37 | // o.a, o.b are not visible 38 | // o.c and o.d are visible (same module) 39 | // Outer.Nested is not visible, and Nested::e is not visible either 40 | 41 | init { 42 | // INFO ⚠️ not Visible 43 | // val nestedClass = Nested() 44 | } 45 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter2OOP/Tutorial2_8Extensions.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter2OOP 2 | 3 | fun main() { 4 | 5 | // INFO 🔥 Extension function 6 | val testString: String = "Hello World " 7 | println("Test ${testString.upperCaseAndTrim()}") 8 | 9 | val list = mutableListOf(); 10 | list.add(1) 11 | list.add(2) 12 | list.add(3) 13 | 14 | list.swap(0, 1) 15 | list.forEach { it -> println("it $it") } 16 | 17 | // INFO 🔥 Extensions are resolved statically 18 | printFoo(D()) 19 | // calls member function 20 | Cex().foo() 21 | 22 | // INFO 🔥 Overloaded extension function will call suitable overloaded function 23 | CExtension().foo(1) // prints extension 24 | 25 | // INFO 🔥 Companion Object Extensions 26 | MyClass.foo() 27 | 28 | // INFO 🔥 Declaring Extensions as Members 29 | val f = F() 30 | f.caller(G()) 31 | 32 | 33 | } 34 | 35 | // INFO 🔥 Extension function 36 | 37 | fun String.upperCaseAndTrim(): String { 38 | // INFO The this keyword inside an extension function corresponds to the receiver object 39 | return this.uppercase().trim() 40 | } 41 | 42 | fun MutableList.swap(index1: Int, index2: Int) { 43 | val tmp = this[index1] // 'this' corresponds to the list this[index1] = this[index2] 44 | this[index2] = tmp 45 | } 46 | 47 | 48 | // INFO 🔥 Extensions are resolved statically 49 | open class C 50 | 51 | class D : C() 52 | 53 | fun C.foo() = "c" 54 | fun D.foo() = "d" 55 | 56 | fun printFoo(c: C) { 57 | println(c.foo()) 58 | } 59 | 60 | // INFO 🔥 calls member function instead of extension 61 | class Cex { 62 | fun foo() { 63 | println("member") 64 | } 65 | } 66 | 67 | /* 68 | If a class has a member function, and an extension function is de􏰁fined 69 | which has the same receiver type, the same name is applicable to given arguments, 70 | the member always wins. For example: 71 | 🔥 invoking foo() calls MEMBER not EXTENSION function 72 | */ 73 | fun Cex.foo() { 74 | println("extension") 75 | } 76 | 77 | // INFO 🔥 Overloaded extension function 78 | class CExtension { 79 | fun foo() { 80 | println("member") 81 | } 82 | } 83 | 84 | fun CExtension.foo(i: Int) { 85 | println("extension") 86 | } 87 | 88 | /* 89 | Note that extensions can be defined with a nullable receiver type. 90 | Such extensions can be called on an object variable even if its value is null, 91 | and can check for this == null inside the body. 92 | This is what allows you to call toString() in Kotlin without checking for null: 93 | the check happens inside the extension function. 94 | */ 95 | fun Any?.toString(): String { 96 | if (this == null) return "null" 97 | // after the null check, 'this' is autocast to a non-null type, so the toString() 98 | // below resolves to the member function of the Any class 99 | return toString() 100 | } 101 | 102 | // INFO 🔥 Extension Properties 103 | val List.lastIndex: Int 104 | get() = size - 1 105 | 106 | // INFO 🔥 Companion Object Extensions 107 | class MyClass { 108 | companion object {} // will be called "Companion" 109 | } 110 | 111 | fun MyClass.Companion.foo() { 112 | println("MyClass.Companion.foo()") 113 | } 114 | 115 | 116 | // INFO 🔥 Declaring Extensions as Members 117 | class G { 118 | fun bar() { 119 | println("G bar()") 120 | } 121 | } 122 | 123 | class F { 124 | fun baz() { 125 | println("F baz()") 126 | } 127 | 128 | fun G.foo() { 129 | bar() // calls G.bar 130 | baz() // calls F.baz 131 | } 132 | 133 | fun caller(g: G) { 134 | g.foo() 135 | } 136 | } 137 | 138 | 139 | -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter2OOP/Tutorial2_9DataClasses.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter2OOP 2 | 3 | /** 4 | * Example for data classes 5 | * 6 | * * — The primary constructor needs to have at least one parameter 7 | * * — All primary constructor parameters need to be marked as val or var 8 | * * — Data classes cannot be abstract, open, sealed or inner 9 | */ 10 | fun main() { 11 | 12 | val person1 = PersonData("John") 13 | val person2 = PersonData("John") 14 | 15 | // 🔥🔥 age property is not declared inside Constructor, so does NOT effect equals and hash code 16 | person1.age = 10 17 | person2.age = 20 18 | 19 | // 🔥🔥 Copying data from one data class to another, only copies properties in constructor 20 | val person3 = person2.copy(name = "Dave") 21 | 22 | // 🔥 If primary constructor properties have same values objects have Structural Equality 23 | val person4 = person2.copy(name = person2.name) 24 | 25 | 26 | // Prints: person1: 2314539, person2: 2314539, person3: 2122764, person4: 2314539 27 | println("person1: ${person1.hashCode()}, person2: ${person2.hashCode()}, " + 28 | "person3: ${person3.hashCode()}, person4: ${person4.hashCode()}") 29 | 30 | println("${person1.toString()}") // prints PersonData(name=John) 31 | 32 | // Referential equality returns true only for the SAME objects even for data classes 33 | println("Person1 == Person2 -> ${person1 == person2}") // prints true 34 | println("Person1 === Person2 -> ${person1 === person2}") // prints false 35 | 36 | // 🔥 variables with default values can be omitted when data class is instantiated 37 | val carData = CarData(manifacturer = "", model = "") 38 | 39 | /* 40 | *** EQUALITY *** 41 | */ 42 | 43 | // == means Structural equality which looks properties of a class 44 | // === means Referential equality which is true for a and b point to the same object 45 | 46 | val bookData1 = BookData("LOTR", 54) 47 | val bookData2 = BookData("LOTR", 54) 48 | val bookData3 = bookData1 49 | 50 | val book1 = Book("LOTR", 54) 51 | val book2 = Book("LOTR", 54) 52 | val book3 = book1 53 | 54 | println("Book1: ${book1.hashCode()} == Book2: ${book2.hashCode()} -> ${book1 == book2}") // prints false 55 | // Book1: 1096979270 == Book2: 1078694789 -> false 56 | println("Book1 == Book2 -> ${book1 == book2}") // prints false 57 | println("Book1 === Book2 -> ${book1 === book2}") // prints false 58 | 59 | // Data class 60 | println("BookData1: ${bookData1.hashCode()} == BookData2: ${bookData2.hashCode()} -> ${bookData1 == bookData2}") // prints true 61 | // BookData1: 72624405 == BookData2: 72624405 -> true 62 | println("BookData2: ${bookData2.hashCode()} == BookData3: ${bookData3.hashCode()} -> ${bookData2 == bookData3}") // prints true 63 | // BookData2: 72624405 == BookData3: 72624405 -> true 64 | 65 | println("BookData1 === BookData2 -> ${bookData1 === bookData2}") // prints false 66 | 67 | println("Book1: ${book1.toString()}") // prints Book1: chapter2OOP.Book@1d44bcfa 68 | println("Book1Data: ${bookData1.toString()}") // prints Book1Data: BookData(name=LOTR, age=54) 69 | 70 | 71 | } 72 | 73 | //🔥🔥 To exclude a property from the generated implementations, declare it inside the class body: 74 | data class PersonData(val name: String) { 75 | var age: Int = 0 76 | 77 | } 78 | 79 | // 🔥 variables with default values can be omitted when data class is instantiated 80 | data class CarData( 81 | var type: Int? = 0, 82 | var manifacturer: String, 83 | var model: String 84 | ) { 85 | fun type(type: Int): CarData = apply { this.type = type } 86 | 87 | } 88 | 89 | 90 | /** 91 | * This example is from Stackoverflow 92 | * 93 | * definition 1 94 | 95 | data class Person (var name:String, var age:Int) 96 | definition 2 97 | 98 | class Person (var name:String, var age:Int) 99 | definition 3 100 | 101 | class Person (){ 102 | var name:String = "" 103 | var age:Int = 1 104 | } 105 | 106 | Difference in equals, hashCode, & toString 107 | the most important difference between definition 1 and definitions 2 & 3 is that in definition 1, 108 | the equals, hashcode and toString methods are overridden for you: 109 | 110 | equals and hashCode methods test for structural equality 111 | toString method returns a nice, human-friendly string 112 | Code example: 113 | 114 | NOTE: in Kotlin, the == operator calls an object's .equals() method. see operator overloading 115 | on kotlinlang.org for more info. 116 | 117 | data class Person1 (var name:String, var age:Int) 118 | class Person2 (var name:String, var age:Int) 119 | 120 | @Test fun test1() 121 | { 122 | val alice1 = Person1("Alice", 22) 123 | val alice2 = Person1("Alice", 22) 124 | val bob = Person1("bob", 23) 125 | 126 | // alice1 and alice2 are structurally equal, so this returns true. 127 | println(alice1 == alice2) // true 128 | 129 | // alice1 and bob are NOT structurally equal, so this returns false. 130 | println(alice1 == bob) // false 131 | 132 | // the toString method for data classes are generated for you. 133 | println(alice1) // Person1(name=Alice, age=22) 134 | } 135 | 136 | @Test fun test2() 137 | { 138 | val alice1 = Person2("Alice", 22) 139 | val alice2 = Person2("Alice", 22) 140 | val bob = Person2("bob", 23) 141 | 142 | // even though alice1 and alice2 are structurally equal, this returns false. 143 | println(alice1 == alice2) // false 144 | println(alice1 == bob) // false 145 | 146 | // the toString method for normal classes are NOT generated for you. 147 | println(alice1) // Person2@1ed6993a 148 | } 149 | Difference in constructors 150 | another difference between definitions 1 & 2 and definition 3 is that: 151 | 152 | definitions 1 & 2 both have a constructor that takes 2 parameters 153 | definition 3 only has a no argument constructor that assigns default values to the class members. 154 | Code example: 155 | 156 | data class Person1 (var name:String, var age:Int) 157 | class Person2 (var name:String, var age:Int) 158 | class Person3 () 159 | { 160 | var name:String = "" 161 | var age:Int = 1 162 | } 163 | 164 | @Test fun test3() 165 | { 166 | Person1("alice",22) // OK 167 | Person2("bob",23) // OK 168 | Person3("charlie",22) // error 169 | 170 | Person1() // error 171 | Person2() // error 172 | Person3() // OK 173 | } 174 | The copy method 175 | Finally, another difference between definition 1 and definitions 2 & 3 is that in definition 1, 176 | a copy method is generated for it. Here's an example of how it can be used: 177 | 178 | val jack = Person1("Jack", 1) 179 | val olderJack = jack.copy(age = 2) 180 | 181 | // jack.age = 1 182 | // olderJack.age = 2 183 | */ 184 | 185 | class Book(val name: String, val age: Int) 186 | 187 | data class BookData(val name: String, val age: Int) 188 | 189 | 190 | 191 | 192 | -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter2OOP/Tutorial2_9DataClasses2Inheritance.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter2OOP 2 | 3 | fun main() { 4 | 5 | /* 6 | Overloading method getName in Corn and Vegetable let's these object to call 7 | one for the static type(type on left side of the equation val milkCorn:Vegetable = Corn()) 8 | 9 | */ 10 | val apple = Apple(0, "🍏") 11 | println("Apple name: ${apple.name}") 12 | 13 | val corn: Corn = Corn(0, "🌽") 14 | println("Corn name: ${corn.name}") 15 | corn.name = "🌽🌽" 16 | corn.printName() 17 | 18 | val milkCorn: Vegetable = corn 19 | corn.id = 20 20 | println("MilkCorn name: ${milkCorn.name}") 21 | milkCorn.name = "🌽x3" 22 | val m = milkCorn as Corn 23 | println("MilkCorn Changed name: ${milkCorn.name}, m id: ${m.id}") 24 | corn.printName() 25 | 26 | 27 | /* 28 | Prints 29 | Apple name: 🍏 30 | Corn name: 🌽 31 | Name: 🌽🌽, super name: 🌽 32 | MilkCorn name: 🌽🌽 33 | MilkCorn Changed name: 🌽x3, m id: 20 34 | Name: 🌽x3, super name: 🌽 35 | */ 36 | 37 | val vegetableList = listOf(corn) 38 | 39 | vegetableList.forEach { 40 | print("Vegetable name: $it") 41 | } 42 | /* 43 | Prints: 44 | Vegetable name: Corn(id=20, name=🌽x3) 45 | 46 | */ 47 | } 48 | 49 | open class Fruit { 50 | open var name: String = "fruit" 51 | } 52 | 53 | /* 54 | public class Fruit { 55 | @NotNull 56 | private final String name = "fruit"; 57 | 58 | @NotNull 59 | public String getName() { 60 | return this.name; 61 | } 62 | } 63 | */ 64 | 65 | data class Apple(val id: Int, override var name: String) : Fruit() { 66 | 67 | } 68 | /* 69 | public final class Apple extends Fruit { 70 | 71 | private final int id; 72 | @NotNull 73 | private String name; 74 | 75 | public Apple(int id, @NotNull String name) { 76 | super(); 77 | this.id = id; 78 | this.name = name; 79 | } 80 | 81 | public final int getId() { 82 | return this.id; 83 | } 84 | 85 | @NotNull 86 | public String getName() { 87 | return this.name; 88 | } 89 | 90 | public void setName(@NotNull String var1) { 91 | Intrinsics.checkNotNullParameter(var1, ""); 92 | this.name = var1; 93 | } 94 | 95 | 96 | 97 | public final int component1() { 98 | return this.id; 99 | } 100 | 101 | @NotNull 102 | public final String component2() { 103 | return this.getName(); 104 | } 105 | 106 | } 107 | */ 108 | 109 | open class Vegetable(open var name: String) 110 | /* 111 | public class Vegetable { 112 | @NotNull 113 | private final String name; 114 | 115 | @NotNull 116 | public String getName() { 117 | return this.name; 118 | } 119 | 120 | public Vegetable(@NotNull String name) { 121 | Intrinsics.checkNotNullParameter(name, "name"); 122 | super(); 123 | this.name = name; 124 | } 125 | } 126 | */ 127 | 128 | class Bean(id: Int, name:String) : Vegetable(name) { 129 | 130 | fun printName() { 131 | println("Name: $name, super name: ${super.name}") 132 | } 133 | } 134 | 135 | /* 136 | public final class Bean extends Vegetable { 137 | 138 | public Bean(int id, @NotNull String name) { 139 | super(name); 140 | } 141 | 142 | public final void printName() { 143 | String var1 = "Name: " + this.getName() + ", super name: " + super.getName(); 144 | boolean var2 = false; 145 | System.out.println(var1); 146 | } 147 | 148 | 149 | } 150 | */ 151 | 152 | data class Corn(var id: Int, override var name: String) : Vegetable(name) { 153 | 154 | fun printName() { 155 | println("Name: $name, super name: ${super.name}") 156 | } 157 | 158 | } 159 | 160 | /* 161 | public final class Corn extends Vegetable { 162 | private final int id; 163 | @NotNull 164 | private String name; 165 | 166 | public Corn(int id, @NotNull String name) { 167 | Intrinsics.checkNotNullParameter(name, "name"); 168 | super(name); 169 | this.id = id; 170 | this.name = name; 171 | } 172 | 173 | public final void printName() { 174 | String var1 = "Name: " + this.getName() + ", super name: " + super.getName(); 175 | boolean var2 = false; 176 | System.out.println(var1); 177 | } 178 | 179 | public final int getId() { 180 | return this.id; 181 | } 182 | 183 | @NotNull 184 | public String getName() { 185 | return this.name; 186 | } 187 | 188 | public void setName(@NotNull String var1) { 189 | Intrinsics.checkNotNullParameter(var1, ""); 190 | this.name = var1; 191 | } 192 | 193 | public final int component1() { 194 | return this.id; 195 | } 196 | 197 | @NotNull 198 | public final String component2() { 199 | return this.getName(); 200 | } 201 | 202 | @NotNull 203 | public final Corn copy(int id, @NotNull String name) { 204 | Intrinsics.checkNotNullParameter(name, "name"); 205 | return new Corn(id, name); 206 | } 207 | 208 | // $FF: synthetic method 209 | public static Corn copy$default(Corn var0, int var1, String var2, int var3, Object var4) { 210 | if ((var3 & 1) != 0) { 211 | var1 = var0.id; 212 | } 213 | 214 | if ((var3 & 2) != 0) { 215 | var2 = var0.getName(); 216 | } 217 | 218 | return var0.copy(var1, var2); 219 | } 220 | 221 | @NotNull 222 | public String toString() { 223 | return "Corn(id=" + this.id + ", name=" + this.getName() + ")"; 224 | } 225 | 226 | public int hashCode() { 227 | int var10000 = this.id * 31; 228 | String var10001 = this.getName(); 229 | return var10000 + (var10001 != null ? var10001.hashCode() : 0); 230 | } 231 | 232 | public boolean equals(@Nullable Object var1) { 233 | if (this != var1) { 234 | if (var1 instanceof Corn) { 235 | Corn var2 = (Corn)var1; 236 | if (this.id == var2.id && Intrinsics.areEqual(this.getName(), var2.getName())) { 237 | return true; 238 | } 239 | } 240 | 241 | return false; 242 | } else { 243 | return true; 244 | } 245 | } 246 | } 247 | */ 248 | 249 | 250 | abstract class Resource { 251 | abstract var id: Long 252 | abstract var location: String 253 | } 254 | 255 | data class Article( 256 | override var id: Long = 0, 257 | override var location: String = "", 258 | var isbn: String 259 | ) : Resource() -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter2OOP/model/ConstructorModels.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter2OOP.model 2 | 3 | class MyClass { 4 | 5 | // property (data member) 6 | private var name: String = "Tutorials.point" 7 | 8 | // member function 9 | fun printMe() { 10 | println("You are at the best Learning website Named-" + name) 11 | } 12 | } 13 | 14 | class MyObject(name: String) { 15 | 16 | val objName = name 17 | 18 | init { 19 | println("Name: $name") 20 | } 21 | } 22 | /* 23 | public final class MyObject { 24 | @NotNull 25 | private final String objName; 26 | 27 | @NotNull 28 | public final String getObjName() { 29 | return this.objName; 30 | } 31 | 32 | public MyObject(@NotNull String name) { 33 | Intrinsics.checkNotNullParameter(name, "name"); 34 | super(); 35 | this.objName = name; 36 | String var2 = "Name: " + name; 37 | boolean var3 = false; 38 | System.out.println(var2); 39 | } 40 | } 41 | */ 42 | 43 | 44 | // INFO CONSTRUCTOR 45 | // age parameter can be NULL 46 | // 🔥 INFO If the primary constructor does not have any annotations or visibility modifiers, 47 | // the constructor keyword can be omitted: 48 | //🔥 INFO If the constructor has annotations or visibility modifiers, 49 | // the constructor keyword is required, and the modifiers go before it: 50 | class Person constructor(var firstName: String, val lastName: String, val age: Int?) { 51 | //... 52 | } 53 | 54 | // INFO INIT BLOCK 55 | 56 | // INFO During an instance initialization, 57 | // the initializer blocks are executed in the 🔥 SAME ORDER as they appear in the class body, 58 | // interleaved with the property initializers: 59 | 60 | class InitOrderDemo(name: String) { 61 | 62 | // 1st 63 | val firstProperty = "First property: $name".also(::println) 64 | 65 | // 2nd 66 | init { 67 | println("First initializer block that prints ${name}") 68 | } 69 | 70 | // 3rd 71 | val secondProperty = "Second property: ${name.length}".also(::println) 72 | 73 | // 4th 74 | init { 75 | println("Second initializer block that prints ${name.length}") 76 | } 77 | } 78 | 79 | 80 | class MYNewClass(private val name: String) 81 | 82 | /* 83 | public final class MYNewClass { 84 | private final String name; 85 | 86 | public MYNewClass(@NotNull String name) { 87 | Intrinsics.checkNotNullParameter(name, "name"); 88 | super(); 89 | this.name = name; 90 | } 91 | } 92 | */ 93 | 94 | 95 | // INFO 🔥 Assign Constructor parameters to class fields 96 | // INFO 🔥 ⚠️ ️Prefixing your constructor arguments with val or var is not a must 97 | // if you don't want the getter (or setter if you use var) to be generated, you can always do the following: 98 | 99 | class Person2(firstName: String, lastName: String, howOld: Int?) { 100 | 101 | private val name: String 102 | private val age: Int? 103 | 104 | init { 105 | // IMPORTANT 🔥🔥🔥 Properties must be initialized in constructor(this could also be inside init{}), 106 | // or be abstract or lateinit 107 | this.name = "$firstName,$lastName" 108 | this.age = howOld 109 | } 110 | 111 | fun getName(): String = this.name 112 | fun getAge(): Int? = this.age 113 | } 114 | 115 | class Customer(name: String) { 116 | val customerKey = name.uppercase() 117 | } 118 | 119 | // INFO 🔥 Constructors with val/var properties 120 | class User(val id: Long, email: String) { 121 | 122 | val hasEmail = email.isNotBlank() //email can be accessed here 123 | 124 | init { 125 | //email can be accessed here 126 | println("Email $email") 127 | } 128 | 129 | fun getEmail() { 130 | // 🔥 email can't be accessed here 131 | } 132 | } 133 | 134 | // INFO SECONDARY CONSTRUCTORS 135 | 136 | class Person3 { 137 | 138 | // INFO Secondary Constructor 139 | constructor(firstName: String, lastName: String) { 140 | println("😳 Secondary constructor of Person3") 141 | } 142 | } 143 | 144 | class Person4 constructor(val firstName: String, val lastName: String, val age: Int?) { 145 | 146 | // INFO 🔥 Secondary Constructor that calls Primary Constructor 147 | constructor(firstName: String, lastName: String) : this(firstName, lastName, null) { 148 | println("😳 Secondary constructor of Person4") 149 | } 150 | } 151 | 152 | /* 153 | 154 | public final class Person4 { 155 | 156 | @NotNull 157 | private final String firstName; 158 | @NotNull 159 | private final String lastName; 160 | @Nullable 161 | private final Integer age; 162 | 163 | @NotNull 164 | public final String getFirstName() { 165 | return this.firstName; 166 | } 167 | 168 | @NotNull 169 | public final String getLastName() { 170 | return this.lastName; 171 | } 172 | 173 | @Nullable 174 | public final Integer getAge() { 175 | return this.age; 176 | } 177 | 178 | // 🔥 Primary Constructor 179 | public Person4(@NotNull String firstName, @NotNull String lastName, @Nullable Integer age) { 180 | super(); 181 | this.firstName = firstName; 182 | this.lastName = lastName; 183 | this.age = age; 184 | } 185 | 186 | // 🔥 Secondary Constructor 187 | public Person4(@NotNull String firstName, @NotNull String lastName) { 188 | this(firstName, lastName, (Integer)null); 189 | String var3 = "\ud83d\ude33 Secondary constructor of Person4"; 190 | System.out.println(var3); 191 | } 192 | } 193 | 194 | */ 195 | 196 | class Constructors { 197 | 198 | constructor(i: Int) { 199 | println("😎 Secondary Constructor of Constructors class") 200 | } 201 | 202 | // INFO 🔥 init block is called before secondary constructor 203 | init { 204 | println("Init block of Constructors class") 205 | } 206 | 207 | } 208 | 209 | class Auto(age: Int) { 210 | 211 | 212 | init { 213 | println("🥳 Init block of Auto class with $age") 214 | } 215 | 216 | constructor(name: String) : this(0) { 217 | println("🚗🚗 Secondary Constructor of Auto with type $name") 218 | } 219 | 220 | constructor(i: Int, name: String) : this(i) { 221 | println("🚙 Secondary Constructor of Auto class with type $name and i $i") 222 | } 223 | } 224 | 225 | class SomeObject { 226 | 227 | constructor(age: Int) 228 | 229 | constructor(age: Int, name: String):this(age) 230 | 231 | } 232 | /* 233 | 234 | public final class Auto { 235 | 236 | public Auto(int age) { 237 | String var2 = "\ud83e\udd73 Init block of Auto class with " + age; 238 | System.out.println(var2); 239 | } 240 | 241 | public Auto(@NotNull String name) { 242 | this(0); 243 | String var2 = "\ud83d\ude97\ud83d\ude97 Secondary Constructor of Auto with type " + name; 244 | System.out.println(var2); 245 | } 246 | 247 | public Auto(int i, @NotNull String name) { 248 | this(i); 249 | String var3 = "\ud83d\ude99 Secondary Constructor of Auto class with type " + name + " and i " + i; 250 | System.out.println(var3); 251 | } 252 | } 253 | 254 | */ -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter2OOP/model/InheritanceModels.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter2OOP.model 2 | 3 | 4 | open class Base(val name: String) { 5 | 6 | init { 7 | // INFO called 2nd 8 | println("Initializing Base") 9 | } 10 | 11 | open val size: Int = 12 | // INFO called 3rd 13 | name.length.also { println("Initializing size in Base: $it") } 14 | } 15 | 16 | class Derived( 17 | name: String, 18 | val lastName: String 19 | ) 20 | // INFO called 1st 21 | : Base(name.capitalize().also { println("Argument for Base: $it") }) { 22 | 23 | init { 24 | // INFO called 4th 25 | println("Initializing Derived") 26 | } 27 | 28 | override val size: Int = 29 | // INFO called 5th 30 | (super.size + lastName.length).also { println("Initializing size in Derived:$it") } 31 | } 32 | 33 | 34 | open class BaseClassA { 35 | 36 | open fun f() { 37 | print("BaseClassA") 38 | } 39 | 40 | fun a() { 41 | print("a") 42 | } 43 | } 44 | 45 | interface InterfaceB { 46 | 47 | var name: String 48 | 49 | // interface members are 'open' by default 50 | fun f() { 51 | print("InterfaceB") 52 | } 53 | 54 | fun b() { 55 | print("b") 56 | } 57 | 58 | // INFO f(), and b() methods are not ABSTRACT but foo is abstract and must be 59 | // overridden on class that implement InterfaceB interface 60 | fun foo() 61 | } 62 | 63 | /* 64 | public interface InterfaceB { 65 | @NotNull 66 | String getName(); 67 | 68 | void f(); 69 | 70 | void b(); 71 | 72 | void foo(); 73 | 74 | // 🔥 This class gets generated because of methods with body in Interface in Java 75 | public static final class DefaultImpls { 76 | public static void f(@NotNull InterfaceB $this) { 77 | String var1 = "InterfaceB"; 78 | boolean var2 = false; 79 | System.out.print(var1); 80 | } 81 | 82 | public static void b(@NotNull InterfaceB $this) { 83 | String var1 = "b"; 84 | boolean var2 = false; 85 | System.out.print(var1); 86 | } 87 | } 88 | } 89 | */ 90 | 91 | class C() : BaseClassA(), InterfaceB { 92 | 93 | override fun foo() { 94 | TODO("not implemented") //To change body of created functions use File | Settings | File Templates. 95 | } 96 | 97 | private var className = "" 98 | 99 | override var name: String 100 | get() = className 101 | set(value) { 102 | className = value 103 | } 104 | 105 | // INFO only invokes methods called with super 106 | // 🔥 The compiler requires f() to be overridden because it's open fun in BaseClassA 107 | override fun f() { 108 | super.f() // call to BaseClassA.f() 109 | super.f() // call to InterfaceB.f() } 110 | } 111 | 112 | } 113 | 114 | class InterfaceTestClass(override var name: String) : InterfaceB { 115 | 116 | // 🔥 This should be implemented because it has no body in Interface 117 | override fun foo() { 118 | TODO("not implemented") //To change body of created functions use File | Settings | File Templates. 119 | } 120 | } 121 | 122 | /* 123 | public final class InterfaceTestClass implements InterfaceB { 124 | @NotNull 125 | private String name; 126 | 127 | public InterfaceTestClass(@NotNull String name) { 128 | Intrinsics.checkNotNullParameter(name, "name"); 129 | super(); 130 | this.name = name; 131 | } 132 | 133 | 134 | public void foo() { 135 | String var1 = "not implemented"; 136 | boolean var2 = false; 137 | throw (Throwable)(new NotImplementedError("An operation is not implemented: " + var1)); 138 | } 139 | 140 | @NotNull 141 | public String getName() { 142 | return this.name; 143 | } 144 | 145 | public void setName(@NotNull String var1) { 146 | Intrinsics.checkNotNullParameter(var1, ""); 147 | this.name = var1; 148 | } 149 | 150 | public void f() { 151 | InterfaceB.DefaultImpls.f(this); 152 | } 153 | 154 | public void b() { 155 | InterfaceB.DefaultImpls.b(this); 156 | } 157 | } 158 | */ 159 | 160 | 161 | // INFO Super with Constructor 162 | // Parent class 163 | open class Computer( 164 | val name: String, 165 | val brand: String 166 | ) { 167 | 168 | open var age: Double = 0.0 169 | 170 | open fun start() { 171 | 172 | } 173 | } 174 | 175 | // Child class (initializes the parent class) 176 | class Laptop : Computer { 177 | 178 | override var age: Double = 0.0 179 | get() = super.age 180 | set(value) { 181 | field = value + 2 182 | } 183 | 184 | 185 | val batteryLife: Double 186 | 187 | // Calls super() to initialize the Parent class 188 | constructor(name: String, brand: String, batteryLife: Double) : super(name, brand) { 189 | this.batteryLife = batteryLife 190 | } 191 | 192 | // Calls another constructor (which calls super()) 193 | constructor(name: String, brand: String) : this(name, brand, 0.0) { 194 | 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter2OOP/model/InheritanceModels2.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter2OOP.model 2 | 3 | fun main() { 4 | val employee = Employee() 5 | println(employee.baseSalary) // 30000.0 6 | 7 | val programmer = Programmer() 8 | println(programmer.baseSalary) // 50000.0 9 | } 10 | 11 | // 🔥 INFO Overriding Properties 12 | 13 | abstract class Account(initialAmount: Double) { 14 | open var baseAmount = initialAmount 15 | } 16 | 17 | class PrivateAccount(initial: Double) : Account(initial) { 18 | fun displayValue() { 19 | println("PrivateAccount Parent super.baseAmount: ${super.baseAmount}, derived: $baseAmount") 20 | } 21 | } 22 | 23 | class BusinessAccount(base: Double) : Account(base) { 24 | 25 | //🔥 INFO When you override a property or a member function of a super class, 26 | // the super class implementation 27 | // is shadowed by the child class implementation. 28 | // You can access the properties and functions of the super class using super() keyword. 29 | 30 | override var baseAmount: Double = 0.0 31 | set(value) { 32 | field = value * 3 33 | } 34 | 35 | 36 | fun displayValue() { 37 | println("BusinessAccount Parent super.baseAmount: ${super.baseAmount}, derived: $baseAmount") 38 | } 39 | } 40 | 41 | // Overriding a value returns a different value than parent has 42 | class UnionAccount(override var baseAmount: Double) : Account(baseAmount) { 43 | 44 | fun setBase(amount: Double) { 45 | baseAmount = amount 46 | } 47 | 48 | var unionProperty: String = "" 49 | set(value) { 50 | field = "$value $baseAmount" 51 | } 52 | 53 | fun displayValue() { 54 | println("UnionAccount Parent super.baseAmount: ${super.baseAmount}, derived: $baseAmount") 55 | } 56 | } 57 | 58 | /* 59 | 60 | public final class UnionAccount extends Account { 61 | @NotNull 62 | private String unionProperty; 63 | private double baseAmount; 64 | 65 | public final void setBase(double amount) { 66 | this.setBaseAmount(amount); 67 | } 68 | 69 | @NotNull 70 | public final String getUnionProperty() { 71 | return this.unionProperty; 72 | } 73 | 74 | public final void setUnionProperty(@NotNull String value) { 75 | this.unionProperty = value + ' ' + this.getBaseAmount(); 76 | } 77 | 78 | public final void displayValue() { 79 | String var1 = "UnionAccount Parent super.baseAmount: " + super.getBaseAmount() + ", derived: " + this.getBaseAmount(); 80 | System.out.println(var1); 81 | } 82 | 83 | public double getBaseAmount() { 84 | return this.baseAmount; 85 | } 86 | 87 | public void setBaseAmount(double var1) { 88 | this.baseAmount = var1; 89 | } 90 | 91 | public UnionAccount(double baseAmount) { 92 | super(baseAmount); 93 | this.baseAmount = baseAmount; 94 | this.unionProperty = ""; 95 | } 96 | } 97 | 98 | */ 99 | 100 | open class Employee { 101 | // Use "open" modifier to allow child classes to override this property 102 | open val baseSalary: Double = 30000.0 103 | } 104 | 105 | class Programmer : Employee() { 106 | // Use "override" modifier to override the property of base class 107 | override val baseSalary: Double = 50000.0 108 | } 109 | 110 | interface AnimalBase { 111 | // 🔥 Implementing class MUST override this 112 | var MAX_AGE: Int 113 | 114 | fun makeSound(): String 115 | 116 | fun doMove(): String 117 | } 118 | 119 | class Snake : AnimalBase { 120 | 121 | // Has to be overriden 122 | override var MAX_AGE = 7 123 | 124 | override fun doMove() = "Slithers" 125 | 126 | override fun makeSound() = "Hisses" 127 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter2OOP/mvp/BaseComponents.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter2OOP.mvp 2 | 3 | open class BaseFragment> : Fragment() { 4 | 5 | var presenter: P? = null 6 | 7 | override fun onCreate() { 8 | super.onCreate() 9 | presenter?.attachOutput() 10 | } 11 | 12 | override fun onCreateView() { 13 | super.onCreateView() 14 | println("😍 BaseFragment onCreateView()") 15 | presenter?.attachView(this as V) 16 | } 17 | 18 | override fun onDestroyView() { 19 | super.onDestroyView() 20 | println("😍 BaseFragment onDestroyView()") 21 | presenter?.detachView() 22 | } 23 | 24 | override fun onDestroy() { 25 | super.onDestroy() 26 | println("😍 BaseFragment onDestroy()") 27 | presenter?.onDestroy() 28 | } 29 | } 30 | 31 | abstract class BasePresenter(interactor: I) : BaseContract.IPresenter, 32 | BaseContract.IOutput { 33 | 34 | protected var view: V? = null 35 | 36 | protected var interactor: I? 37 | 38 | init { 39 | this.interactor = interactor 40 | } 41 | 42 | override fun attachOutput() { 43 | if (interactor is BaseInteractor<*>) { 44 | (interactor as? BaseInteractor)?.output = this 45 | } 46 | } 47 | 48 | override fun attachView(view: V) { 49 | this.view = view 50 | println("😅 BasePresenter attachView() view: $view") 51 | } 52 | 53 | override fun detachView() { 54 | println("😅 BasePresenter detachView()") 55 | view = null 56 | } 57 | 58 | override fun onDestroy() { 59 | println("😅 BasePresenter onDestroy()") 60 | interactor?.onDestroy() 61 | interactor = null 62 | } 63 | 64 | 65 | } 66 | 67 | abstract class BaseInteractor : BaseContract.IInteractor { 68 | var output: O? = null 69 | 70 | override fun onDestroy() { 71 | println("🍒 BaseInteractor onDestroy()") 72 | output = null 73 | } 74 | } 75 | 76 | /** 77 | * Mock fragment class to mock Android's Fragment with MVP 78 | */ 79 | open class Fragment { 80 | open fun onCreate() { 81 | println("🔥 Fragment() onCreate()") 82 | } 83 | 84 | open fun onCreateView() { 85 | println("🌽 Fragment() onCreateView()") 86 | } 87 | 88 | open fun onDestroyView() { 89 | println("🎃 Fragment() onDestroyView()") 90 | } 91 | 92 | open fun onDestroy() { 93 | println("🍺 Fragment() onDestroy()") 94 | } 95 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter2OOP/mvp/BaseContract.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter2OOP.mvp 2 | 3 | interface BaseContract { 4 | 5 | interface IView 6 | interface IPresenter { 7 | 8 | fun attachOutput() 9 | fun attachView(view: V) 10 | fun detachView() 11 | fun onDestroy() 12 | } 13 | 14 | interface IInteractor { 15 | fun onDestroy() 16 | } 17 | 18 | interface IOutput 19 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter2OOP/mvp/UserContract.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter2OOP.mvp 2 | 3 | import com.smarttoolfactory.tutorial.chapter2OOP.User 4 | 5 | 6 | interface UserContract { 7 | 8 | interface UserView : BaseContract.IView { 9 | fun displayUser(users: List) 10 | fun displayError(message: String?) 11 | } 12 | 13 | interface UserPresenter : BaseContract.IPresenter { 14 | fun fetchUsers() 15 | } 16 | 17 | interface UserInteractor : BaseContract.IInteractor { 18 | fun fetchUsersFromNetwork() 19 | } 20 | 21 | interface UserOutput : BaseContract.IOutput { 22 | fun onUsersFetched(users: List) 23 | fun onErrorOccurred(message: String) 24 | } 25 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter2OOP/mvp/UserFragmentMain.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter2OOP.mvp 2 | 3 | import com.smarttoolfactory.tutorial.chapter2OOP.mvp.user.UserFragment 4 | 5 | 6 | fun main() { 7 | val userFragment = UserFragment() 8 | userFragment.onCreate() 9 | Thread.sleep(400) 10 | 11 | userFragment.onCreateView() 12 | Thread.sleep(400) 13 | 14 | userFragment.onDestroyView() 15 | Thread.sleep(400) 16 | userFragment.onDestroy() 17 | 18 | println("PROGRAM FINISHED") 19 | } 20 | -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter2OOP/mvp/user/UserFragment.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter2OOP.mvp.user 2 | 3 | 4 | 5 | import com.smarttoolfactory.tutorial.chapter2OOP.User 6 | import com.smarttoolfactory.tutorial.chapter2OOP.mvp.BaseFragment 7 | import com.smarttoolfactory.tutorial.chapter2OOP.mvp.BaseInteractor 8 | import com.smarttoolfactory.tutorial.chapter2OOP.mvp.BasePresenter 9 | import com.smarttoolfactory.tutorial.chapter2OOP.mvp.UserContract 10 | import java.util.* 11 | 12 | class UserFragment : BaseFragment(), 13 | UserContract.UserView { 14 | 15 | init { 16 | presenter = InitUserFragment.initUserModule() 17 | } 18 | 19 | override fun onCreate() { 20 | super.onCreate() 21 | println("😍 UserFragment onCreate()") 22 | } 23 | 24 | override fun onCreateView() { 25 | super.onCreateView() 26 | presenter?.fetchUsers() 27 | } 28 | 29 | override fun displayUser(users: List) { 30 | for (i in users.indices) { 31 | val user = users[i] 32 | println("🔥🔥 UserFragment displayUser() USER: " + user.fullName) 33 | } 34 | } 35 | 36 | override fun displayError(message: String?) {} 37 | 38 | internal object InitUserFragment { 39 | fun initUserModule(): UserContract.UserPresenter { 40 | val userInteractor: UserContract.UserInteractor = UserInteractor() 41 | return UserPresenter(userInteractor) 42 | } 43 | } 44 | } 45 | 46 | 47 | class UserPresenter(interactor: UserContract.UserInteractor) : 48 | BasePresenter(interactor), UserContract.UserPresenter, 49 | UserContract.UserOutput { 50 | 51 | override fun fetchUsers() { 52 | interactor?.fetchUsersFromNetwork() 53 | } 54 | 55 | override fun onUsersFetched(users: List) { 56 | view?.displayUser(users) 57 | } 58 | 59 | override fun onErrorOccurred(message: String) { 60 | view?.displayError(message) 61 | } 62 | } 63 | 64 | class UserInteractor : BaseInteractor(), UserContract.UserInteractor { 65 | 66 | override fun fetchUsersFromNetwork() { 67 | val userList = GetUsers.users 68 | output?.onUsersFetched(userList) 69 | println("🤩 UserInteractor fetchUsersFromNetwork() userList: $userList") 70 | } 71 | 72 | internal object GetUsers { 73 | val users: List 74 | get() { 75 | val userList: MutableList = ArrayList() 76 | val user = User("John", "Smith") 77 | userList.add(user) 78 | return userList 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter3Other/Tutorial3_1Destructuring.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter3Other 2 | 3 | 4 | fun main() { 5 | 6 | // INFO Destructuring Declarations 7 | val account = Account("Standard", 100) 8 | val (name: String, amount: Int) = account 9 | println("Account type: $name, amount: $amount") 10 | 11 | 12 | // Info Returning Two Values from a Function 13 | val (result, status) = function() 14 | 15 | 16 | val map = mapOf("key1" to 1, "key2" to 2) 17 | 18 | for ((key, value) in map) { 19 | // do something with the key and the value 20 | } 21 | 22 | // Info Underscore for unused variables (since 1.1) 23 | val (_:String, _:Int, interest:Float) = account 24 | 25 | 26 | } 27 | 28 | 29 | data class Account(var type: String, var amount: Int, var interest: Float = 5.7f) 30 | 31 | 32 | // Info Returning Two Values from a Function 33 | data class Result(val result: Int, val status: String) 34 | 35 | fun function(): Result { 36 | 37 | val result = 10 38 | val status = "COMPLETED" 39 | 40 | return Result(result, status) 41 | } 42 | 43 | -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter3Other/Tutorial3_2TypeChecks.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter3Other 2 | 3 | 4 | fun main() { 5 | 6 | // Info Cast 7 | var obj: String = ""; 8 | if (obj is String) { 9 | print(obj.length) 10 | } 11 | if (obj !is String) { // same as !(obj is String) print("Not a String") 12 | } else { 13 | println(obj.length) 14 | } 15 | 16 | // INFO "Unsafe" cast operator 17 | // val y: Any? = null 18 | // val x: String = y as String 19 | // println(x) // INFO Throw TypeCastException 20 | 21 | // INFO "Safe" (nullable) cast operator 22 | val a:Any? = null 23 | val b:String? = a as? String 24 | println("a: $a") // prints a: null 25 | 26 | } 27 | 28 | // INFO Smart Casts 29 | fun demo(x: Any) { 30 | if (x is String) { 31 | print(x.length) // x is automatically cast to String } 32 | } 33 | } 34 | 35 | fun testCast(x: Any) { 36 | when (x) { 37 | is Int -> print(x + 1) 38 | is String -> print(x.length + 1) 39 | is IntArray -> print(x.sum()) 40 | } 41 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter3Other/Tutorial3_3Equality.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter3Other 2 | 3 | fun main() { 4 | 5 | println("Primitive Equality") 6 | val int1 = 10 7 | val int2 = 10 8 | 9 | println(int1 == int2) // true 10 | println(int1.equals(int2)) // true 11 | println(int1 === int2) // true 12 | 13 | println("Class Equality") 14 | 15 | class Employee(val name: String) 16 | 17 | val emp1 = Employee("John") 18 | val emp2 = Employee("John") 19 | 20 | println(emp1 == emp2) //false 21 | println(emp1.equals(emp2)) //false 22 | println(emp1 === emp2) //false 23 | 24 | println(emp1.name == emp2.name) //true 25 | println(emp1.name.equals(emp2.name)) //true 26 | println(emp1.name === emp2.name) //true 27 | 28 | println("Data Class Equality") 29 | 30 | data class EmployeeData(val name: String) 31 | 32 | val empData1 = EmployeeData("John") 33 | val empData2 = EmployeeData("John") 34 | 35 | println(empData1 == empData2) //true 36 | println(empData1.equals(empData2)) //true 37 | println(empData1 === empData2) //false 38 | 39 | println(empData1.name == empData2.name) //true 40 | println(empData1.name.equals(empData2.name)) //true 41 | println(empData1.name === empData2.name) //true 42 | 43 | println("Numbers") 44 | val number1 = Integer(10) // create new instance 45 | val number2 = Integer(10) // create new instance 46 | val number3 = number1 47 | 48 | // check if number1 and number2 are Structural equality 49 | println(number1 == number2) // prints true 50 | 51 | // check if number1 and number2 points to the same object 52 | // in other words, checks for Referential equality 53 | println(number1 === number2) // prints false 54 | 55 | // check if number1 and number3 points to the same object 56 | println(number1 === number3) // prints true 57 | 58 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter3Other/Tutorial3_4NullSafety.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter3Other 2 | 3 | fun main() { 4 | 5 | val a: String = "abc" 6 | // a = null // compilation error 7 | 8 | var b: String? = "abc" 9 | b = null // ok print(b) 10 | // val l = b.length // WARNING error: variable 'b' can be null 11 | 12 | // INFO Checking for null in conditions 13 | val l = if (b != null) b.length else -1 14 | 15 | 16 | val c: String? = "Kotlin" 17 | if (c != null && c.length > 0) { 18 | print("String of length ${c.length}") 19 | } else { 20 | print("Empty string") 21 | } 22 | 23 | 24 | val notNullString = "Kotlin" 25 | val nullableString: String? = null 26 | println(nullableString?.length) 27 | println(notNullString?.length) // Unnecessary safe call 28 | 29 | // INFO Safe Calls with Let 30 | val listWithNulls: List = listOf("Kotlin", null) 31 | for (item in listWithNulls) { 32 | item?.let { println(it) } // prints Kotlin and ignores null 33 | } 34 | 35 | // BaseClassA safe call can also be placed on the left side of an assignment. 36 | // Then, if one of the receivers in the safe calls chain is null, 37 | // the assignment is skipped, and the expression on the right is not evaluated at all: 38 | 39 | // If either `person` or `person.department` is null, the function is not called: 40 | // person?.department?.head = managersPool.getManager() 41 | 42 | // INFO Elvis Operator 43 | val myInt: Int = if (b != null) b.length else -1 44 | val myInt2 = b?.length ?: -1 45 | 46 | // INFO The !! Operator 47 | // val nullableInt = b!!.length // Throws NPE since b is NULL 48 | 49 | // INFO Safe Casts 50 | // Regular casts may result into a ClassCastException if the object is not of the target type. 51 | // Another option is to use safe casts that return null if the attempt was not successful: 52 | val randomVal: Any = 3 53 | val aInt: Int? = randomVal as? Int 54 | 55 | // INFO Collections of Nullable Type 56 | val nullableList: List = listOf(1, 2, null, 4) 57 | // Filters out the null elements 58 | val intList: List = nullableList.filterNotNull() 59 | 60 | 61 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter3Other/Tutorial3_5Exceptions.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter3Other 2 | 3 | import java.io.* 4 | import java.lang.Integer.parseInt 5 | 6 | fun main() { 7 | 8 | try { 9 | // some code 10 | } catch (e: Exception) { 11 | // handler(if finally is available catch is optional) 12 | } finally { 13 | // optional finally block 14 | } 15 | 16 | 17 | // INFO Try is an expression 18 | val a: Int? = try { 19 | parseInt("5") 20 | } catch (e: NumberFormatException) { 21 | null 22 | } 23 | 24 | // INFO Checked Exceptions 25 | 26 | val stringBuilder = StringBuilder() 27 | stringBuilder.append(charArrayOf('a', 'b', 'c'), 1, 2) 28 | 29 | // INFO Checked Exception with File IO 30 | try { 31 | 32 | val homePath = System.getProperty("user.home") 33 | 34 | // Throws FileNotFoundException 35 | File("$homePath/desktop/test.txt") 36 | .writer().use { 37 | it.write("Hello World") 38 | } 39 | 40 | } catch (e: FileNotFoundException) { 41 | println("Exception occurred: ${e.message}") 42 | } 43 | 44 | // INFO Nothing Type 45 | val person = Person(null) 46 | // Throws IllegalArgumentException: Name required since name is NULL 47 | // val s = person.name ?: throw IllegalArgumentException("Name required") 48 | 49 | val test = person.name ?: fail("Name required") 50 | println(test) // 'test' is known to be initialized at this point 51 | 52 | 53 | val x = null // 'x' has type `Nothing?` 54 | val l = listOf(null) // 'l' has type `List 55 | 56 | } 57 | 58 | 59 | data class Person(val name: String?) 60 | 61 | fun fail(message: String): Nothing { 62 | throw IllegalArgumentException(message) 63 | } 64 | -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter3Other/Tutorial3_6Annotation.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter3Other 2 | 3 | -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter3Other/Tutorial3_7Reflection.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter3Other 2 | 3 | fun main() { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter3Other/Tutorial3_8FunctionLiteralsWithReceiver.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter3Other 2 | 3 | fun main() { 4 | 5 | // INFO 🔥 High Order Function 6 | 7 | val test = createString(5, { 8 | println("createTest() $it") 9 | it * 2 10 | }) 11 | 12 | println("test: $test") 13 | 14 | val test2 = createString(4, { 15 | it / 2 16 | }) 17 | 18 | println("Test2 $test2") 19 | 20 | 21 | // {} = block: (StringBuilder) -> Unit 22 | val stringFromHighOrder = createStringFromStringBuilder({ 23 | it.append(4) 24 | it.append("Hello") 25 | }) 26 | 27 | 28 | val stringFromHighOrder2 = createStringFromStringBuilder(lambdaString("Hello", "World")) 29 | println("stringFromHighOrder2: $stringFromHighOrder2") 30 | 31 | println("stringFromHighOrder $stringFromHighOrder") 32 | 33 | 34 | // INFO 🔥 Function Literal with Receiver 35 | 36 | // Function Literal with Receiver 37 | val stringFromLiteralWithReceiver = createStringWithLiteral({ 38 | append(4) 39 | append("hello") 40 | }) 41 | 42 | 43 | // Function Literal with Receiver without Parenthesis 44 | val stringFromLiteralWithReceiver2 = createStringWithLiteral { 45 | 46 | //here we're in the context of a `StringBuilder` 47 | append(4) 48 | append("hello") 49 | // functions inside {} are basically call to sb.block() in function body 50 | } 51 | println("stringFromLiteralWithReceiver2: $stringFromLiteralWithReceiver2") 52 | 53 | // INFO 🔥 Extension Function Literal with Receiver 54 | val sb = StringBuilder() 55 | val sbNew = sb.extra({ 56 | 57 | }) 58 | 59 | sb.extra { 60 | 61 | } 62 | 63 | val ch = sbNew.extra2(3) { 64 | val num = it * 2 65 | num 66 | } 67 | 68 | } 69 | 70 | // INFO 🔥 High Order Functions 71 | fun createString(value: Int, block: (Int) -> Int): String { 72 | return block(value * value).toString() 73 | } 74 | 75 | fun createStringFromStringBuilder(block: (StringBuilder) -> Unit): String { 76 | val sb = StringBuilder() 77 | block(sb) // 🔥 This CAN NOT be sb.block() 78 | return sb.toString() 79 | } 80 | 81 | // Lambda function to pass as parameter to high-order function 82 | fun lambdaString(vararg texts: String): (StringBuilder) -> Unit = { 83 | texts.forEach { text -> 84 | it.append(text) 85 | } 86 | 87 | 88 | } 89 | 90 | // INFO 🔥 Function Literal with Receiver 91 | // StringBuilder.() sets this function as extension function of StringBuilder class 92 | // receiver is defined as StringBuilder 93 | fun createStringWithLiteral(block: StringBuilder.() -> Unit): String { 94 | 95 | val sb = StringBuilder() // create the receiver object 96 | sb.block() // 🔥 This can also be block(sb) 97 | 98 | // pass the receiver object to the lambda 99 | 100 | return sb.toString() 101 | } 102 | 103 | 104 | 105 | // INFO 🔥 Extension Function Literal with Receiver 106 | /** 107 | * This function is extension function of [StringBuilder] class by [StringBuilder]. before function name. 108 | * 109 | * It takes a block parameter which is a function literal receiver which returns StringBuilder instance 110 | * in function implementation 111 | */ 112 | fun StringBuilder.extra(block: StringBuilder.() -> Unit): StringBuilder { 113 | block() 114 | return this 115 | } 116 | 117 | fun StringBuilder.extra2(value: Int, block: (Int) -> Int): StringBuilder { 118 | block(value) 119 | return this 120 | } 121 | 122 | -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter4Functions/Tutorial4_1HigOrderFunctions.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter4Functions 2 | 3 | fun main() { 4 | 5 | // INFO 🔥 High-order functions 6 | val sum = arithmeticOperation(3, 4) { x, y -> 7 | x + y 8 | } 9 | 10 | println("Sum: $sum") // 7 11 | 12 | val multiply = arithmeticOperation(3, 4) { x, y -> 13 | x * y 14 | } 15 | 16 | println("Multiply: $multiply") // 12 17 | 18 | 19 | // INFO 🔥 High-order functions 20 | val bigger = compare(2, 3) { x, y -> 21 | x > y 22 | } 23 | 24 | val list = listOf(1.1, 5.3, 3.4) 25 | list.max { 26 | println("MAX double: $it") 27 | } 28 | 29 | // INFO 🔥 High-order functions 30 | // This function uses lambda 31 | val compareLambda: (Int, Int) -> Boolean = { x, y -> 32 | x > y 33 | } 34 | 35 | val bigger2 = compare(2, 3, compareLambda) 36 | println("Bigger: $bigger, Bigger2 $bigger2") 37 | 38 | 39 | val strFirst = compareStrings("Zeta", "Alpha") { x, y -> 40 | x.first() > y.first() 41 | } 42 | println("strFirst: $strFirst") 43 | 44 | val strLength = compareStrings("Zeta", "Alpha") { x, y -> 45 | x.length > y.length 46 | } 47 | println("strLength: $strLength") 48 | 49 | // lengthCompare(): (String, String) -> Boolean = { x, y -> x.length > y.length } 50 | val strLength2 = compareStrings("Alpha", "Zeta", lengthCompare()) 51 | println("strLength2: $strLength2") 52 | 53 | 54 | // INFO 🔥 List High-order function 55 | 56 | val items = listOf(1, 2, 3, 4, 5) 57 | 58 | // Lambdas are code blocks enclosed in curly braces. 59 | val result: Int = items.fold(0, { 60 | 61 | // When a lambda has parameters, they go first, followed by '->' 62 | // acc: R, nextElement: T R: Int, T: Int 63 | acc: Int, nextElement: Int -> 64 | 65 | print("acc = $acc, i = $nextElement, ") 66 | val result: Int = acc + nextElement 67 | println("result = $result") 68 | // The last expression in a lambda is considered the return value: 69 | result 70 | }) 71 | 72 | 73 | // Parameter types in a lambda are optional if they can be inferred: 74 | val joinedToString = items.fold("Elements:", { acc, i -> "$acc $i" }) 75 | 76 | // Function references can also be used for higher-order function calls: 77 | val product = items.fold(1, Int::times) 78 | 79 | // High order function takes String and (Int) -> Int lambda function as parameter 80 | // Returns String to int or predefined result of lambda function 81 | // INFO 🔥 High-order function takes a LAMBDA function 82 | highOrderFun("3", lambdaFun()) 83 | 84 | // INFO 🔥 High-order function takes a REGULAR function that returns LAMBDA function 85 | highOrderFun("3", nonLambdaFunction()) 86 | 87 | 88 | val condition1 = 2 > 3 89 | 90 | // INFO 🔥 High-order function 91 | // this high-order function gets condition1 as Boolean, action1:() -> Unit and action2:()->Unit 92 | runWithCondition( 93 | condition1, 94 | { 95 | println("Action1 Invoked") 96 | }, 97 | lambdaAction2() 98 | ) 99 | 100 | } 101 | 102 | fun arithmeticOperation(num1: Int, num2: Int, predicate: (Int, Int) -> Int): Int { 103 | return predicate(num1, num2) 104 | } 105 | 106 | // INFO 🔥 High-order function 107 | fun compare(num1: Int, num2: Int, action: (Int, Int) -> Boolean): Boolean { 108 | return action(num1, num2) 109 | } 110 | 111 | // INFO 🔥 High-order function 112 | fun compareStrings(str1: String, str2: String, block: (String, String) -> Boolean): Boolean { 113 | return block(str1, str2) 114 | } 115 | 116 | 117 | // INFO 🔥 Lambda function 118 | fun lengthCompare(): (String, String) -> Boolean = { x, y -> x.length > y.length } 119 | 120 | 121 | // INFO 🔥 High-order function 122 | fun Collection.fold( 123 | initial: R, 124 | combine: (acc: R, nextElement: T) -> R 125 | ): R { 126 | 127 | var accumulator: R = initial 128 | 129 | for (element: T in this) { 130 | accumulator = combine(accumulator, element) 131 | } 132 | 133 | return accumulator 134 | } 135 | 136 | fun List.max(action: (T) -> Unit) { 137 | 138 | var max: Double = 0.0 139 | for (element: T in this) { 140 | element.toInt() > max 141 | } 142 | forEach { 143 | val number: Double = (it as? Double) ?: 0.0 144 | if (number > max) max = number 145 | } 146 | 147 | (max as? T)?.let { 148 | action(it) 149 | } 150 | } 151 | 152 | // INFO 🔥 High-order function 153 | fun invokeAfterDelay(delayInMs: Long, predicateAfterDelay: () -> Unit) { 154 | println("STARTING DELAY FUNCTION") 155 | delay(delayInMs) 156 | predicateAfterDelay() 157 | } 158 | 159 | 160 | fun delay(timeInMillis: Long = 0) { 161 | Thread.sleep(timeInMillis) 162 | } 163 | 164 | 165 | // INFO 🔥 High-order function 166 | fun highOrderFun(str: String, predicate: (String) -> Int): Int { 167 | return predicate(str) 168 | } 169 | 170 | // INFO 🔥 Lambda function 171 | fun lambdaFun(): (String) -> Int = { 172 | it.toIntOrNull() ?: -1 173 | } 174 | 175 | // INFO 🔥🔥 NOT a lambda function, REGULAR function that returns a LAMBDA function 176 | fun nonLambdaFunction(): (String) -> Int { 177 | return { s: String -> s.toIntOrNull() ?: -1 } 178 | } 179 | 180 | // INFO 🔥 High-order function 181 | fun runWithCondition(condition: Boolean, action1: () -> Unit, action2: (() -> Unit)? = null) { 182 | if (condition) action1() 183 | else if (action2 != null) action2() 184 | } 185 | 186 | // INFO 🔥 Lambda Function 187 | fun lambdaAction2(): () -> Unit = { 188 | println("Action2 Invoked") 189 | } 190 | 191 | -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter4Functions/Tutorial4_2FunctionTypes.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter4Functions 2 | 3 | fun main() { 4 | 5 | 6 | // INFO 🔥 Custom class that implements a function type as an interface 7 | val intFunction: (Int) -> Int = IntTransformer() 8 | println("intFunction: $intFunction(5)") // prints x * x 9 | 10 | 11 | // Lambda Expression that takes 2 int params and returns int 12 | val testFun: (Int, Int) -> Int = { x, y -> x + y } 13 | val testFunInferred = { x: Int, y: Int -> x + y } 14 | // Lambda means-> testFun: (Int, Int) -> Int 15 | val resFun = testFun(2, 4) 16 | 17 | // The compiler can infer the function types for variables if there is enough information: 18 | val a = { i: Int -> i + 1 } // The inferred type is (Int) -> Int 19 | println("a ${a(2)}") 20 | 21 | 22 | // INFO 🔥🔥 BaseClassA value of type (BaseClassA, InterfaceB) -> C can be passed or assigned 23 | // where a BaseClassA.(InterfaceB) -> C 24 | val repeatFun: String.(Int) -> String = { times:Int -> 25 | this.repeat(times) 26 | } 27 | val twoParameters: (String, Int) -> String = repeatFun // OK 28 | val twoParams: (String, Int) -> String = { str, times -> str.repeat(times) } 29 | 30 | val result = runTransformation(repeatFun) // OK 31 | val result2 = runTransformation(twoParameters) 32 | val result3 = runTransformation(twoParams) 33 | 34 | twoParameters("hi", 4) 35 | 36 | println("result: $result") // prints hello-hello-hello- 37 | println("result2: $result2") // prints hello-hello-hello- 38 | println("result3: $result3") // prints hello-hello-hello- 39 | 40 | 41 | // INFO 🔥 Invoking a function type instance 42 | 43 | // If the value has a receiver type, the receiver object should be passed as the first argument. 44 | // Another way to invoke a value of a function type with receiver is to prepend it with the receiver object, 45 | // as if the value were an extension function: 1.foo(2) , 46 | 47 | val stringPlus: (String, String) -> String = String::plus 48 | 49 | // INFO 🔥 Both mean same thing 50 | // val intPlus: Int.(Int) -> Int = Int::plus 51 | val intPlus: Int.(Int) -> Int = { x -> this.plus(x) } 52 | 53 | println(stringPlus.invoke("<-", "->")) 54 | println(stringPlus("Hello, ", "world!")) 55 | 56 | println(intPlus.invoke(1, 1)) 57 | println(intPlus(1, 2)) 58 | println(2.intPlus(3)) // extension-like call 59 | 60 | 61 | val testString = "Hello World" 62 | 63 | // INFO High Order Function 64 | val resultHighOrder: String = exampleHighOrder(testString) { 65 | it.uppercase() 66 | } 67 | 68 | // INFO Function Literal With Receiver 69 | val resultLiteralReceiver: String = exampleLiteralWithReceiver(testString) { 70 | uppercase() 71 | } 72 | 73 | // INFO Extension Function 74 | val resultExtension: String = testString.exampleExtension { 75 | it.uppercase() 76 | } 77 | 78 | // INFO Extension Function that Literal With Receiver 79 | val resultExtensionLiteral: String = testString.exampleLiteralExtension { 80 | uppercase() 81 | } 82 | 83 | println("TEST-> resultHighOrder: $resultHighOrder, resultLiteralReceiver: $resultLiteralReceiver, resultExtension: $resultExtension, resultExtensionLiteral: $resultExtensionLiteral") 84 | 85 | 86 | } 87 | 88 | 89 | // INFO 🔥 Custom class that implements a function type as an interface 90 | class IntTransformer : (Int) -> Int { 91 | // override operator fun invoke(x: Int): Int = TODO() 92 | override operator fun invoke(x: Int): Int = x * x 93 | } 94 | 95 | // INFO 🔥 BaseClassA value of type (BaseClassA, InterfaceB) -> C can be passed or assigned where a BaseClassA.(InterfaceB) -> C 96 | fun runTransformation(action: (String, Int) -> String): String { 97 | return action("hello-", 3) 98 | } 99 | 100 | /* 101 | INFO 🔥 Both functions give the same result 102 | * First function is High Order function that takes function as a param that takes String as param 103 | * Second function is Function Literal With Receiver that is action is extension function of String 104 | * Third one is Extension Function which is called by a String object only 105 | * and this corresponds to String inside the function 106 | */ 107 | 108 | // INFO 🔥 High Order Function 109 | fun exampleHighOrder(value: String, action: (String) -> String): String { 110 | return action(value) 111 | } 112 | 113 | /** 114 | action: String.() defines here that if we have a String inside this function 115 | that String call action() with no params since its String extension with .() 116 | 117 | 😎 Basically it means: action() lambda will be called only by receiver(String) inside this high-order 118 | function. 119 | 120 | 🎃 If we want to call lambda action() with any param, for instance action(String) 121 | we should change action: String.() to String.(String) 122 | 123 | */ 124 | // INFO 🔥 Function Literal With Receiver 125 | fun exampleLiteralWithReceiver(value: String, action: String.() -> String): String { 126 | // INFO Both result implementations are the same 127 | 128 | // value here act as String of String.() and action is without params() 129 | val result = value.action() 130 | return result 131 | } 132 | 133 | /** 134 | * This is an extension function that does not require a String param since only String class instance 135 | * can call this function, and this caller String can be sent as a parameter to lambda 136 | * by using action(this) 137 | */ 138 | // INFO 🔥 High Order Extension Function 139 | fun String.exampleExtension(action: (String) -> String): String { 140 | val result = action(this) 141 | return result 142 | } 143 | 144 | /** 145 | * This function is an extension function which can be called only by a String instance. 146 | * Since it has String.() literal with receiver and receiver is a String, 147 | * it's already called on a String which does not require a parameter as the function above 148 | */ 149 | // INFO 🔥 Extension Function that Literal With Receiver 150 | fun String.exampleLiteralExtension(action: String.() -> String): String { 151 | // INFO Both result implementations are the same 152 | val result = action() 153 | return result 154 | } 155 | 156 | -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter4Functions/Tutorial4_3LambdaExpressions.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter4Functions 2 | 3 | 4 | /* 5 | Lambda function takes parameters from when it's invoked 6 | definition uses that argument in codeBody 7 | 8 | val lambdaName : Type = { argumentList -> codeBody } 9 | or 10 | val lambdaName: Type = {argument: TypeOfArgument -> codeBody } 11 | */ 12 | fun main() { 13 | 14 | // INFO 🔥 Lambda Expression 15 | val lambda1: (String) -> Unit = { s -> println(s) } 16 | lambda1("lambda1 String") 17 | 18 | 19 | val lambda2 = { s: String -> println(s) } 20 | lambda2("lambda2 String 2") 21 | 22 | val sumAlternative1 = { x: Int, y: Int -> x + y } 23 | val sumAlternative2: (Int, Int) -> Int = { x, y -> x + y } 24 | println("Sum Alt1: ${sumAlternative1(2, 3)}") 25 | println("Sum Alt2: ${sumAlternative2(2, 3)}") 26 | 27 | // INFO Passing Lambda as last parameter 28 | val items = listOf(1, 2, 3) 29 | 30 | // If lambda is the last parameter it can be taken outside the parenthesis 31 | val prod = items.fold(1, { acc, e -> acc * e }) 32 | val product = items.fold(1) { acc, e -> acc * e } 33 | 34 | // If the lambda is the only argument to that call, the parentheses can be omitted entirely: 35 | run { println("...") } 36 | 37 | // Info Single parameter it 38 | val ints = listOf(1, 2, 3) 39 | ints.filter { it > 0 } // this literal is of type '(it: Int) -> Boolean' 40 | 41 | 42 | // INFO 🔥🔥🔥 Lambda Expression with Receiver 43 | 44 | // Lambda expression that has a Receiver String. and function literal () -> Unit that returns Unit 45 | 46 | 47 | val greet: String.() -> Unit = { 48 | println("Function Literal Receiver: $this") 49 | } 50 | 51 | // Both implementations have same results 52 | greet("TestStringConcatenation String") 53 | "TestStringConcatenation String".greet() 54 | 55 | // Lambda expression that has a Receiver String. and function literal () -> String that returns a String 56 | val myString: String.() -> String = { 57 | "length is ${this.length}" 58 | } 59 | println("myString: ${myString("TestStringConcatenation")}") 60 | "TestStringConcatenation".myString() 61 | 62 | // Lambda expression that has a Receiver Int. and function literal () -> Boolean that returns a Boolean 63 | // 🤨 isEven becomes EXTENSION of an Integer by Int.(). () with no params means that 64 | // 🤨 isEven is called such as 3.isEven() or isEven(3) 65 | val isEven: Int.() -> Boolean = { 66 | this % 2 == 0 67 | } 68 | // Both returns same result 69 | isEven(3) 70 | 3.isEven() 71 | 72 | // Lambda expression that has a Receiver Int. and function literal (Int) -> Boolean that returns a Boolean 73 | val isWithCarry: Int.(Int) -> Boolean = { divider: Int -> 74 | this % divider != 0 75 | } 76 | 77 | 78 | // Both returns same result 79 | isWithCarry(3, 2) 80 | 3.isWithCarry(2) 81 | 82 | 83 | val total: Int.(Int) -> Int = { other -> this.plus(other) } 84 | println("Function Literal with Receiver total2: Int.(Int)-> Int: " + total(3, 4)) 85 | 86 | 87 | val testLambdaReceiver: String.(Int) -> String = { number: Int -> 88 | this + number 89 | } 90 | 91 | testLambdaReceiver("Hello", 4) 92 | "Hello".testLambdaReceiver(4) 93 | 94 | // INFO 🔥🔥 Anonymous Function with Receiver 95 | // The anonymous function syntax allows you to specify the receiver type of a function literal directly. 96 | // This can be useful if you need to declare a variable of a function type with receiver, and to use it later. 97 | val sumWithLiteralParam = fun Int.(other: Int): Int = this + other 98 | val res1: Int = sumWithLiteralParam(3, 4) 99 | val res2: Int = 3.sumWithLiteralParam(4) 100 | 101 | 102 | // INFO 🔥🔥 Function Literal with Receiver 103 | // This function is defined as Function Literal with Receiver 104 | val isOdd = isOdd(4) { 105 | this % 2 == 1 106 | } 107 | 108 | createString({ 109 | 110 | }) 111 | 112 | // Function Literal with Receiver without Parentheses 113 | val stringCreated = createString { 114 | //here we're in the context of a `StringBuilder` 115 | append(4) 116 | append("hello") 117 | 118 | 119 | } 120 | println("stringCreated $stringCreated") 121 | 122 | val sb = StringBuilder() 123 | // val sbNew = extra({ 124 | // 125 | // }) 126 | // 127 | // extra { 128 | // 129 | // } 130 | 131 | // val ch = sbNew.extra2(3) { 132 | // val num = it * 2 133 | // num 134 | // } 135 | 136 | // INFO 🔥🔥 Function Literal that returns String 137 | val upperCase = createStringBlock(4) { 138 | 139 | // Thi predicate function returns String 140 | "createStringBlock $it" 141 | } 142 | 143 | println("Uppercase String: $upperCase") 144 | 145 | } 146 | 147 | 148 | // INFO 🔥🔥 Function Literal that returns Boolean 149 | fun isOdd(value: Int, action: Int.() -> Boolean): Boolean { 150 | // return action(value) 151 | return value.action() 152 | } 153 | 154 | // INFO 🔥🔥 High Oder Function 155 | fun createStringBlock(num: Int, block: (Int) -> String): String { 156 | return block(num).uppercase() 157 | } 158 | 159 | 160 | // INFO 🔥🔥 Function Literal with Receiver 161 | // StringBuilder.() sets this function as extension function of StringBuilder class 162 | // receiver is defined as StringBuilder 163 | fun createString(block: StringBuilder.() -> Unit): String { 164 | 165 | val sb = StringBuilder() // create the receiver object 166 | sb.block() // pass the receiver object to the lambda 167 | 168 | return sb.toString() 169 | } 170 | 171 | // INFO 🔥 Extension Functions Literal with Receiver 172 | fun StringBuilder.extra(block: StringBuilder.() -> Unit): StringBuilder { 173 | block() 174 | return this 175 | } 176 | 177 | fun StringBuilder.extra2(value: Int, block: (Int) -> Int): StringBuilder { 178 | this.append(block(value).toString()) 179 | return this 180 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter4Functions/Tutorial4_4InlineFunctions.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter4Functions 2 | 3 | fun main() { 4 | 5 | // INFO 🔥 Inline Function 6 | /**********************************************/ 7 | // Creates instance of lambda function instance every time invoked 8 | nonInlined { 9 | println("Hello from NON-inlined") 10 | } 11 | 12 | // only gets block inside lambda, does not create instance 13 | inlined { 14 | println("Hello from inlined") 15 | } 16 | /**********************************************/ 17 | 18 | val list = arrayListOf() 19 | for (number in 1..10) { 20 | list.add(number) 21 | } 22 | 23 | // 🔥 'it' in lambda is every element that this list contains iterated one by one 24 | val resultList = list.filterOnCondition { isMultipleOf(it, 5) } 25 | println("filterOnCondition resultList : $resultList") 26 | 27 | 28 | // 🔥 'this' in lambda is every element that this list contains iterated one by one 29 | val resultListWithReceiver = list.filterWithCondition { 30 | isMultipleOf(this, 5) 31 | } 32 | println("filterWithCondition resultListWithReceiver : $resultListWithReceiver") 33 | 34 | /**********************************************/ 35 | } 36 | 37 | 38 | fun nonInlined(block: () -> Unit) { 39 | println("before") 40 | block() 41 | println("after") 42 | } 43 | 44 | 45 | inline fun inlined(block: () -> Unit) { 46 | println("before") 47 | block() 48 | println("after") 49 | } 50 | 51 | /* 52 | * Gives the following warning because the inline function does not take any Function object 53 | * Expected performance impact from inlining is insignificant. Inlining works best for functions with parameters of functional types 54 | * */ 55 | inline fun isMultipleOf(number: Int, multipleOf: Int): Boolean { 56 | return number % multipleOf == 0 57 | } 58 | 59 | fun ArrayList.filterOnCondition(condition: (T) -> Boolean): ArrayList { 60 | val result = arrayListOf() 61 | for (item in this) { 62 | if (condition(item)) { 63 | result.add(item) 64 | } 65 | } 66 | return result 67 | } 68 | 69 | fun ArrayList.filterWithCondition(condition: T.() -> Boolean): ArrayList { 70 | val result = arrayListOf() 71 | 72 | for (item in this) { 73 | 74 | /* 75 | 🔥 our parameter is literal with Receiver here, T.() -> Boolean 76 | so we can call condition() function on T 77 | */ 78 | 79 | if (item.condition()) { 80 | result.add(item) 81 | } 82 | } 83 | 84 | return result 85 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter5Coroutines/Tutorial5_5Flow3.kt: -------------------------------------------------------------------------------- 1 | @file:OptIn(ExperimentalCoroutinesApi::class) 2 | 3 | package com.smarttoolfactory.tutorial.chapter5Coroutines 4 | 5 | import kotlinx.coroutines.ExperimentalCoroutinesApi 6 | import kotlinx.coroutines.InternalCoroutinesApi 7 | import kotlinx.coroutines.delay 8 | import kotlinx.coroutines.flow.asFlow 9 | import kotlinx.coroutines.flow.flatMapConcat 10 | import kotlinx.coroutines.flow.flatMapLatest 11 | import kotlinx.coroutines.flow.flatMapMerge 12 | import kotlinx.coroutines.flow.merge 13 | import kotlinx.coroutines.flow.onEach 14 | import kotlinx.coroutines.runBlocking 15 | 16 | 17 | @InternalCoroutinesApi 18 | fun main() = runBlocking { 19 | 20 | // mergeSample() 21 | // flatMapConcatSample() 22 | flatMapMergeSample() 23 | // flatmapLatestSample() 24 | 25 | } 26 | 27 | 28 | // 🔥 RxJava merge 29 | private suspend fun mergeSample() { 30 | val flow1 = listOf("Alpha", "Beta", "Gamma", "Delta", "Epsilon").asFlow() 31 | val flow2 = listOf("Zeta", "Eta", "Theta").asFlow() 32 | 33 | 34 | merge(flow1, flow2) 35 | .collect { 36 | println(it) 37 | } 38 | 39 | /* 40 | RxJava Counterpart 41 | 42 | val source1 = Observable.just("Alpha", "Beta", "Gamma", "Delta", "Epsilon") 43 | val source2 = Observable.just("Zeta", "Eta", "Theta") 44 | 45 | source1 46 | .mergeWith(source2) 47 | .doFinally { 48 | println("doOnFinally()") 49 | } 50 | .subscribe { i -> println("RECEIVED: $i") } 51 | */ 52 | 53 | /* 54 | Prints: 55 | Beta 56 | Gamma 57 | Delta 58 | Epsilon 59 | Zeta 60 | Eta 61 | Theta 62 | */ 63 | } 64 | 65 | // 🔥 RxJava concatMap 66 | private suspend fun flatMapConcatSample() { 67 | (1..3).asFlow() 68 | .onEach { delay(100) } // a number every 100 ms 69 | .flatMapConcat { requestFlow(it) } 70 | .collect { value -> // collect and print 71 | println(value) 72 | } 73 | 74 | /* 75 | RxJava Counterpart 76 | observable1.concatMap{} 77 | 78 | */ 79 | 80 | /* 81 | Prints 82 | 1: First in thread main 83 | 1: Second in thread main 84 | 2: First in thread main 85 | 2: Second in thread main 86 | 3: First in thread main 87 | 3: Second in thread main 88 | */ 89 | } 90 | 91 | // 🔥 RxJava flatMap 92 | private suspend fun flatMapMergeSample() { 93 | (1..3).asFlow() 94 | .onEach { delay(100) } // a number every 100 ms 95 | .flatMapMerge { requestFlow(it) } 96 | .collect { value -> // collect and print 97 | println(value) 98 | } 99 | 100 | /* 101 | RxJava Counterpart 102 | observable1.flatMap{} 103 | 104 | */ 105 | 106 | /* 107 | Prints 108 | 1: First in thread main 109 | 2: First in thread main 110 | 3: First in thread main 111 | 1: Second in thread main 112 | 2: Second in thread main 113 | 3: Second in thread main 114 | */ 115 | } 116 | 117 | // 🔥 RxJava switchMap 118 | private suspend fun flatmapLatestSample() { 119 | 120 | val startTime = System.currentTimeMillis() // remember the start time 121 | (1..3).asFlow() 122 | // .onEach { delay(100) } // a number every 100 ms 123 | .flatMapLatest { requestFlow(it) } 124 | .collect { value -> // collect and print 125 | println("$value at ${System.currentTimeMillis() - startTime} ms from start") 126 | } 127 | 128 | /* 129 | Prints: 130 | 131 | 1: First in thread main at 79 ms from start 132 | 2: First in thread main at 84 ms from start 133 | 3: First in thread main at 85 ms from start 134 | 3: Second in thread main at 588 ms from start 135 | 136 | */ 137 | 138 | /* 139 | 🔥🔥 Note that flatMapLatest cancels all the code in 140 | its block ({ requestFlow(it) } in this example) on a new value. 141 | 142 | It makes no difference in this particular example, 143 | because the call to requestFlow itself is fast, not-suspending, and cannot be cancelled. 144 | However, it would show up if we were to use suspending functions like delay in there. 145 | */ 146 | } 147 | -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter6Advanced/DSLWithLambda.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter6Advanced 2 | 3 | import java.util.* 4 | 5 | 6 | fun main() { 7 | testRouteHandler() 8 | 9 | var delegatedNum2 by delegateCalculationFunction() 10 | 11 | } 12 | 13 | private fun testRouteHandler() { 14 | 15 | routeHandler("/index.html") { 16 | 17 | if (request.query != "") { 18 | // process 19 | } 20 | 21 | response { 22 | code = 404 23 | description = "Not found" 24 | } 25 | } 26 | } 27 | 28 | class RouteHandler(val request: Request, val response: Response) { 29 | var executeNext = false 30 | fun next() { 31 | executeNext = true 32 | } 33 | } 34 | 35 | fun routeHandler(path: String, f: RouteHandler.() -> Unit) { 36 | 37 | val request = Request("GET", "/v1/getProducts", "json") 38 | val response = Response("body", Status(200, "")) 39 | val routeHandler = RouteHandler(request, response) 40 | 41 | routeHandler.f() 42 | } 43 | 44 | class Status(var code: Int, var description: String) 45 | 46 | class Request(val method: String, val query: String, val contentType: String) 47 | 48 | class Response(var contents: String, var status: Status) { 49 | operator fun invoke(status: Status.() -> Unit) { 50 | } 51 | } 52 | 53 | 54 | // HTML 55 | 56 | class HTML { 57 | 58 | fun init() { 59 | 60 | } 61 | } 62 | 63 | interface Element 64 | 65 | class Head : Element 66 | class Body : Element 67 | 68 | val children = ArrayList() 69 | 70 | fun head(init: Head.() -> Unit): Head { 71 | val head = Head() 72 | head.init() 73 | children.add(head) 74 | return head 75 | } 76 | 77 | fun body(init: Body.() -> Unit): Body { 78 | val body = Body() 79 | body.init() 80 | children.add(body) 81 | return body 82 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter6Advanced/DSLWithLambda2.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter6Advanced 2 | 3 | /** 4 | * This is a sample to display how functions with literals used with Jetpack Compose. 5 | * 6 | * This is a series of mock functions to display simplified version of drawing with Compose. 7 | * 8 | * * Column uses [Layout] do draw on a [Canvas] function using [DrawScope] 9 | */ 10 | fun main() { 11 | 12 | val modifier = Modifier 13 | Column(modifier = modifier) { 14 | println("🔥 Draw this COLUMN") 15 | } 16 | } 17 | 18 | fun Column(modifier: Modifier, content: ColumnScope.() -> Unit) { 19 | 20 | Layout(modifier) { 21 | ColumnScope.content() 22 | } 23 | } 24 | 25 | interface ColumnScope { 26 | fun Modifier.alignColumn() 27 | 28 | companion object : ColumnScope { 29 | 30 | override fun Modifier.alignColumn() { 31 | println("🤔 Modifier for Column alignColumn() ") 32 | } 33 | 34 | } 35 | } 36 | 37 | 38 | interface Modifier { 39 | companion object : Modifier 40 | } 41 | fun Layout(modifier: Modifier = Modifier, content: () -> Unit) { 42 | println("Layout()") 43 | modifier.drawBehind { 44 | content() 45 | } 46 | } 47 | 48 | interface DrawScope { 49 | fun draw() 50 | } 51 | 52 | class DrawModifier(val onDraw: DrawScope.() -> Unit) : DrawScope { 53 | 54 | override fun draw() { 55 | println("⚠️ DrawModifier drawContent()") 56 | onDraw() 57 | } 58 | } 59 | 60 | fun Modifier.drawBehind( 61 | onDraw: DrawScope.() -> Unit 62 | ) { 63 | DrawModifier( 64 | onDraw = onDraw 65 | ).draw() 66 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter6Advanced/DelegatedProperty.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter6Advanced 2 | 3 | import kotlin.reflect.KProperty 4 | 5 | 6 | fun main() { 7 | 8 | var delegatedNum1 by CalculateDelegate() 9 | var delegatedNum2 by delegateCalculationFunction() 10 | 11 | println("Initial delegatedNum1: $delegatedNum1, delegatedNum2: $delegatedNum2") 12 | delegatedNum1 = 4 13 | delegatedNum2 = 3 14 | 15 | println("Final delegatedNum1: $delegatedNum1, delegatedNum2: $delegatedNum2") 16 | 17 | /* 18 | Prints: 19 | 🔥 CalculateDelegate getValue() thisRef: null, property: var delegatedNum1: kotlin.Int 20 | 🔥 CalculateDelegate getValue() thisRef: null, property: var delegatedNum2: kotlin.Int 21 | Initial delegatedNum1: 0, delegatedNum2: 0 22 | 🤔 CalculateDelegate setValue() thisRef: null, property: var delegatedNum1: kotlin.Int, value: 4 23 | 🤔 CalculateDelegate setValue() thisRef: null, property: var delegatedNum2: kotlin.Int, value: 3 24 | 🔥 CalculateDelegate getValue() thisRef: null, property: var delegatedNum1: kotlin.Int 25 | 🔥 CalculateDelegate getValue() thisRef: null, property: var delegatedNum2: kotlin.Int 26 | Final delegatedNum1: 8, delegatedNum2: 6 27 | */ 28 | 29 | // 30 | // val owner = Owner() 31 | // val newRes = Resource(5) 32 | // 33 | // println("Owner res: ${owner.varResource}") 34 | // owner.varResource = newRes 35 | // println("Owner res: ${owner.varResource}") 36 | 37 | /* 38 | Prints: 39 | 🚀 ResourceDelegate getValue() thisRef: chapter6Advanced.Owner@4445629, property: var chapter6Advanced.Owner.varResource: chapter6Advanced.Resource 40 | Owner res: chapter6Advanced.Resource@1622f1b 41 | 🍒 ResourceDelegate getValue() thisRef: chapter6Advanced.Owner@4445629, property: var chapter6Advanced.Owner.varResource: chapter6Advanced.Resource, value: chapter6Advanced.Resource@72a7c7e0 42 | 🚀 ResourceDelegate getValue() thisRef: chapter6Advanced.Owner@4445629, property: var chapter6Advanced.Owner.varResource: chapter6Advanced.Resource 43 | Owner res: chapter6Advanced.Resource@72a7c7e0 44 | */ 45 | 46 | } 47 | 48 | /** 49 | * Delegate [CalculateDelegate] to this function 50 | */ 51 | fun delegateCalculationFunction() = CalculateDelegate() 52 | 53 | class CalculateDelegate { 54 | var value = 0 55 | 56 | operator fun getValue(thisRef: Any?, property: KProperty<*>): Int { 57 | println("🔥 CalculateDelegate getValue() thisRef: $thisRef, property: $property") 58 | return value 59 | } 60 | 61 | operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) { 62 | println("🤔 CalculateDelegate setValue() thisRef: $thisRef, property: $property, value: $value") 63 | this.value = value * 2 64 | } 65 | 66 | } 67 | 68 | 69 | @Suppress("NOTHING_TO_INLINE") 70 | inline operator fun CalculateDelegate.getValue(thisRef: Any?, property: KProperty<*>): Int = value 71 | 72 | 73 | @Suppress("NOTHING_TO_INLINE") 74 | inline operator fun CalculateDelegate.setValue(thisObj: Any?, property: KProperty<*>, value: Int) { 75 | this.value = value 76 | } 77 | 78 | /* 79 | JAVA 80 | 81 | static final KProperty[] $$delegatedProperties = 82 | new KProperty[]{(KProperty)Reflection.mutableProperty0(new MutablePropertyReference0Impl(DelegatedPropertyKt.class, "delegatedNum1", "", 1)), (KProperty)Reflection.mutableProperty0(new MutablePropertyReference0Impl(DelegatedPropertyKt.class, "delegatedNum2", "", 1))}; 83 | 84 | public static final void main() { 85 | CalculateDelegate var10000 = new CalculateDelegate(); 86 | KProperty var1 = $$delegatedProperties[0]; 87 | CalculateDelegate delegatedNum1 = var10000; 88 | var10000 = delegateCalculationFunction(); 89 | KProperty var3 = $$delegatedProperties[1]; 90 | CalculateDelegate delegatedNum2 = var10000; 91 | String var4 = "Initial delegatedNum1: " + delegatedNum1.getValue((Object)null, var1) + ", delegatedNum2: " + delegatedNum2.getValue((Object)null, var3); 92 | boolean var5 = false; 93 | System.out.println(var4); 94 | delegatedNum1.setValue((Object)null, var1, 4); 95 | delegatedNum2.setValue((Object)null, var3, 3); 96 | var4 = "Final delegatedNum1: " + delegatedNum1.getValue((Object)null, var1) + ", delegatedNum2: " + delegatedNum2.getValue((Object)null, var3); 97 | var5 = false; 98 | System.out.println(var4); 99 | } 100 | 101 | 102 | public final class CalculateDelegate { 103 | private int calculatedProperty; 104 | 105 | public final int getValue(@Nullable Object thisRef, @NotNull KProperty property) { 106 | Intrinsics.checkNotNullParameter(property, "property"); 107 | String var3 = "\ud83d\udd25 CalculateDelegate getValue() thisRef: " + thisRef + ", property: " + property; 108 | boolean var4 = false; 109 | System.out.println(var3); 110 | return this.calculatedProperty; 111 | } 112 | 113 | public final void setValue(@Nullable Object thisRef, @NotNull KProperty property, int value) { 114 | Intrinsics.checkNotNullParameter(property, "property"); 115 | String var4 = "\ud83e\udd14 CalculateDelegate setValue() thisRef: " + thisRef + ", property: " + property + ", value: " + value; 116 | boolean var5 = false; 117 | System.out.println(var4); 118 | this.calculatedProperty = value * 2; 119 | } 120 | } 121 | */ 122 | 123 | class Resource(var id: Int = 0) { 124 | override fun toString(): String { 125 | return "${super.toString()} with id: $id" 126 | } 127 | } 128 | 129 | class Owner { 130 | var varResource: Resource by ResourceDelegate() 131 | } 132 | 133 | class ResourceDelegate(private var resource: Resource = Resource()) { 134 | 135 | operator fun getValue(thisRef: Owner, property: KProperty<*>): Resource { 136 | 137 | println("🚀 ResourceDelegate getValue() thisRef: $thisRef, property: $property") 138 | return resource 139 | } 140 | 141 | operator fun setValue(thisRef: Owner, property: KProperty<*>, value: Any?) { 142 | 143 | println("🍒 ResourceDelegate getValue() thisRef: $thisRef, property: $property, value: $value") 144 | if (value is Resource) { 145 | resource = value 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter6Advanced/Generics.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter6Advanced 2 | 3 | fun main() { 4 | 5 | var listBaseShape = listOf(BaseShapeK()) 6 | var listOfShape = listOf(ShapeK()) 7 | 8 | // ❌ COMPILE ERROR 9 | // listOfShape = listBaseShape 10 | 11 | // THIS DOES NOT WORK IN JAVA, it should have ? super BaseShapeK to work 12 | // WORKS in KOTLIN 13 | // listBaseShape = listOfShape 14 | 15 | // ❌ COMPILE ERROR 16 | // listOfShape = listBaseShape 17 | 18 | /* 19 | Producer Consumer Behaviour 20 | */ 21 | val shapeBuilder = ShapeBuilder(ShapeK()) 22 | var shapeBuilderWithIN: ShapeBuilder = ShapeBuilder(CircleK()) 23 | 24 | println("ShapeBuilder shape: ${shapeBuilderWithIN.shape}") 25 | shapeBuilderWithIN.shape = RectangleK() 26 | println("ShapeBuilder After shape: ${shapeBuilderWithIN.shape}") 27 | 28 | 29 | var shapeBuilderWithOUT: ShapeBuilder = ShapeBuilder(CircleK()) 30 | 31 | /* 32 | ❌ COMPILE ERROR 33 | Type mismatch. 34 | Required: 35 | Nothing? 36 | Found: 37 | RectangleK 38 | */ 39 | // shapeBuilderWithOUT.shape = RectangleK() 40 | 41 | val shapeBuilderIn: ShapeBuilderIn = ShapeBuilderIn(CircleK()) 42 | println("ShapeBuilderIn shape: ${shapeBuilderIn.fetchShape()}") 43 | shapeBuilderIn.updateShape(RectangleK()) 44 | println("ShapeBuilderIn After shape: ${shapeBuilderIn.fetchShape()}") 45 | 46 | /* 47 | Assigning to Covariant or Contravariant 48 | */ 49 | 50 | // 🔥 Assigning concrete types to variants works 51 | // shapeBuilderWithIN = shapeBuilder 52 | // shapeBuilderWithOUT = shapeBuilder 53 | 54 | // ❌ Compile Error 55 | // shapeBuilder = shapeBuilderWithIN 56 | // shapeBuilder = shapeBuilderWithOUT 57 | 58 | val rectangleBuilder = ShapeBuilder(RectangleK()) 59 | val baseShaBuilder = ShapeBuilder(BaseShapeK()) 60 | 61 | // ❌ Compile Error IN type can only be assigned with higher(super) types 62 | // shapeBuilderWithIN = rectangleBuilder 63 | shapeBuilderWithIN = baseShaBuilder 64 | 65 | shapeBuilderWithOUT = rectangleBuilder 66 | // ❌ Compile Error OUT type can only be assigned with lower(sub) types 67 | // shapeBuilderWithOUT = baseShaBuilder 68 | } 69 | 70 | internal open class BaseShapeK 71 | 72 | internal open class ShapeK : BaseShapeK() 73 | 74 | internal class CircleK : ShapeK() 75 | 76 | internal open class RectangleK : ShapeK() 77 | 78 | internal class SquareK : RectangleK() 79 | 80 | /** 81 | * Invariant class 82 | */ 83 | private class ShapeBuilder(var shape: T? = null) 84 | 85 | /** 86 | * Covariant class 87 | * 88 | * **shape** in constructor cannot have ***var*** parameters 89 | */ 90 | private class ShapeBuilderOut(private val shape: T?) { 91 | private var newShape = shape 92 | 93 | // 🔥🔥 ❌ Compile ErrorType parameter T is declared as 'out' but occurs in 'in' position in type T? 94 | // fun updateShape(shape: T?) { 95 | // newShape = shape 96 | // 97 | // } 98 | } 99 | 100 | /** 101 | * Contravariant class 102 | * **shape** in constructor must be private. If it's not private it returns error 103 | * 104 | * ```Type parameter T is declared as 'in' but occurs in 'invariant' position in type T``` 105 | */ 106 | private class ShapeBuilderIn(private var shape: T?) { 107 | 108 | fun updateShape(shape: T?) { 109 | this.shape = shape 110 | } 111 | 112 | fun fetchShape(): ShapeK? = shape 113 | } 114 | 115 | 116 | interface Source 117 | 118 | /* 119 | 120 | IN JAVA 121 | 122 | // Java 123 | interface Source {} 124 | Copied! 125 | Then, it would be perfectly safe to store a reference to an instance of Source 126 | in a variable of type Source- there are no consumer-methods to call. 127 | But Java does not know this, and still prohibits it: 128 | 129 | // Java 130 | void demo(Source strs) { 131 | Source objects = strs; // 🔥🔥!!! Not allowed in Java 132 | // ... 133 | } 134 | 135 | ❌ COMPILE ERROR, this is not possible without bounds 136 | List objList = new ArrayList<>(); 137 | List strList = new ArrayList<>(); 138 | 139 | objList = strList; 140 | List is not subtype of List 141 | */ 142 | 143 | fun demoSource(strs: Source) { 144 | val objects: Source = strs // 🔥 This is allowed in Kotlin, since T is an out-parameter 145 | } 146 | 147 | interface Dest 148 | 149 | fun demoDest(strs: Dest) { 150 | val objects: Dest = strs // 🔥 This is allowed in Kotlin, since T is an in-parameter 151 | } 152 | 153 | 154 | -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter6Advanced/Generics2PECS.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter6Advanced 2 | 3 | fun main() { 4 | 5 | val tomatoBasket = VegetableBasket() 6 | val vegetableBasket = VegetableBasket() 7 | val plantBasket = VegetableBasket() 8 | 9 | val plants = ArrayList() 10 | val vegtables = ArrayList() 11 | val tomatoes = ArrayList() 12 | 13 | // Can use any class that super of T which is Tomato 14 | tomatoBasket.doSomethingWithList(plants) 15 | tomatoBasket.doSomethingWithList(vegtables) 16 | tomatoBasket.doSomethingWithList(tomatoes) 17 | 18 | vegetableBasket.doSomethingWithList(plants) 19 | vegetableBasket.doSomethingWithList(vegtables) 20 | 21 | vegetableBasket.doSomethingOut(tomatoBasket) 22 | vegetableBasket.doSomethingIn(plantBasket) 23 | 24 | // ❌ COMPILE ERROR Tomato is sub-type of Vegetable, it can accept Vegetable or super types 25 | // vegetableBasket.doSomething(tomatoes) 26 | 27 | 28 | } 29 | 30 | open class Plant 31 | open class Vegetable : Plant() 32 | class Tomato : Vegetable() 33 | class Cucumber : Vegetable() 34 | 35 | class VegetableBasket { 36 | 37 | fun doSomethingOut(basket: VegetableBasket) { 38 | 39 | } 40 | 41 | fun doSomethingIn(basket: VegetableBasket) { 42 | 43 | } 44 | 45 | fun doSomethingWithList(list: ArrayList) { 46 | list.forEach { 47 | println("VegetableBasket element: $it") 48 | } 49 | } 50 | } 51 | 52 | interface Producer { 53 | fun produce(): T 54 | } 55 | 56 | interface Consumer { 57 | fun consume(t: T) 58 | } 59 | 60 | class InOutTestClass { 61 | 62 | fun foo1(i: I) { 63 | 64 | } 65 | 66 | // ❌ COMPILE ERROR Type parameter I is declared as 'in' but occurs in 'out' position in type I 67 | // fun foo2():I{} 68 | 69 | // ❌ COMPILE ERROR Type parameter O is declared as 'out' but occurs in 'in' position in type O 70 | // fun foo3(o:O) = {} 71 | 72 | inline fun foo4(): O { 73 | return O::class.java.newInstance() 74 | } 75 | 76 | 77 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter6Advanced/Generics3Methods.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter6Advanced 2 | 3 | 4 | fun main() { 5 | 6 | val ints: Array = arrayOf(1, 2, 3) 7 | val any = Array(3) { "" } 8 | copy(ints, any) 9 | 10 | } 11 | 12 | 13 | fun copy(from: Array, to: Array) { 14 | from.forEachIndexed { index, any -> 15 | to[index] = any 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter6Advanced/HashCodeEqualsHashMap.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter6Advanced 2 | 3 | fun main() { 4 | 5 | val myMap:HashMap = hashMapOf() 6 | for (i in 0..1000) { 7 | myMap[Person("jon", id = i)] = 1 8 | } 9 | 10 | val person1 = Person("jon", 0) 11 | val person2 = Person("jon", 10) 12 | 13 | println("person1==person2 ${person1==person2}") 14 | println("myMap size: ${myMap.size}") 15 | 16 | } 17 | 18 | /** 19 | * Need to override equals and hash code to use objects from this class to be same keys 20 | * of HashMap 21 | */ 22 | class Person(private val name: String, private val id: Int) { 23 | override fun equals(other: Any?): Boolean { 24 | if(other !is Person) return false 25 | 26 | if (other.name == this.name) return true 27 | 28 | return false 29 | } 30 | 31 | override fun hashCode(): Int { 32 | return name.hashCode() 33 | } 34 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter6Advanced/LambdaWithReceiver.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter6Advanced 2 | 3 | fun main() { 4 | 5 | testScopedFunctions() 6 | 7 | } 8 | 9 | 10 | /* 11 | Scoped Functions 12 | */ 13 | 14 | private fun testScopedFunctions() { 15 | 16 | val testString = "Hello" 17 | 18 | val resLet = testString.letMe { 19 | it.toIntOrNull() ?: -1 20 | } 21 | 22 | val resRun = testString.runMe { 23 | toIntOrNull() ?: -1 24 | } 25 | 26 | 27 | val resAlso = testString.alsoMe { 28 | } 29 | 30 | val resApply = testString.applyMe { 31 | 32 | } 33 | 34 | withMe(testString) { 35 | uppercase() 36 | capitalize() 37 | } 38 | 39 | } 40 | 41 | fun T.letMe(predicate: (T) -> R): R { 42 | return predicate(this) 43 | } 44 | 45 | fun T.runMe(predicate: T.() -> R): R { 46 | return this.predicate() 47 | } 48 | 49 | 50 | fun T.alsoMe(predicate: (T) -> Unit): T { 51 | predicate(this) 52 | return this 53 | } 54 | 55 | fun T.applyMe(predicate: T.() -> Unit): T { 56 | this.predicate() 57 | return this 58 | } 59 | 60 | fun withMe(receiver: T, predicate: (T).() -> R): R { 61 | return receiver.predicate() 62 | } 63 | 64 | 65 | class Task(val date: Long, val description: String) 66 | -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter6Advanced/OperatorOverloading.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter6Advanced 2 | 3 | fun main() { 4 | 5 | val firstText = StringFirstLetter("Hello") 6 | val secondText = StringFirstLetter("World") 7 | 8 | val text = firstText plus secondText 9 | val text2 = firstText + secondText 10 | 11 | println(text) // Prints HW 12 | } 13 | 14 | 15 | class StringFirstLetter(val value: String) { 16 | // operator fun plus(other: StringFirstLetter): String { 17 | // return "${this.value.first()}${other.value.first()}" 18 | // } 19 | } 20 | 21 | // With infix 22 | infix operator fun StringFirstLetter.plus(other: StringFirstLetter): String { 23 | return "${this.value.first()}${other.value.first()}" 24 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter6Advanced/ReferencesAndObjects.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter6Advanced 2 | 3 | fun main() { 4 | // var list: ArrayList? = ArrayList() 5 | // val map: HashMap?> = HashMap() 6 | // list?.add("hello") 7 | // list?.add("world") 8 | // // Map holds a reference to list 9 | // map["foo"] = list 10 | // 11 | // // 🔥 We set reference of list variable, actual objects are still referenced by map 12 | // list = null 13 | 14 | // var list1: List? = listOf(1, 2, 3) 15 | // var list2 = list1 16 | // println("list1==list2 ${list1 == list2}, ${list1.hashCode()}, ${list2.hashCode()}") 17 | // list1 = null 18 | // 19 | // println("${list2?.size}") 20 | // println("map: ${map.size}") 21 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter6Advanced/RegularExpressions.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter6Advanced 2 | 3 | /* 4 | Here is the complete list of various character classes constructs: 5 | [abc]: It would match with text if the text is having either one of them(a,b or c) and only once. 6 | [^abc]: Any single character except a, b, or c (^ denote negation) 7 | [a-zA-Z]: a through z, or A through Z, inclusive (range) 8 | [a-d[m-p]]: a through d, or m through p: [a-dm-p] (union) 9 | [a-z&&[def]]: Any one of them (d, e, or f) 10 | [a-z&&[^bc]]: a through z, except for b and c: [ad-z] (subtraction) 11 | [a-z&&[^m-p]]: a through z, and not m through p: [a-lq-z] (subtraction) 12 | 13 | Predefined Character Classes – Metacharacters 14 | These are like short codes which you can use while writing regex. 15 | 16 | Construct Description 17 | . -> Any character (may or may not match line terminators) 18 | \d -> A digit: [0-9] 19 | \D -> A non-digit: [^0-9] 20 | \s -> A whitespace character: [ \t\n\x0B\f\r] 21 | \S -> A non-whitespace character: [^\s] 22 | \w -> A word character: [a-zA-Z_0-9] 23 | \W -> A non-word character: [^\w] 24 | 25 | Boundary Matchers 26 | ^ Matches the beginning of a line. 27 | $ Matches then end of a line. 28 | \b Matches a word boundary. 29 | \B Matches a non-word boundary. 30 | \A Matches the beginning of the input text. 31 | \G Matches the end of the previous match 32 | \Z Matches the end of the input text except the final terminator if any. 33 | \z Matches the end of the input text. 34 | */ 35 | 36 | fun main() { 37 | 38 | } 39 | -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter6Advanced/Reified.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter6Advanced 2 | 3 | 4 | fun main() { 5 | 6 | val reified = Reified() 7 | 8 | val result = reified.getClassOf() 9 | 10 | val str = result.newInstance() 11 | 12 | println(str) 13 | 14 | val publicAccount = reified.getAccountClass().newInstance() 15 | val publicAccountBalance = publicAccount.getBalance() 16 | println("Public Balance $publicAccountBalance") 17 | 18 | val privateAccount = reified.getAccountInstance() 19 | val privateAccountBalance = privateAccount.getBalance() 20 | println("Private Balance: $privateAccountBalance") 21 | 22 | 23 | } 24 | 25 | class Reified { 26 | 27 | internal inline fun getClassOf(): Class { 28 | return T::class.java 29 | } 30 | 31 | internal inline fun getAccountClass(): Class { 32 | return T::class.java 33 | } 34 | 35 | internal inline fun getAccountInstance(): T { 36 | return T::class.java.newInstance() 37 | } 38 | } 39 | 40 | interface Account { 41 | fun getBalance(): Int 42 | } 43 | 44 | class PrivateAccount() : Account { 45 | 46 | override fun getBalance(): Int { 47 | return 3 48 | } 49 | } 50 | 51 | class PublicAccount() : Account { 52 | override fun getBalance(): Int { 53 | return 5 54 | } 55 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter7Threading/ReentrantLock.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter7Threading 2 | 3 | import java.util.concurrent.locks.ReentrantLock 4 | 5 | /** 6 | * 1) If Thread1 accesses lock before Thread2, since [ReentrantLock.lock] locks it multiple times, 7 | * Thread2 is dead locked. 8 | * 9 | * 2) If Thread2 accesses lock before Thread1, Thread2 access program main thread that is waiting 10 | * threads because of join ends successfully. 11 | */ 12 | fun main() { 13 | 14 | val lock = ReentrantLock() 15 | 16 | val thread1 = Thread { 17 | 18 | println( 19 | "1st START Lock in thread: ${Thread.currentThread().name}, " + 20 | "lock held by current thread: ${lock.isHeldByCurrentThread}" 21 | ) 22 | lock.lock() 23 | lock.lock() 24 | println( 25 | "1st GOT Lock in thread: ${Thread.currentThread().name}, " + 26 | "lock held by this thread: ${lock.isHeldByCurrentThread}, " + 27 | "hold count: ${lock.holdCount}, " + 28 | "queue: ${lock.queueLength}" 29 | ) 30 | Thread.sleep(4500) 31 | lock.unlock() 32 | println( 33 | "1st Unlock in thread: ${Thread.currentThread().name}, " + 34 | "lock held by current thread: ${lock.isHeldByCurrentThread}, " + 35 | "hold count: ${lock.holdCount}, " + 36 | "queue: ${lock.queueLength}" 37 | ) 38 | } 39 | 40 | val thread2 = Thread { 41 | println( 42 | "2nd START Lock in thread: ${Thread.currentThread().name}, " + 43 | "lock held by current thread: ${lock.isHeldByCurrentThread}" 44 | ) 45 | lock.lock() 46 | println( 47 | "2nd GOT Lock in thread: ${Thread.currentThread().name}, " + 48 | "lock held by this thread: ${lock.isHeldByCurrentThread}, " + 49 | "hold count: ${lock.holdCount}, " + 50 | "queue: ${lock.queueLength}" 51 | ) 52 | Thread.sleep(900) 53 | lock.unlock() 54 | println( 55 | "2nd Unlock in thread: ${Thread.currentThread().name}, " + 56 | "lock held by current thread: ${lock.isHeldByCurrentThread}, " + 57 | "hold count: ${lock.holdCount}, " + 58 | "queue: ${lock.queueLength}" 59 | ) 60 | } 61 | 62 | // logStates(thread1, thread2, lock) 63 | 64 | thread1.start() 65 | thread2.start() 66 | 67 | thread1.join() 68 | thread2.join() 69 | 70 | println("Program finishing") 71 | } 72 | 73 | private fun logStates( 74 | thread1: Thread, 75 | thread2: Thread, 76 | lock: ReentrantLock 77 | ) { 78 | val mainThread = Thread.currentThread() 79 | 80 | Thread { 81 | var counter = 0 82 | 83 | while (counter < 100) { 84 | 85 | println( 86 | "Thread1 state: ${thread1.state}, Thread2 state: ${thread2.state}, mainThread state: ${mainThread.state}, " + 87 | "lock count for this thread: ${lock.holdCount}, queue length: ${lock.queueLength}" 88 | ) 89 | Thread.sleep(50) 90 | counter++ 91 | } 92 | }.start() 93 | } 94 | -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter7Threading/Tutorial7_1Thread.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter7Threading 2 | 3 | fun main() { 4 | // Threads run concurrently 5 | val myThread1 = MyThread() 6 | val myThread2 = MyThread() 7 | myThread1.start() 8 | myThread2.start() 9 | 10 | // Runnable can be passed to thread constructor 11 | val thread = Thread(MyRunnable()) 12 | thread.start() 13 | 14 | val thread2 = Thread { 15 | // This is run method of this thread 16 | for (i in 0..10) { 17 | println("ANONYMOUS THREAD Count: $i, in thread: ${Thread.currentThread().name}") 18 | Thread.sleep(100) 19 | } 20 | } 21 | thread2.start() 22 | 23 | Thread.sleep(1000) 24 | } 25 | 26 | class MyThread : Thread() { 27 | override fun run() { 28 | for (i in 0..10) { 29 | println("THREAD Count: $i, in thread: ${Thread.currentThread().name}") 30 | sleep(100) 31 | } 32 | } 33 | } 34 | 35 | class MyRunnable : Runnable { 36 | override fun run() { 37 | for (i in 0..10) { 38 | println("RUNNABLE Count: $i, in thread: ${Thread.currentThread().name}") 39 | Thread.sleep(100) 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter7Threading/Tutorial7_2Synchronized.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter7Threading 2 | 3 | fun main() { 4 | val nonSyncCounter = NonSyncCounter() 5 | val syncCounter = SyncedCounter() 6 | 7 | nonSyncCounter.count() 8 | syncCounter.count() 9 | } 10 | 11 | class NonSyncCounter { 12 | /** 13 | * Any thread can access and update this value while other thread might be reading 14 | * value that is not updated yet 15 | */ 16 | private var counter: Int = 0 17 | 18 | fun count() { 19 | val thread1 = Thread { 20 | repeat(10000) { 21 | counter++ 22 | } 23 | } 24 | 25 | val thread2 = Thread { 26 | repeat(10000) { 27 | counter++ 28 | } 29 | } 30 | 31 | thread1.start() 32 | thread2.start() 33 | 34 | thread1.join() 35 | thread2.join() 36 | 37 | println("NonSyncCounter counter: $counter") 38 | } 39 | } 40 | 41 | class SyncedCounter { 42 | private var counter: Int = 0 43 | 44 | /** 45 | * We acquire lock with SyncedCounter object this. 46 | * Only one thread at a time can access to counter 47 | */ 48 | @Synchronized 49 | private fun increment() { 50 | counter++ 51 | } 52 | 53 | 54 | fun count() { 55 | val thread1 = Thread { 56 | repeat(10000) { 57 | increment() 58 | } 59 | } 60 | 61 | val thread2 = Thread { 62 | repeat(10000) { 63 | increment() 64 | } 65 | } 66 | 67 | thread1.start() 68 | thread2.start() 69 | 70 | thread1.join() 71 | thread2.join() 72 | 73 | println("SyncedCounter counter: $counter") 74 | } 75 | 76 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter7Threading/Tutorial7_3SynchronizedBlock.kt: -------------------------------------------------------------------------------- 1 | @file:OptIn(ExperimentalTime::class, ExperimentalTime::class) 2 | 3 | package com.smarttoolfactory.tutorial.chapter7Threading 4 | 5 | import kotlin.random.Random 6 | import kotlin.time.ExperimentalTime 7 | import kotlin.time.measureTime 8 | 9 | fun main() { 10 | /** 11 | * This one takes more than 4 seconds because we lock addToFirstList and 12 | * addSecondList with same lock, SingleLockedObject, instance(this) because of that 13 | * while lock is acquired while addingFirstList second list is locked as well 14 | */ 15 | // val singleLockedObject = SingleLockedObject() 16 | // singleLockedObject.start() 17 | 18 | /** 19 | * Takes about 2.5s 20 | */ 21 | val multipleLockedObject = MultipleLockedObject() 22 | multipleLockedObject.start() 23 | 24 | } 25 | 26 | 27 | class SingleLockedObject { 28 | private val list1 = mutableListOf() 29 | private val list2 = mutableListOf() 30 | 31 | @Synchronized 32 | private fun addToFirstList() { 33 | Thread.sleep(1) 34 | list1.add(Random.nextInt(100)) 35 | // println("🍏 addToFirstList-> Thread: ${Thread.currentThread().name}") 36 | } 37 | 38 | @Synchronized 39 | private fun addToSecondList() { 40 | Thread.sleep(1) 41 | list2.add(Random.nextInt(100)) 42 | // println("🍎 addToSecondList-> Thread: ${Thread.currentThread().name}") 43 | } 44 | 45 | fun start() { 46 | val time = measureTime { 47 | val thread1 = Thread { 48 | process() 49 | } 50 | val thread2 = Thread { 51 | process() 52 | } 53 | 54 | thread1.start() 55 | thread2.start() 56 | 57 | thread1.join() 58 | thread2.join() 59 | } 60 | 61 | println("😆 Total time: $time, list1: ${list1.size}, list2: ${list2.size}") 62 | 63 | } 64 | 65 | private fun process() { 66 | for (i in 0..999) { 67 | addToFirstList() 68 | addToSecondList() 69 | } 70 | } 71 | } 72 | 73 | class MultipleLockedObject { 74 | private val list1 = mutableListOf() 75 | private val list2 = mutableListOf() 76 | 77 | private val lock1 = Any() 78 | private val lock2 = Any() 79 | 80 | 81 | private fun addToFirstList() { 82 | /** 83 | * While this lock is acquired no other thread can run this but can run 84 | * other function with lock2 85 | */ 86 | synchronized(lock1){ 87 | Thread.sleep(1) 88 | list1.add(Random.nextInt(100)) 89 | // println("🍏 addToFirstList-> Thread: ${Thread.currentThread().name}") 90 | } 91 | } 92 | 93 | private fun addToSecondList() { 94 | /** 95 | * While this lock is acquired no other thread can run this but can run 96 | * other function with lock1 97 | */ 98 | synchronized(lock2){ 99 | Thread.sleep(1) 100 | list2.add(Random.nextInt(100)) 101 | // println("🍎 addToSecondList-> Thread: ${Thread.currentThread().name}") 102 | } 103 | } 104 | 105 | fun start() { 106 | val time = measureTime { 107 | val thread1 = Thread { 108 | process() 109 | } 110 | val thread2 = Thread { 111 | process() 112 | } 113 | 114 | thread1.start() 115 | thread2.start() 116 | 117 | thread1.join() 118 | thread2.join() 119 | } 120 | 121 | println("😆 Total time: $time, list1: ${list1.size}, list2: ${list2.size}") 122 | 123 | } 124 | 125 | private fun process() { 126 | for (i in 0..999) { 127 | addToFirstList() 128 | addToSecondList() 129 | } 130 | } 131 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter7Threading/Tutorial7_4CountdownLatch.kt: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter7Threading 2 | 3 | import java.util.concurrent.CountDownLatch 4 | 5 | fun main() { 6 | 7 | } -------------------------------------------------------------------------------- /tutorial/src/main/java/com/smarttoolfactory/tutorial/chapter8fileIO/Test.java: -------------------------------------------------------------------------------- 1 | package com.smarttoolfactory.tutorial.chapter8fileIO; 2 | 3 | import java.io.*; 4 | 5 | public class Test { 6 | 7 | public static void writeFile4() throws IOException { 8 | File fout = new File("out.txt"); 9 | FileOutputStream fos = new FileOutputStream(fout); 10 | 11 | OutputStreamWriter osw = new OutputStreamWriter(fos); 12 | 13 | for (int i = 0; i < 10; i++) { 14 | osw.write("something"); 15 | } 16 | 17 | osw.close(); 18 | } 19 | 20 | public static void writeFile3() throws IOException { 21 | PrintWriter pw = new PrintWriter(new FileWriter("out.txt")); 22 | 23 | for (int i = 0; i < 10; i++) { 24 | pw.write("something"); 25 | } 26 | 27 | pw.close(); 28 | } 29 | 30 | public static void writeFile2() throws IOException { 31 | FileWriter fw = new FileWriter("out.txt"); 32 | 33 | for (int i = 0; i < 10; i++) { 34 | fw.write("something"); 35 | } 36 | 37 | fw.close(); 38 | } 39 | } 40 | --------------------------------------------------------------------------------