├── .gitattributes ├── subprojects ├── kotlin-for-java-devs │ ├── src │ │ ├── main │ │ │ ├── resources │ │ │ │ └── hello.txt │ │ │ ├── kotlin │ │ │ │ └── org │ │ │ │ │ └── sdkotlin │ │ │ │ │ ├── intro │ │ │ │ │ └── kotlin │ │ │ │ │ │ ├── _04_imports │ │ │ │ │ │ ├── otherpackage │ │ │ │ │ │ │ └── Thing1.kt │ │ │ │ │ │ └── ImportsInKotlin.kt │ │ │ │ │ │ ├── _23_generics │ │ │ │ │ │ └── util │ │ │ │ │ │ │ └── Utilities.kt │ │ │ │ │ │ ├── _XX_tdd │ │ │ │ │ │ └── TddInKotlin.kt │ │ │ │ │ │ ├── _XX_reflection │ │ │ │ │ │ └── ReflectionInKotlin.kt │ │ │ │ │ │ ├── _XX_scopingfunctions │ │ │ │ │ │ └── ScopingFunctionsInKotlin.kt │ │ │ │ │ │ ├── _02_helloworld │ │ │ │ │ │ ├── HelloKotlin.kt │ │ │ │ │ │ └── withargs │ │ │ │ │ │ │ └── HelloWithArgs.kt │ │ │ │ │ │ ├── _01_packages │ │ │ │ │ │ └── PackagesInKotlin.kt │ │ │ │ │ │ ├── _30_sealedtypes │ │ │ │ │ │ ├── UnknownPlayer.kt │ │ │ │ │ │ ├── otherpackage │ │ │ │ │ │ │ ├── OtherPackagePlayer.kt │ │ │ │ │ │ │ └── AiNPC.kt │ │ │ │ │ │ └── SealedTypesInKotlin.kt │ │ │ │ │ │ ├── _00_classes │ │ │ │ │ │ └── ClassesInKotlin.kt │ │ │ │ │ │ ├── _03_toplevel │ │ │ │ │ │ ├── TopLevelInKotlin.kt │ │ │ │ │ │ └── usage │ │ │ │ │ │ │ └── TopLevelUsageInKotlin.kt │ │ │ │ │ │ ├── _07_equality │ │ │ │ │ │ └── EqualityInKotlin.kt │ │ │ │ │ │ ├── _08_3_extensionfunctions │ │ │ │ │ │ ├── otherpackage │ │ │ │ │ │ │ └── ExtensionFunctionScopingInKotlin.kt │ │ │ │ │ │ └── ExtensionFunctionsInKotlin.kt │ │ │ │ │ │ ├── _08_2_tailrecursivefunctions │ │ │ │ │ │ ├── TailRecursionDebugging.kt │ │ │ │ │ │ └── TailRecursionInKotlin.kt │ │ │ │ │ │ ├── _05_1_unsigned_types │ │ │ │ │ │ └── UnsignedTypesInKotlin.kt │ │ │ │ │ │ ├── _XX_try_w_resources │ │ │ │ │ │ └── TryWithResourcesInKotlin.kt │ │ │ │ │ │ ├── _16_static │ │ │ │ │ │ └── StaticInKotlin.kt │ │ │ │ │ │ ├── _08_5_infixfunctions │ │ │ │ │ │ └── InfixFunctionsInKotlin.kt │ │ │ │ │ │ ├── _32_delegation │ │ │ │ │ │ └── LateInit.kt │ │ │ │ │ │ ├── _15_singletons │ │ │ │ │ │ └── SingletonInKotlin.kt │ │ │ │ │ │ ├── _06_strings │ │ │ │ │ │ └── StringsInKotlin.kt │ │ │ │ │ │ ├── _18_inheritance │ │ │ │ │ │ └── InheritanceInKotlin.kt │ │ │ │ │ │ ├── _19_abstractclasses │ │ │ │ │ │ └── AbstractClassesInKotlin.kt │ │ │ │ │ │ ├── _12_properties │ │ │ │ │ │ └── PropertiesInKotlin.kt │ │ │ │ │ │ ├── _08_4_operatorfunctions │ │ │ │ │ │ └── OperatorOverloadingInKotlin.kt │ │ │ │ │ │ ├── _05_0_variables_and_types │ │ │ │ │ │ └── VariablesAndTypesInKotlin.kt │ │ │ │ │ │ ├── _17_constants │ │ │ │ │ │ └── ConstantsInKotlin.kt │ │ │ │ │ │ ├── _11_iteration │ │ │ │ │ │ └── IterationInKotlin.kt │ │ │ │ │ │ ├── _13_constructors │ │ │ │ │ │ └── ConstructorsAndInitializersInKotlin.kt │ │ │ │ │ │ ├── _25_varargfunctions │ │ │ │ │ │ └── VarargsInKotlin.kt │ │ │ │ │ │ ├── _21_smartcasts │ │ │ │ │ │ └── SmartCastsInKotlin.kt │ │ │ │ │ │ ├── _XX_coroutines │ │ │ │ │ │ └── CoroutinesInKotlin.kt │ │ │ │ │ │ ├── _10_selection │ │ │ │ │ │ └── SelectionInKotlin.kt │ │ │ │ │ │ ├── _20_interfaces │ │ │ │ │ │ └── InterfacesInKotlin.kt │ │ │ │ │ │ ├── _08_0_functions │ │ │ │ │ │ └── FunctionsInKotlin.kt │ │ │ │ │ │ ├── _29_enums │ │ │ │ │ │ └── EnumsInKotlin.kt │ │ │ │ │ │ ├── _31_annotations │ │ │ │ │ │ └── AnnotationsInKotlin.kt │ │ │ │ │ │ └── _26_collections │ │ │ │ │ │ └── CollectionsInKotlin.kt │ │ │ │ │ ├── javainterop │ │ │ │ │ └── _XX_constants │ │ │ │ │ │ ├── ConstantsInKotlinObject.kt │ │ │ │ │ │ ├── ConstantsInKotlin.kt │ │ │ │ │ │ └── ConstantsInKotlinCompanion.kt │ │ │ │ │ └── meetup │ │ │ │ │ └── firstwednesday │ │ │ │ │ └── SDKotlin.kt │ │ │ └── java │ │ │ │ └── org │ │ │ │ └── sdkotlin │ │ │ │ └── intro │ │ │ │ └── java │ │ │ │ ├── _01_packages │ │ │ │ └── PackagesInJava.java │ │ │ │ ├── _06_strings │ │ │ │ └── StringsInJava.java │ │ │ │ ├── _13_constructors │ │ │ │ └── ConstructorsAndInitializersInJava.java │ │ │ │ ├── _02_helloworld │ │ │ │ ├── HelloJava.java │ │ │ │ └── HelloJava21.java │ │ │ │ ├── _17_constants │ │ │ │ └── ConstantsInJava.java │ │ │ │ ├── _00_classes │ │ │ │ └── ClassesInJava.java │ │ │ │ ├── _16_static │ │ │ │ └── StaticInJava.java │ │ │ │ ├── _22_nullsafety │ │ │ │ └── NullSafetyInJava.java │ │ │ │ ├── _24_arrays │ │ │ │ └── ArraysInJava.java │ │ │ │ ├── _10_selection │ │ │ │ └── SelectionInJava.java │ │ │ │ ├── _XX_try_w_resources │ │ │ │ └── TryWithResourcesInJava.java │ │ │ │ ├── _11_iteration │ │ │ │ └── IterationInJava.java │ │ │ │ ├── _06_equality │ │ │ │ └── EqualityInJava.java │ │ │ │ ├── _05_variables │ │ │ │ └── VariablesAndTypesInJava.java │ │ │ │ ├── _14_dataclasses │ │ │ │ └── DataClassesInJava.java │ │ │ │ ├── _28_variance │ │ │ │ └── VarianceInJava.java │ │ │ │ └── _15_singletons │ │ │ │ └── SingletonsInJava.java │ │ └── test │ │ │ └── kotlin │ │ │ └── org │ │ │ └── sdkotlin │ │ │ └── intro │ │ │ └── kotlin │ │ │ ├── _XX_tdd │ │ │ └── TddInKotlinTest.kt │ │ │ └── _14_0_dataclasses │ │ │ └── PolyglotPersonTest.kt │ ├── README.md │ └── build.gradle.kts ├── tdd-in-kotlin │ ├── src │ │ ├── test │ │ │ ├── resources │ │ │ │ └── io │ │ │ │ │ └── mockk │ │ │ │ │ └── settings.properties │ │ │ └── kotlin │ │ │ │ └── org │ │ │ │ └── sdkotlin │ │ │ │ └── tdd │ │ │ │ ├── fizzbuzz │ │ │ │ ├── TestPrinter.kt │ │ │ │ ├── FizzBuzzMockKTest.kt │ │ │ │ ├── FizzBuzzMockitoKotlinTest.kt │ │ │ │ └── FizzBuzzTest.kt │ │ │ │ ├── assertj │ │ │ │ └── FloatingPointAssertionsTest.kt │ │ │ │ ├── testfixtures │ │ │ │ └── InternalComponentTest.kt │ │ │ │ ├── mockk │ │ │ │ └── MockKValueClassParameterTest.kt │ │ │ │ └── sorting │ │ │ │ ├── KotestSortTest.kt │ │ │ │ ├── JUnit5LoopingSortTest.kt │ │ │ │ ├── JUnit5DynamicTestSortTest.kt │ │ │ │ ├── JUnit5ParameterizedSortTest.kt │ │ │ │ └── JUnit5SortTest.kt │ │ ├── main │ │ │ └── kotlin │ │ │ │ └── org │ │ │ │ └── sdkotlin │ │ │ │ └── tdd │ │ │ │ ├── fizzbuzz │ │ │ │ ├── Printer.kt │ │ │ │ ├── ConsolePrinter.kt │ │ │ │ └── FizzBuzz.kt │ │ │ │ ├── sorting │ │ │ │ └── Sorting.kt │ │ │ │ └── testfixtures │ │ │ │ └── InternalComponent.kt │ │ └── testFixtures │ │ │ └── kotlin │ │ │ └── org │ │ │ └── sdkotlin │ │ │ └── tdd │ │ │ └── testfixtures │ │ │ └── InternalComponentTestDouble.kt │ └── build.gradle.kts ├── constant-dependencies │ ├── constant-producer │ │ ├── build.gradle.kts │ │ └── src │ │ │ └── main │ │ │ └── kotlin │ │ │ └── org │ │ │ └── sdkotlin │ │ │ └── constants │ │ │ └── producer │ │ │ └── CompanionConstants.kt │ └── constant-consumer │ │ ├── build.gradle.kts │ │ └── src │ │ └── main │ │ └── kotlin │ │ └── org │ │ └── sdkotlin │ │ └── constants │ │ └── consumer │ │ └── CompanionConstantConsumer.kt ├── sorting-in-kotlin │ ├── build.gradle.kts │ └── src │ │ ├── main │ │ └── kotlin │ │ │ └── org │ │ │ └── sdkoltin │ │ │ └── sort │ │ │ ├── BubbleSort.kt │ │ │ └── Swap.kt │ │ └── test │ │ └── kotlin │ │ └── org │ │ └── sdkoltin │ │ └── sort │ │ ├── SortTestCases.kt │ │ ├── BubbleSortKtTest.kt │ │ └── SwapKtTest.kt ├── testing-with-mokkery │ ├── src │ │ ├── main │ │ │ └── kotlin │ │ │ │ └── org │ │ │ │ └── sdkotlin │ │ │ │ └── testing │ │ │ │ └── mokkery │ │ │ │ ├── ValueClassSuperType.kt │ │ │ │ ├── DataClassSuperType.kt │ │ │ │ ├── ValueClass.kt │ │ │ │ └── DataClass.kt │ │ └── test │ │ │ └── kotlin │ │ │ └── org │ │ │ └── sdkotlin │ │ │ └── testing │ │ │ └── mokkery │ │ │ └── MokkeryValueClassParameterTest.kt │ └── build.gradle.kts ├── ksp-builder-generator │ ├── api │ │ ├── builder │ │ │ ├── build.gradle.kts │ │ │ └── src │ │ │ │ └── main │ │ │ │ └── kotlin │ │ │ │ └── org │ │ │ │ └── sdkotlin │ │ │ │ └── buildergen │ │ │ │ └── api │ │ │ │ └── builder │ │ │ │ └── Builder.kt │ │ └── annotations │ │ │ ├── build.gradle.kts │ │ │ └── src │ │ │ ├── main │ │ │ └── kotlin │ │ │ │ └── org │ │ │ │ └── sdkotlin │ │ │ │ └── buildergen │ │ │ │ └── api │ │ │ │ └── annotations │ │ │ │ └── GeneratedBuilder.kt │ │ │ └── test │ │ │ └── kotlin │ │ │ └── org │ │ │ └── sdkotlin │ │ │ └── buildergen │ │ │ └── api │ │ │ └── annotations │ │ │ └── GeneratedBuilderTest.kt │ └── processor │ │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ └── com.google.devtools.ksp.processing.SymbolProcessorProvider │ │ │ └── kotlin │ │ │ └── org │ │ │ └── sdkotlin │ │ │ └── buildergen │ │ │ └── processor │ │ │ ├── BuilderGenSymbolProcessorProvider.kt │ │ │ └── BuilderGenSymbolProcessor.kt │ │ ├── test-project │ │ ├── src │ │ │ ├── main │ │ │ │ └── kotlin │ │ │ │ │ └── org │ │ │ │ │ └── sdkotlin │ │ │ │ │ └── buildergen │ │ │ │ │ └── processor │ │ │ │ │ └── it │ │ │ │ │ └── TestEntity.kt │ │ │ └── test │ │ │ │ └── kotlin │ │ │ │ └── org │ │ │ │ └── sdkotlin │ │ │ │ └── buildergen │ │ │ │ └── processor │ │ │ │ └── it │ │ │ │ └── TestEntityBuilderTest.kt │ │ └── build.gradle.kts │ │ └── build.gradle.kts ├── di-with-koin │ ├── src │ │ ├── main │ │ │ └── kotlin │ │ │ │ └── org │ │ │ │ └── sdkotlin │ │ │ │ └── koin │ │ │ │ └── hello │ │ │ │ ├── ExternalComponent.kt │ │ │ │ ├── GreetingService.kt │ │ │ │ ├── HelloController.kt │ │ │ │ ├── ExternalComponentContainer.kt │ │ │ │ ├── HelloKoin.kt │ │ │ │ ├── SimpleHelloController.kt │ │ │ │ ├── EnglishGreetingService.kt │ │ │ │ ├── HelloApp.kt │ │ │ │ ├── RandomGreetingService.kt │ │ │ │ └── helloModule.kt │ │ ├── it │ │ │ └── kotlin │ │ │ │ └── org │ │ │ │ └── sdkotlin │ │ │ │ └── koin │ │ │ │ └── it │ │ │ │ └── hello │ │ │ │ ├── test │ │ │ │ ├── testHelloModule.kt │ │ │ │ └── TestGreetingService.kt │ │ │ │ ├── HelloControllerIT.kt │ │ │ │ └── HelloModuleIT.kt │ │ └── test │ │ │ └── kotlin │ │ │ └── org │ │ │ └── sdkotlin │ │ │ └── koin │ │ │ └── hello │ │ │ ├── RandomGreetingServiceTest.kt │ │ │ ├── EnglishGreetingServiceTest.kt │ │ │ └── SimpleHelloControllerTest.kt │ └── build.gradle.kts ├── kotlin-reflect │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── kotlin │ │ └── org │ │ └── sdkotlin │ │ └── kotlin │ │ └── reflect │ │ └── SealedSubclasses.kt ├── effective-kotlin │ ├── build.gradle.kts │ ├── README.md │ └── src │ │ └── main │ │ └── kotlin │ │ └── org │ │ └── sdkotlin │ │ └── meetup │ │ ├── effectivejava │ │ ├── item62 │ │ │ └── AvoidStrings.kt │ │ ├── item58 │ │ │ └── PreferForEach.kt │ │ ├── item3and4 │ │ │ └── Singletons.kt │ │ ├── item17 │ │ │ └── MinimizeMutability.kt │ │ ├── item2 │ │ │ └── ConsiderBuilders.kt │ │ ├── item10through13 │ │ │ └── CommonMethods.kt │ │ ├── item16 │ │ │ └── AccessorsOverFields.kt │ │ ├── item50 │ │ │ └── MakeDefensiveCopies.kt │ │ ├── item18and19 │ │ │ └── FavorComposition.kt │ │ └── item1 │ │ │ └── ConsiderStaticFactoryMethods.kt │ │ └── idioms │ │ ├── Destructuring.kt │ │ ├── Nulls.kt │ │ └── ControlStructuresAsExpressions.kt ├── user-defined-integrals-in-kotlin │ ├── src │ │ ├── main │ │ │ └── kotlin │ │ │ │ └── org │ │ │ │ └── sdkotlin │ │ │ │ └── integral │ │ │ │ ├── Progression.kt │ │ │ │ ├── Integral.kt │ │ │ │ ├── IntegralRange.kt │ │ │ │ ├── Octal.kt │ │ │ │ ├── UOctal.kt │ │ │ │ └── IntegralProgression.kt │ │ └── test │ │ │ └── kotlin │ │ │ └── org │ │ │ └── sdkotlin │ │ │ └── integral │ │ │ ├── IntegralProgressionTest.kt │ │ │ ├── IntegralRangeTest.kt │ │ │ └── SignedIntegral.kt │ └── build.gradle.kts ├── kotlin-for-java-devs-client │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── kotlin │ │ └── org │ │ └── sdkotlin │ │ └── intro │ │ └── kotlin │ │ └── _30_sealedtypes │ │ ├── OtherModulePlayer.kt │ │ ├── SealedTypeSmartCastFromOtherModule.kt │ │ └── otherpackage │ │ └── OtherModuleAndPackageNPC.kt ├── typed-errors-in-kotlin │ ├── src │ │ └── main │ │ │ └── kotlin │ │ │ └── org │ │ │ └── sdkotlin │ │ │ └── typederrors │ │ │ ├── FruitBasket.kt │ │ │ ├── Fruit.kt │ │ │ ├── TypedError.kt │ │ │ ├── union │ │ │ └── ConvertersWithUnionTypes.kt │ │ │ ├── raise │ │ │ ├── ConvertersWithRaise.kt │ │ │ └── TypedErrorsWithRaise.kt │ │ │ └── either │ │ │ └── ConvertersWithEither.kt │ └── build.gradle.kts ├── kotlin-dl │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── kotlin │ │ └── org │ │ └── sdkotlin │ │ └── kotlindl │ │ └── FashionMnist.kt └── equalsverifier-with-kotlin │ ├── src │ ├── test │ │ └── kotlin │ │ │ └── org │ │ │ └── sdkotlin │ │ │ └── equalsverifier │ │ │ ├── delegation │ │ │ ├── BarImplTest.kt │ │ │ ├── BazImplTest.kt │ │ │ ├── LazyEqualsImplTest.kt │ │ │ └── FooBarImplTest.kt │ │ │ └── recursive │ │ │ └── DataClassWithSealedTypePropertyTest.kt │ └── main │ │ └── kotlin │ │ └── org │ │ └── sdkotlin │ │ └── equalsverifier │ │ ├── delegation │ │ ├── LazyEquals.kt │ │ └── DelegatedEquals.kt │ │ └── recursive │ │ └── DataClassWithSealedTypeProperty.kt │ └── build.gradle.kts ├── detekt-rules ├── src │ └── main │ │ ├── resources │ │ ├── config │ │ │ └── config.yml │ │ └── META-INF │ │ │ └── services │ │ │ └── io.gitlab.arturbosch.detekt.api.RuleSetProvider │ │ └── kotlin │ │ └── org │ │ └── sdkotlin │ │ └── detekt │ │ └── junit │ │ ├── JUnitRuleSetProvider.kt │ │ └── InvalidTestFactoryReturnType.kt ├── settings.gradle.kts └── build.gradle.kts ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .idea ├── codeStyles │ └── codeStyleConfig.xml ├── encodings.xml └── inspectionProfiles │ └── Project_Default.xml ├── platforms ├── plugins-platform │ └── build.gradle.kts ├── app-platform │ └── build.gradle.kts ├── settings.gradle.kts └── test-platform │ └── build.gradle.kts ├── .editorconfig ├── .gitignore ├── gradle.properties ├── config └── detekt │ └── detekt.yml ├── README.md ├── .github └── workflows │ └── gradle.yml ├── settings.gradle.kts ├── gradlew.bat └── .junie └── guidelines.md /.gitattributes: -------------------------------------------------------------------------------- 1 | *.jar filter=lfs diff=lfs merge=lfs -text 2 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/resources/hello.txt: -------------------------------------------------------------------------------- 1 | Hello, World! 2 | -------------------------------------------------------------------------------- /subprojects/tdd-in-kotlin/src/test/resources/io/mockk/settings.properties: -------------------------------------------------------------------------------- 1 | relaxed=true 2 | -------------------------------------------------------------------------------- /detekt-rules/src/main/resources/config/config.yml: -------------------------------------------------------------------------------- 1 | junit: 2 | InvalidTestFactoryReturnType: 3 | active: true 4 | -------------------------------------------------------------------------------- /subprojects/constant-dependencies/constant-producer/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.sdkotlin.buildlogic.kotlin-project") 3 | } 4 | -------------------------------------------------------------------------------- /detekt-rules/src/main/resources/META-INF/services/io.gitlab.arturbosch.detekt.api.RuleSetProvider: -------------------------------------------------------------------------------- 1 | org.sdkotlin.detekt.junit.JUnitRuleSetProvider 2 | -------------------------------------------------------------------------------- /subprojects/sorting-in-kotlin/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.sdkotlin.buildlogic.kotlin-project") 3 | id("org.sdkotlin.buildlogic.test.unit-test-suite") 4 | } 5 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:423cb469ccc0ecc31f0e4e1c309976198ccb734cdcbb7029d4bda0f18f57e8d9 3 | size 45633 4 | -------------------------------------------------------------------------------- /subprojects/testing-with-mokkery/src/main/kotlin/org/sdkotlin/testing/mokkery/ValueClassSuperType.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.testing.mokkery 2 | 3 | interface ValueClassSuperType 4 | -------------------------------------------------------------------------------- /subprojects/ksp-builder-generator/api/builder/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.sdkotlin.buildlogic.kotlin-project") 3 | id("org.sdkotlin.buildlogic.test.unit-test-suite") 4 | } 5 | -------------------------------------------------------------------------------- /subprojects/ksp-builder-generator/api/annotations/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.sdkotlin.buildlogic.kotlin-project") 3 | id("org.sdkotlin.buildlogic.test.unit-test-suite") 4 | } 5 | -------------------------------------------------------------------------------- /subprojects/tdd-in-kotlin/src/main/kotlin/org/sdkotlin/tdd/fizzbuzz/Printer.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.tdd.fizzbuzz 2 | 3 | fun interface Printer { 4 | 5 | fun print(fizzBuzz: String) 6 | } 7 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /subprojects/di-with-koin/src/main/kotlin/org/sdkotlin/koin/hello/ExternalComponent.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.koin.hello 2 | 3 | @JvmInline 4 | value class ExternalComponent(val value: String) 5 | -------------------------------------------------------------------------------- /subprojects/di-with-koin/src/main/kotlin/org/sdkotlin/koin/hello/GreetingService.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.koin.hello 2 | 3 | interface GreetingService { 4 | 5 | fun getGreeting(): String 6 | } 7 | -------------------------------------------------------------------------------- /subprojects/di-with-koin/src/main/kotlin/org/sdkotlin/koin/hello/HelloController.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.koin.hello 2 | 3 | interface HelloController { 4 | 5 | fun sayHello(): String 6 | } 7 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_04_imports/otherpackage/Thing1.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._04_imports.otherpackage 2 | 3 | class Thing1 4 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_23_generics/util/Utilities.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._23_generics.util 2 | 3 | internal fun itsYourBirthday() = true 4 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_XX_tdd/TddInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._XX_tdd 2 | 3 | class TddInKotlin { 4 | 5 | fun weCanDoIt() = true 6 | } 7 | -------------------------------------------------------------------------------- /subprojects/ksp-builder-generator/processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider: -------------------------------------------------------------------------------- 1 | org.sdkotlin.buildergen.processor.BuilderGenSymbolProcessorProvider 2 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/java/org/sdkotlin/intro/java/_01_packages/PackagesInJava.java: -------------------------------------------------------------------------------- 1 | // Package names must match the source folder in Java. 2 | 3 | package org.sdkotlin.intro.java._01_packages; 4 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_XX_reflection/ReflectionInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._XX_reflection 2 | 3 | // TODO: Add reflection example for Kotlin. 4 | -------------------------------------------------------------------------------- /subprojects/tdd-in-kotlin/src/main/kotlin/org/sdkotlin/tdd/sorting/Sorting.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.tdd.sorting 2 | 3 | fun > sort(array: Array): Array = 4 | array.copyOf().sortedArray() 5 | -------------------------------------------------------------------------------- /subprojects/testing-with-mokkery/src/main/kotlin/org/sdkotlin/testing/mokkery/DataClassSuperType.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.testing.mokkery 2 | 3 | interface DataClassSuperType { 4 | val v: ValueClassSuperType 5 | } 6 | -------------------------------------------------------------------------------- /subprojects/testing-with-mokkery/src/main/kotlin/org/sdkotlin/testing/mokkery/ValueClass.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.testing.mokkery 2 | 3 | @JvmInline 4 | value class ValueClass(val s: String) : ValueClassSuperType 5 | -------------------------------------------------------------------------------- /subprojects/di-with-koin/src/main/kotlin/org/sdkotlin/koin/hello/ExternalComponentContainer.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.koin.hello 2 | 3 | data class ExternalComponentContainer(val externalComponent: ExternalComponent) 4 | -------------------------------------------------------------------------------- /subprojects/ksp-builder-generator/api/annotations/src/main/kotlin/org/sdkotlin/buildergen/api/annotations/GeneratedBuilder.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.buildergen.api.annotations 2 | 3 | annotation class GeneratedBuilder 4 | -------------------------------------------------------------------------------- /subprojects/ksp-builder-generator/api/builder/src/main/kotlin/org/sdkotlin/buildergen/api/builder/Builder.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.buildergen.api.builder 2 | 3 | fun interface Builder { 4 | 5 | fun build(): T 6 | } 7 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_XX_scopingfunctions/ScopingFunctionsInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._XX_scopingfunctions 2 | 3 | // TODO: Add scoping functions example for Kotlin. 4 | -------------------------------------------------------------------------------- /subprojects/constant-dependencies/constant-consumer/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.sdkotlin.buildlogic.kotlin-project") 3 | } 4 | 5 | dependencies { 6 | 7 | implementation(projects.subprojects.constantDependencies.constantProducer) 8 | } 9 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/java/org/sdkotlin/intro/java/_06_strings/StringsInJava.java: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.java._06_strings; 2 | 3 | public class StringsInJava { 4 | 5 | public static final String STRING = "string"; 6 | } 7 | -------------------------------------------------------------------------------- /subprojects/kotlin-reflect/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.sdkotlin.buildlogic.kotlin-project") 3 | id("org.sdkotlin.buildlogic.test.unit-test-suite") 4 | } 5 | 6 | dependencies { 7 | 8 | implementation(kotlin("reflect")) 9 | } 10 | -------------------------------------------------------------------------------- /subprojects/tdd-in-kotlin/src/main/kotlin/org/sdkotlin/tdd/testfixtures/InternalComponent.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.tdd.testfixtures 2 | 3 | internal interface InternalComponent { 4 | 5 | fun getInt(): Int 6 | 7 | fun getString(): String 8 | } 9 | -------------------------------------------------------------------------------- /subprojects/tdd-in-kotlin/src/main/kotlin/org/sdkotlin/tdd/fizzbuzz/ConsolePrinter.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.tdd.fizzbuzz 2 | 3 | internal class ConsolePrinter : Printer { 4 | 5 | override fun print(fizzBuzz: String) { 6 | println(fizzBuzz) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /subprojects/effective-kotlin/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.sdkotlin.buildlogic.kotlin-project") 3 | } 4 | 5 | dependencies { 6 | 7 | implementation(platform("org.sdkotlin.platforms:app-platform")) 8 | 9 | implementation(libs.org.json) 10 | } 11 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_02_helloworld/HelloKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._02_helloworld 2 | 3 | // "Hello, World" is pretty simple in Kotlin. 4 | 5 | fun main() { 6 | println("Hello, SD Kotlin!") 7 | } 8 | -------------------------------------------------------------------------------- /subprojects/kotlin-reflect/src/main/kotlin/org/sdkotlin/kotlin/reflect/SealedSubclasses.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.kotlin.reflect 2 | 3 | sealed class SealedClass 4 | class SubClass : SealedClass() 5 | 6 | fun main() { 7 | println(SealedClass::class.sealedSubclasses) 8 | } 9 | -------------------------------------------------------------------------------- /subprojects/di-with-koin/src/main/kotlin/org/sdkotlin/koin/hello/HelloKoin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.koin.hello 2 | 3 | import org.koin.core.context.startKoin 4 | 5 | fun main() { 6 | 7 | startKoin { 8 | modules(helloModule) 9 | } 10 | 11 | HelloApp().run() 12 | } 13 | -------------------------------------------------------------------------------- /subprojects/user-defined-integrals-in-kotlin/src/main/kotlin/org/sdkotlin/integral/Progression.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.integral 2 | 3 | interface Progression : Iterable { 4 | val first: I 5 | val last: I 6 | val step: I 7 | 8 | fun isEmpty(): Boolean 9 | } 10 | -------------------------------------------------------------------------------- /subprojects/constant-dependencies/constant-producer/src/main/kotlin/org/sdkotlin/constants/producer/CompanionConstants.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.constants.producer 2 | 3 | class CompanionConstants { 4 | 5 | companion object { 6 | 7 | const val TESTING: String = "testing" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /subprojects/testing-with-mokkery/src/main/kotlin/org/sdkotlin/testing/mokkery/DataClass.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.testing.mokkery 2 | 3 | @Suppress("unused") // Not mockable by Mokkery unless all-opened. 4 | data class DataClass( 5 | override val v: ValueClassSuperType, 6 | ) : DataClassSuperType 7 | -------------------------------------------------------------------------------- /subprojects/constant-dependencies/constant-consumer/src/main/kotlin/org/sdkotlin/constants/consumer/CompanionConstantConsumer.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.constants.consumer 2 | 3 | import org.sdkotlin.constants.producer.CompanionConstants.Companion.TESTING 4 | 5 | fun main() { 6 | println(TESTING) 7 | } 8 | -------------------------------------------------------------------------------- /subprojects/di-with-koin/src/main/kotlin/org/sdkotlin/koin/hello/SimpleHelloController.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.koin.hello 2 | 3 | class SimpleHelloController(private val greetingService: GreetingService) : 4 | HelloController { 5 | 6 | override fun sayHello() = greetingService.getGreeting() 7 | } 8 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_01_packages/PackagesInKotlin.kt: -------------------------------------------------------------------------------- 1 | // Package names in Kotlin don't have to match the source folder. 2 | 3 | package somewhere.over.the.rainbow // Also, no semicolon. 4 | 5 | // Semicolons are optional in Kotlin, and rarely used. 6 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_02_helloworld/withargs/HelloWithArgs.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._02_helloworld.withargs 2 | 3 | // Args are optional. 4 | 5 | fun main(args: Array) { 6 | 7 | println("args: ${args.contentToString()}") 8 | } 9 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/java/org/sdkotlin/intro/java/_13_constructors/ConstructorsAndInitializersInJava.java: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.java._13_constructors; 2 | 3 | public class ConstructorsAndInitializersInJava { 4 | 5 | // TODO: Add constructors and initializers example for Java 6 | } 7 | -------------------------------------------------------------------------------- /subprojects/tdd-in-kotlin/src/test/kotlin/org/sdkotlin/tdd/fizzbuzz/TestPrinter.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.tdd.fizzbuzz 2 | 3 | internal class TestPrinter : Printer { 4 | 5 | val prints = mutableListOf() 6 | 7 | override fun print(fizzBuzz: String) { 8 | prints.add(fizzBuzz) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-all.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /subprojects/di-with-koin/src/main/kotlin/org/sdkotlin/koin/hello/EnglishGreetingService.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.koin.hello 2 | 3 | class EnglishGreetingService : GreetingService { 4 | 5 | companion object { 6 | const val GREETING = "Hi, World!" 7 | } 8 | 9 | override fun getGreeting() = GREETING 10 | } 11 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs-client/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.sdkotlin.buildlogic.global-exec") 3 | id("org.sdkotlin.buildlogic.kotlin-project") 4 | id("org.sdkotlin.buildlogic.test.unit-test-suite") 5 | } 6 | 7 | dependencies { 8 | 9 | api(projects.subprojects.kotlinForJavaDevs) 10 | } 11 | -------------------------------------------------------------------------------- /subprojects/typed-errors-in-kotlin/src/main/kotlin/org/sdkotlin/typederrors/FruitBasket.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.typederrors 2 | 3 | interface FruitBasket { 4 | 5 | val fruit: List 6 | } 7 | 8 | internal data class FruitBasketImpl( 9 | override val fruit: List = emptyList(), 10 | ) : FruitBasket 11 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/README.md: -------------------------------------------------------------------------------- 1 | # Introduction to Kotlin for Java Developers 2 | 3 | Code examples from 4 | the [San Diego Kotlin User Group](https://www.meetup.com/sd-kotlin/events/nlxltpyxjbjb/)' 5 | s meetings on introducing the [Kotlin](http://kotlinlang.org/) programming 6 | language to Java developers (and friends). 7 | -------------------------------------------------------------------------------- /subprojects/di-with-koin/src/it/kotlin/org/sdkotlin/koin/it/hello/test/testHelloModule.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.koin.it.hello.test 2 | 3 | import org.koin.dsl.module 4 | import org.sdkotlin.koin.hello.GreetingService 5 | 6 | internal val testHelloModule = module { 7 | 8 | single { TestGreetingService() } 9 | } 10 | -------------------------------------------------------------------------------- /subprojects/kotlin-dl/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.sdkotlin.buildlogic.kotlin-project") 3 | } 4 | 5 | dependencies { 6 | 7 | api(platform("org.sdkotlin.platforms:app-platform")) 8 | 9 | implementation(libs.kotlinx.dl.api) 10 | implementation(libs.kotlinx.dl.dataset) 11 | implementation(libs.kotlinx.dl.tensorflow) 12 | } 13 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/java/org/sdkotlin/intro/java/_02_helloworld/HelloJava.java: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.java._02_helloworld; 2 | 3 | import java.util.Arrays; 4 | 5 | public class HelloJava { 6 | 7 | public static void main(final String[] args) { 8 | System.out.println("args: " + Arrays.toString(args)); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /subprojects/typed-errors-in-kotlin/src/main/kotlin/org/sdkotlin/typederrors/Fruit.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.typederrors 2 | 3 | sealed interface Fruit { 4 | data class Apple(val hasWorm: Boolean) : Fruit 5 | data class Banana(val microSieverts: Double) : Fruit 6 | data class Grapes(val moreLikeRaisins: Boolean) : Fruit 7 | data object Orange : Fruit 8 | } 9 | -------------------------------------------------------------------------------- /platforms/plugins-platform/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java-platform") 3 | } 4 | 5 | dependencies { 6 | constraints { 7 | api(libs.dependencyAnalysis.gradlePluginDependency) 8 | api(libs.jvmDependencyConflictResolution.gradlePluginDependency) 9 | api(libs.kotlin.gradlePluginDependency) 10 | api(libs.ksp.gradlePluginDependency) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_30_sealedtypes/UnknownPlayer.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._30_sealedtypes 2 | 3 | // Direct subclasses can be in other files so long as they're in the same 4 | // package. 5 | 6 | class UnknownPlayer : Player { 7 | override val name: String = "Unknown" 8 | override val health: Int = 0 9 | } 10 | -------------------------------------------------------------------------------- /subprojects/testing-with-mokkery/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.sdkotlin.buildlogic.kotlin-project") 3 | id("org.sdkotlin.buildlogic.test.unit-test-suite") 4 | id("org.sdkotlin.buildlogic.mokkery-project") 5 | } 6 | 7 | dependencies { 8 | 9 | testImplementation(platform("org.sdkotlin.platforms:test-platform")) 10 | 11 | testImplementation(libs.mokkery.core) 12 | } 13 | -------------------------------------------------------------------------------- /subprojects/di-with-koin/src/main/kotlin/org/sdkotlin/koin/hello/HelloApp.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.koin.hello 2 | 3 | import org.koin.core.component.KoinComponent 4 | import org.koin.core.component.inject 5 | 6 | internal class HelloApp : KoinComponent { 7 | 8 | private val helloController: HelloController by inject() 9 | 10 | fun run() = println(helloController.sayHello()) 11 | } 12 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/javainterop/_XX_constants/ConstantsInKotlinObject.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.javainterop._XX_constants 2 | 3 | object ConstantsInKotlinObject { 4 | 5 | @JvmField 6 | val OBJECT_JVM_FIELD_VAL = "Testing @JvmField val in object" 7 | 8 | @JvmStatic 9 | val OBJECT_JVM_STATIC_VAL = "Testing @JvmStatic val in object" 10 | } 11 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/java/org/sdkotlin/intro/java/_17_constants/ConstantsInJava.java: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.java._17_constants; 2 | 3 | import java.math.BigInteger; 4 | 5 | public class ConstantsInJava { 6 | 7 | public static final String JAVA_CONST = "Constants in Java"; 8 | 9 | public static final BigInteger NON_PRIMITIVE_JAVA_CONST = BigInteger.ONE; 10 | } 11 | -------------------------------------------------------------------------------- /subprojects/di-with-koin/src/it/kotlin/org/sdkotlin/koin/it/hello/test/TestGreetingService.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.koin.it.hello.test 2 | 3 | import org.sdkotlin.koin.hello.GreetingService 4 | 5 | internal class TestGreetingService : GreetingService { 6 | 7 | companion object { 8 | const val TEST_GREETING = "Howdy, World!" 9 | } 10 | 11 | override fun getGreeting() = TEST_GREETING 12 | } 13 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs-client/src/main/kotlin/org/sdkotlin/intro/kotlin/_30_sealedtypes/OtherModulePlayer.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._30_sealedtypes 2 | 3 | // Direct subclasses of sealed classes can't be in other modules. 4 | 5 | // Does not compile... 6 | /* 7 | class OtherModulePlayer( 8 | override val name: String, 9 | override val health: Int, 10 | ) : Player 11 | */ 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | charset = utf-8 3 | max_line_length = 80 4 | end_of_line = lf 5 | trim_trailing_whitespace = true 6 | insert_final_newline = true 7 | indent_style = tab 8 | tab_width = 4 9 | 10 | [*.{yml,yaml}] 11 | indent_size = 2 12 | 13 | [detekt-baseline.xml] 14 | max_line_length = off 15 | 16 | [{.idea/**/*.xml,*.iml}] 17 | indent_size = 2 18 | max_line_length = off 19 | insert_final_newline = false 20 | -------------------------------------------------------------------------------- /subprojects/effective-kotlin/README.md: -------------------------------------------------------------------------------- 1 | # Effective Kotlin 2 | 3 | Code examples from the San Diego Kotlin User 4 | Group's [June 2018 meeting](https://www.meetup.com/sd-kotlin/events/nlxltpyxjbjb/) 5 | on popular Kotlin idioms, and how Kotlin addresses several items from Joshua 6 | Bloch's seminal book, 7 | _[Effective Java](https://www.pearson.com/us/higher-education/program/Bloch-Effective-Java-3rd-Edition/PGM1763855.html)_. 8 | -------------------------------------------------------------------------------- /subprojects/equalsverifier-with-kotlin/src/test/kotlin/org/sdkotlin/equalsverifier/delegation/BarImplTest.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.equalsverifier.delegation 2 | 3 | import nl.jqno.equalsverifier.EqualsVerifier 4 | import org.junit.jupiter.api.Test 5 | 6 | class BarImplTest { 7 | 8 | @Test 9 | fun `test equals, hashCode, and toString`() { 10 | 11 | EqualsVerifier.forClass(BarImpl::class.java).verify() 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /subprojects/equalsverifier-with-kotlin/src/test/kotlin/org/sdkotlin/equalsverifier/delegation/BazImplTest.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.equalsverifier.delegation 2 | 3 | import nl.jqno.equalsverifier.EqualsVerifier 4 | import org.junit.jupiter.api.Test 5 | 6 | class BazImplTest { 7 | 8 | @Test 9 | fun `test equals, hashCode, and toString`() { 10 | 11 | EqualsVerifier.forClass(BazImpl::class.java).verify() 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /subprojects/ksp-builder-generator/processor/test-project/src/main/kotlin/org/sdkotlin/buildergen/processor/it/TestEntity.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.buildergen.processor.it 2 | 3 | import org.sdkotlin.buildergen.api.annotations.GeneratedBuilder 4 | 5 | @GeneratedBuilder 6 | data class TestEntity( 7 | val testInt: Int, 8 | val testNullableInt: Int?, 9 | val testString: String, 10 | val testNullableString: String?, 11 | ) 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### JetBrains template 2 | 3 | .idea/* 4 | !.idea/codeStyles/ 5 | !.idea/inspectionProfiles/ 6 | !.idea/encodings.xml 7 | 8 | # Gradle: 9 | *.iml 10 | 11 | # IntelliJ 12 | out/ 13 | 14 | ### Gradle template 15 | .gradle 16 | build/ 17 | 18 | ### Gradle Profiler 19 | gradle-user-home/ 20 | profile-out*/ 21 | 22 | ### Kotlin 23 | .kotlin 24 | 25 | ### KotlinTest 26 | .kotlintest/ 27 | 28 | ### KotlinDL 29 | cache/ 30 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/java/org/sdkotlin/intro/java/_00_classes/ClassesInJava.java: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.java._00_classes; 2 | 3 | // Public class names must match the source file name in Java. 4 | 5 | public class ClassesInJava { 6 | //... 7 | } 8 | 9 | // You can have multiple classes in a file, but only one public class, 10 | // and curly braces are required. 11 | 12 | class Class { 13 | 14 | } 15 | -------------------------------------------------------------------------------- /subprojects/user-defined-integrals-in-kotlin/src/main/kotlin/org/sdkotlin/integral/Integral.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.integral 2 | 3 | interface Integral : Comparable where I : Comparable { 4 | 5 | val minValue: I 6 | val maxValue: I 7 | val zero: I 8 | val one: I 9 | 10 | operator fun plus(other: I): I 11 | operator fun minus(other: I): I 12 | operator fun unaryMinus(): I 13 | operator fun rem(other: I): I 14 | } 15 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_00_classes/ClassesInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._00_classes 2 | 3 | // Class name doesn't have to match file name, though doing so is idiomatic. 4 | 5 | class ClassesInKotlinExample { 6 | //... 7 | } 8 | 9 | // Unlike Java, you can have multiple public classes in a source file. 10 | 11 | // Curly braces are optional for empty classes. 12 | 13 | class Class 14 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_30_sealedtypes/otherpackage/OtherPackagePlayer.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("UnusedImport") 2 | 3 | package org.sdkotlin.intro.kotlin._30_sealedtypes.otherpackage 4 | 5 | // Direct subtypes of sealed types can't be in other packages. 6 | 7 | // Does not compile... 8 | /* 9 | class OtherPackagePlayer( 10 | override val name: String, 11 | override var health: Int, 12 | ) : Player 13 | */ 14 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/test/kotlin/org/sdkotlin/intro/kotlin/_XX_tdd/TddInKotlinTest.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._XX_tdd 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.junit.jupiter.api.Test 5 | 6 | internal class TddInKotlinTest { 7 | 8 | @Test 9 | fun `test TDD in Kotlin for smoke`() { 10 | 11 | val tddInKotlin = TddInKotlin() 12 | 13 | assertThat(tddInKotlin.weCanDoIt()).isTrue 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /platforms/app-platform/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java-platform") 3 | } 4 | 5 | javaPlatform { 6 | allowDependencies() 7 | } 8 | 9 | dependencies { 10 | 11 | api(platform(libs.koin.bom)) 12 | api(platform(libs.kotlinx.coroutines.bom)) 13 | 14 | constraints { 15 | api(libs.bundles.arrow.jvm) 16 | api(libs.jetbrains.annotations) 17 | api(libs.bundles.kotlinx.dl) 18 | api(libs.ksp.api) 19 | api(libs.org.json) 20 | api(libs.slf4j.simple) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /subprojects/ksp-builder-generator/processor/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.sdkotlin.buildlogic.kotlin-project") 3 | id("org.sdkotlin.buildlogic.ksp-processor-project") 4 | id("org.sdkotlin.buildlogic.test.unit-test-suite") 5 | } 6 | 7 | dependencies { 8 | 9 | implementation(projects.subprojects.kspBuilderGenerator.api.annotations) 10 | implementation(projects.subprojects.kspBuilderGenerator.api.builder) 11 | 12 | implementation(libs.bundles.kotlinpoet.ksp) 13 | } 14 | -------------------------------------------------------------------------------- /subprojects/sorting-in-kotlin/src/main/kotlin/org/sdkoltin/sort/BubbleSort.kt: -------------------------------------------------------------------------------- 1 | package org.sdkoltin.sort 2 | 3 | /** 4 | * Sorts the list in-place via a stable, iterative implementation of the Bubble 5 | * Sort algorithm. 6 | */ 7 | fun > MutableList.bubbleSort() { 8 | 9 | if (size <= 1) return 10 | 11 | for (i in 0 until size - 1) { 12 | for (j in 0 until size - i - 1) { 13 | if (this[j] > this[j + 1]) this.swap(j, j + 1) 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /subprojects/ksp-builder-generator/processor/test-project/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.sdkotlin.buildlogic.kotlin-project") 3 | id("org.sdkotlin.buildlogic.ksp-processed-project") 4 | id("org.sdkotlin.buildlogic.test.unit-test-suite") 5 | } 6 | 7 | dependencies { 8 | 9 | api(projects.subprojects.kspBuilderGenerator.api.annotations) 10 | api(projects.subprojects.kspBuilderGenerator.api.builder) 11 | 12 | ksp(projects.subprojects.kspBuilderGenerator.processor) 13 | } 14 | -------------------------------------------------------------------------------- /platforms/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencyResolutionManagement { 2 | versionCatalogs { 3 | create("libs") { 4 | from(files("../gradle/libs.versions.toml")) 5 | } 6 | } 7 | } 8 | 9 | rootProject.name = "platforms" 10 | 11 | gradle.beforeProject { 12 | // Set group and version properties for all projects 13 | group = "org.sdkotlin.platforms" 14 | version = "1.0.0-SNAPSHOT" 15 | } 16 | 17 | include("app-platform") 18 | include("test-platform") 19 | include("plugins-platform") 20 | -------------------------------------------------------------------------------- /platforms/test-platform/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java-platform") 3 | } 4 | 5 | javaPlatform { 6 | allowDependencies() 7 | } 8 | 9 | dependencies { 10 | 11 | api(platform(libs.junit.bom)) 12 | api(platform(libs.koin.bom)) 13 | api(platform(libs.kotest.bom)) 14 | 15 | constraints { 16 | api(libs.assertj) 17 | api(libs.equalsverifier) 18 | api(libs.mockito) 19 | api(libs.mockito.kotlin) 20 | api(libs.mokkery.core) 21 | api(libs.bundles.mockk.jvm) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs-client/src/main/kotlin/org/sdkotlin/intro/kotlin/_30_sealedtypes/SealedTypeSmartCastFromOtherModule.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._30_sealedtypes 2 | 3 | fun main() { 4 | 5 | // Sealed types from other modules can be smart cast by type. 6 | 7 | val output = when (val player = playerFactory()) { 8 | is HumanPlayer -> player.topScore 9 | is NPC -> player.attack() 10 | is UnknownPlayer -> player.name 11 | } 12 | 13 | println(output) 14 | } 15 | -------------------------------------------------------------------------------- /subprojects/sorting-in-kotlin/src/test/kotlin/org/sdkoltin/sort/SortTestCases.kt: -------------------------------------------------------------------------------- 1 | package org.sdkoltin.sort 2 | 3 | internal object SortTestCases { 4 | 5 | val IN_PLACE_SORT_TEST_CASES: List> = listOf( 6 | mutableListOf(), 7 | mutableListOf(1), 8 | mutableListOf(1, 2), 9 | mutableListOf(2, 1), 10 | mutableListOf(1, 2, 3), 11 | mutableListOf(2, 1, 3), 12 | mutableListOf(1, 3, 2), 13 | mutableListOf(3, 1, 2), 14 | mutableListOf(3, 2, 1), 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /detekt-rules/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | gradlePluginPortal() 5 | } 6 | } 7 | 8 | dependencyResolutionManagement { 9 | @Suppress("UnstableApiUsage") 10 | repositories { 11 | mavenCentral() 12 | gradlePluginPortal() 13 | } 14 | versionCatalogs { 15 | create("libs") { 16 | from(files("../gradle/libs.versions.toml")) 17 | } 18 | } 19 | } 20 | 21 | includeBuild("../platforms") 22 | 23 | rootProject.name = "detekt-rules" 24 | -------------------------------------------------------------------------------- /subprojects/equalsverifier-with-kotlin/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.sdkotlin.buildlogic.kotlin-project") 3 | id("org.sdkotlin.buildlogic.test.unit-test-suite") 4 | } 5 | 6 | dependencies { 7 | 8 | testImplementation(platform("org.sdkotlin.platforms:test-platform")) 9 | 10 | testImplementation(libs.equalsverifier) 11 | 12 | testRuntimeOnly(libs.mockito) { 13 | because("Used by EqualsVerifier for prefab value generation.") 14 | } 15 | testRuntimeOnly(kotlin("reflect")) 16 | } 17 | -------------------------------------------------------------------------------- /subprojects/typed-errors-in-kotlin/src/main/kotlin/org/sdkotlin/typederrors/TypedError.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.typederrors 2 | 3 | sealed interface TypedError { 4 | 5 | sealed interface BadFruitTypedError : TypedError { 6 | 7 | data object BadAppleTypedError : BadFruitTypedError 8 | 9 | data object RadioactiveBananaTypedError : BadFruitTypedError 10 | 11 | data object ShriveledGrapesTypedError : BadFruitTypedError 12 | } 13 | 14 | data object BadBasketTypedError : TypedError 15 | } 16 | -------------------------------------------------------------------------------- /subprojects/di-with-koin/src/main/kotlin/org/sdkotlin/koin/hello/RandomGreetingService.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.koin.hello 2 | 3 | import kotlin.random.Random 4 | 5 | class RandomGreetingService : GreetingService { 6 | 7 | companion object { 8 | val GREETINGS = listOf( 9 | "Hello, World!", 10 | "¡Hola Mundo!", 11 | "Salamu, Dunia!", 12 | "Aloha nui kāua!" 13 | ) 14 | } 15 | 16 | override fun getGreeting(): String { 17 | 18 | return GREETINGS[Random.nextInt(GREETINGS.size)] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_03_toplevel/TopLevelInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._03_toplevel 2 | 3 | // You can define top level properties and functions in Kotlin. 4 | 5 | var sanDiego = true 6 | 7 | // They can be private, in which case they're only visible within the same file. 8 | 9 | private var justHere = true 10 | 11 | fun printWeather() { 12 | 13 | if (sanDiego) { 14 | println("It's sunny!") 15 | } 16 | 17 | println(justHere) 18 | } 19 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_07_equality/EqualityInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._07_equality 2 | 3 | fun main() { 4 | 5 | println(java.lang.String("foo") === java.lang.String("foo")) 6 | println(java.lang.String("foo") == java.lang.String("foo")) 7 | 8 | // Equivalent to "==" 9 | println("foo".equals("foo")) 10 | 11 | // TODO: Research and explain why this prints "true"... 12 | println("fo" + "o" === "foo") 13 | 14 | println("foo" == "foo") 15 | } 16 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_08_3_extensionfunctions/otherpackage/ExtensionFunctionScopingInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._08_3_extensionfunctions.otherpackage 2 | 3 | import org.sdkotlin.intro.kotlin._08_3_extensionfunctions.shuffled 4 | 5 | fun main() { 6 | 7 | // Extension functions aren't really added to the receiver classes. They 8 | // must be imported to be used in a scope other than where they are defined. 9 | 10 | println("Hello".shuffled()) 11 | } 12 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/meetup/firstwednesday/SDKotlin.kt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | package org.sdkotlin.meetup.firstwednesday 9 | 10 | import java.time.DayOfWeek.WEDNESDAY 11 | import java.time.LocalDate.now 12 | import java.time.temporal.TemporalAdjusters.dayOfWeekInMonth 13 | 14 | fun firstWednesday() = 15 | now().equals(now().with(dayOfWeekInMonth(1, WEDNESDAY))) 16 | 17 | fun main() { 18 | when { 19 | firstWednesday() -> println("SD Kotlin time!") 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_30_sealedtypes/otherpackage/AiNPC.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._30_sealedtypes.otherpackage 2 | 3 | import org.sdkotlin.intro.kotlin._30_sealedtypes.NPC 4 | 5 | // Indirect subtypes of open types that are sealed subtypes are allowed in 6 | // other packages and modules. 7 | 8 | data class AiNPC( 9 | override val name: String, 10 | override var health: Int = 100, 11 | ) : NPC() { 12 | 13 | override fun attack() = "AI is coming to get ya!" 14 | } 15 | -------------------------------------------------------------------------------- /subprojects/tdd-in-kotlin/src/testFixtures/kotlin/org/sdkotlin/tdd/testfixtures/InternalComponentTestDouble.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.tdd.testfixtures 2 | 3 | class InternalComponentTestDouble( 4 | private val testInt: Int = TEST_INT, 5 | private val testString: String = TEST_STRING, 6 | ) : InternalComponent { 7 | 8 | companion object { 9 | const val TEST_INT = 42 10 | const val TEST_STRING = "Testing" 11 | } 12 | 13 | override fun getInt(): Int = testInt 14 | 15 | override fun getString(): String = testString 16 | } 17 | -------------------------------------------------------------------------------- /subprojects/typed-errors-in-kotlin/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 2 | 3 | plugins { 4 | id("org.sdkotlin.buildlogic.kotlin-project") 5 | id("org.sdkotlin.buildlogic.test.unit-test-suite") 6 | } 7 | 8 | dependencies { 9 | 10 | api(platform("org.sdkotlin.platforms:app-platform")) 11 | 12 | api(libs.bundles.arrow.jvm) 13 | } 14 | 15 | tasks { 16 | withType().configureEach { 17 | compilerOptions { 18 | freeCompilerArgs.add("-Xcontext-receivers") 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /subprojects/di-with-koin/src/test/kotlin/org/sdkotlin/koin/hello/RandomGreetingServiceTest.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.koin.hello 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.junit.jupiter.api.Test 5 | 6 | internal class RandomGreetingServiceTest { 7 | 8 | @Test 9 | fun `should return a random greeting`() { 10 | 11 | val greetingService: GreetingService = RandomGreetingService() 12 | 13 | val response = greetingService.getGreeting() 14 | 15 | assertThat(response).isIn(RandomGreetingService.GREETINGS) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/javainterop/_XX_constants/ConstantsInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.javainterop._XX_constants 2 | 3 | @JvmField 4 | val JVM_FIELD_VAL = "Testing @JvmField val" 5 | 6 | @JvmField 7 | val NON_PRIMITIVE_JVM_FIELD_VAL = intArrayOf(1) 8 | 9 | // Only members in named objects and companion objects can be annotated with '@JvmStatic' 10 | 11 | //@JvmStatic 12 | //val JVM_STATIC_VAL = "Testing @JvmStatic val" 13 | 14 | //@JvmStatic 15 | //const val JVM_STATIC_VAL = "Testing @JvmStatic const val" 16 | -------------------------------------------------------------------------------- /subprojects/user-defined-integrals-in-kotlin/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.sdkotlin.buildlogic.kotlin-project") 3 | id("org.sdkotlin.buildlogic.mockk-project") 4 | id("org.sdkotlin.buildlogic.test.unit-test-suite") 5 | } 6 | 7 | dependencies { 8 | 9 | testImplementation(platform("org.sdkotlin.platforms:test-platform")) 10 | 11 | testImplementation(libs.equalsverifier) 12 | 13 | testRuntimeOnly(libs.mockito) { 14 | because("Used by EqualsVerifier for prefab value generation.") 15 | } 16 | testRuntimeOnly(kotlin("reflect")) 17 | } 18 | -------------------------------------------------------------------------------- /detekt-rules/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(libs.plugins.kotlin.gradlePlugin) 3 | id("jvm-test-suite") 4 | } 5 | 6 | group = "org.sdkotlin.detekt" 7 | version = "1.0.0-SNAPSHOT" 8 | 9 | dependencies { 10 | 11 | compileOnly(libs.detekt.api) 12 | 13 | testImplementation(libs.assertj) 14 | testImplementation(libs.detekt.test) 15 | } 16 | 17 | @Suppress("UnstableApiUsage") 18 | testing { 19 | suites { 20 | configureEach { 21 | if (this is JvmTestSuite) { 22 | useJUnitJupiter(libs.versions.junit.get()) 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /subprojects/di-with-koin/src/test/kotlin/org/sdkotlin/koin/hello/EnglishGreetingServiceTest.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.koin.hello 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.junit.jupiter.api.Test 5 | 6 | internal class EnglishGreetingServiceTest { 7 | 8 | @Test 9 | fun `should return an English greeting`() { 10 | 11 | val greetingService: GreetingService = EnglishGreetingService() 12 | 13 | val response = greetingService.getGreeting() 14 | 15 | assertThat(response).isIn(EnglishGreetingService.GREETING) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /subprojects/equalsverifier-with-kotlin/src/test/kotlin/org/sdkotlin/equalsverifier/delegation/LazyEqualsImplTest.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.equalsverifier.delegation 2 | 3 | import nl.jqno.equalsverifier.EqualsVerifier 4 | import org.junit.jupiter.api.Test 5 | 6 | class LazyEqualsImplTest { 7 | 8 | @Test 9 | fun `test equals, hashCode, and toString`() { 10 | 11 | EqualsVerifier 12 | // Required per https://github.com/jqno/equalsverifier/issues/1097 13 | .forExamples(LazyEqualsImpl(1, "red"), LazyEqualsImpl(2, "blue")) 14 | .verify() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /detekt-rules/src/main/kotlin/org/sdkotlin/detekt/junit/JUnitRuleSetProvider.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.detekt.junit 2 | 3 | import io.gitlab.arturbosch.detekt.api.Config 4 | import io.gitlab.arturbosch.detekt.api.RuleSet 5 | import io.gitlab.arturbosch.detekt.api.RuleSetProvider 6 | 7 | class JUnitRuleSetProvider : RuleSetProvider { 8 | 9 | override val ruleSetId: String = "junit" 10 | 11 | override fun instance(config: Config): RuleSet = 12 | RuleSet( 13 | ruleSetId, 14 | listOf( 15 | InvalidTestFactoryReturnType(config), 16 | ), 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_03_toplevel/usage/TopLevelUsageInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._03_toplevel.usage 2 | 3 | // Top level variables and functions are package scoped... 4 | 5 | import org.sdkotlin.intro.kotlin._03_toplevel.printWeather 6 | import org.sdkotlin.intro.kotlin._03_toplevel.sanDiego 7 | 8 | // Can't access private top-level properties or functions 9 | //import org.sdkotlin.intro.kotlin._03_toplevel.justHere 10 | 11 | fun main() { 12 | println(sanDiego) 13 | printWeather() 14 | } 15 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_08_2_tailrecursivefunctions/TailRecursionDebugging.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._08_2_tailrecursivefunctions 2 | 3 | /** 4 | * Debugger didn't used to show variable changes in tailrec functions per 5 | * [KT-47203](https://youtrack.jetbrains.com/issue/KT-47203), but now it does. 6 | */ 7 | tailrec fun tailrecFun(number: Int): Int = 8 | if (number < 10) { 9 | number 10 | } else { 11 | tailrecFun(number - 1) // breakpoint here 12 | } 13 | 14 | fun main() { 15 | tailrecFun(15) 16 | } 17 | -------------------------------------------------------------------------------- /subprojects/ksp-builder-generator/api/annotations/src/test/kotlin/org/sdkotlin/buildergen/api/annotations/GeneratedBuilderTest.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.buildergen.api.annotations 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.junit.jupiter.api.Test 5 | 6 | internal class GeneratedBuilderTest { 7 | 8 | @GeneratedBuilder 9 | data class TestEntity( 10 | val b: Boolean, 11 | ) 12 | 13 | @Test 14 | fun `verify GeneratedBuilder target`() { 15 | 16 | assertThat(TestEntity::class.java) 17 | .hasAnnotation(GeneratedBuilder::class.java) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.sdkotlin.buildlogic.global-exec") 3 | id("org.sdkotlin.buildlogic.kotlin-project") 4 | id("org.sdkotlin.buildlogic.test.unit-test-suite") 5 | } 6 | 7 | dependencies { 8 | 9 | api(platform("org.sdkotlin.platforms:app-platform")) 10 | 11 | api(libs.bundles.arrow.jvm) 12 | 13 | implementation(libs.bundles.kotlinx.coroutines.jvm) 14 | 15 | compileOnly(libs.jetbrains.annotations) 16 | 17 | testImplementation(platform("org.sdkotlin.platforms:test-platform")) 18 | 19 | testImplementation(libs.equalsverifier) 20 | } 21 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs-client/src/main/kotlin/org/sdkotlin/intro/kotlin/_30_sealedtypes/otherpackage/OtherModuleAndPackageNPC.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._30_sealedtypes.otherpackage 2 | 3 | import org.sdkotlin.intro.kotlin._30_sealedtypes.NPC 4 | 5 | // Indirect subtypes of sealed types can be in other modules. 6 | 7 | class OtherModuleAndPackageNPC : NPC() { 8 | 9 | override val name: String = "Invincible" 10 | override val health: Int = 100 11 | 12 | override fun attack() = "$name attack!" 13 | } 14 | 15 | fun main() { 16 | println(OtherModuleAndPackageNPC().attack()) 17 | } 18 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/java/org/sdkotlin/intro/java/_02_helloworld/HelloJava21.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Java 21+ is previewing "Unnamed Classes and Instance Main Methods": 3 | * 9 | */ 10 | void main() { 11 | System.out.println("Hello World"); 12 | } 13 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/java/org/sdkotlin/intro/java/_16_static/StaticInJava.java: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.java._16_static; 2 | 3 | import org.sdkotlin.intro.kotlin._16_static.StaticInKotlin; 4 | 5 | public class StaticInJava { 6 | 7 | public static final String CONSTANT = "Death & Taxes"; 8 | 9 | public static void saySomething() { 10 | System.out.println(CONSTANT); 11 | } 12 | 13 | public static void main(final String[] args) { 14 | StaticInJava.saySomething(); 15 | StaticInKotlin.saySomething(); 16 | 17 | new StaticInJava().saySomething(); 18 | new StaticInKotlin().saySomething(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/javainterop/_XX_constants/ConstantsInKotlinCompanion.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.javainterop._XX_constants 2 | 3 | class ConstantsInKotlinCompanion { 4 | 5 | companion object { 6 | 7 | @JvmField 8 | val COMPANION_JVM_FIELD_VAL = "Testing @JvmField val in object" 9 | 10 | @JvmStatic 11 | val COMPANION_JVM_STATIC_VAL = "Testing @JvmStatic val in object" 12 | 13 | // '@JvmStatic' annotation is useless for const or '@JvmField' properties 14 | 15 | //@JvmStatic 16 | //const val COMPANION_JVM_STATIC_CONST_VAL = "Testing @JvmStatic const val in object" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/java/org/sdkotlin/intro/java/_22_nullsafety/NullSafetyInJava.java: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.java._22_nullsafety; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | public class NullSafetyInJava { 7 | 8 | public static final String MAYBE_NULL = null; 9 | 10 | @NotNull 11 | public static final String NOT_NULL = ""; 12 | 13 | @Nullable 14 | public static final String NULL = null; 15 | 16 | @NotNull 17 | public static final String SURPRISE = null; 18 | 19 | @NotNull 20 | public static String surpriseMethod() { 21 | return null; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /subprojects/equalsverifier-with-kotlin/src/main/kotlin/org/sdkotlin/equalsverifier/delegation/LazyEquals.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.equalsverifier.delegation 2 | 3 | interface LazyEquals { 4 | val foo: Int 5 | val bar: String 6 | } 7 | 8 | class LazyEqualsImpl(foo: Int, bar: String) : LazyEquals { 9 | 10 | override val foo: Int by lazy { foo } 11 | override val bar: String by lazy { bar } 12 | 13 | override fun equals(other: Any?) = 14 | other is LazyEqualsImpl && other.foo == foo && other.bar == bar 15 | 16 | override fun hashCode() = foo.hashCode() + 31 * bar.hashCode() 17 | override fun toString() = "LazyEqualsImpl(foo=$foo, bar=$bar)" 18 | } 19 | -------------------------------------------------------------------------------- /subprojects/testing-with-mokkery/src/test/kotlin/org/sdkotlin/testing/mokkery/MokkeryValueClassParameterTest.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.testing.mokkery 2 | 3 | import dev.mokkery.answering.returns 4 | import dev.mokkery.every 5 | import dev.mokkery.mock 6 | import org.assertj.core.api.Assertions.assertThat 7 | import org.junit.jupiter.api.Test 8 | 9 | class MokkeryValueClassParameterTest { 10 | 11 | @Test 12 | fun `test value class parameter`() { 13 | 14 | val testValue = ValueClass("testing") 15 | 16 | val mock = mock { 17 | every { v } returns testValue 18 | } 19 | 20 | assertThat(mock.v).isEqualTo(testValue) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /subprojects/sorting-in-kotlin/src/test/kotlin/org/sdkoltin/sort/BubbleSortKtTest.kt: -------------------------------------------------------------------------------- 1 | package org.sdkoltin.sort 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.junit.jupiter.api.DynamicTest.dynamicTest 5 | import org.junit.jupiter.api.TestFactory 6 | import org.sdkoltin.sort.SortTestCases.IN_PLACE_SORT_TEST_CASES 7 | 8 | internal class BubbleSortKtTest { 9 | 10 | @TestFactory 11 | fun `test bubble sort`() = 12 | IN_PLACE_SORT_TEST_CASES.map { mutableList -> 13 | 14 | dynamicTest("test bubble sort for $mutableList") { 15 | 16 | mutableList.bubbleSort() 17 | 18 | assertThat(mutableList).isSorted 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /subprojects/user-defined-integrals-in-kotlin/src/test/kotlin/org/sdkotlin/integral/IntegralProgressionTest.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.integral 2 | 3 | import io.mockk.mockk 4 | import nl.jqno.equalsverifier.EqualsVerifier 5 | import org.junit.jupiter.api.Test 6 | 7 | class IntegralProgressionTest { 8 | 9 | @Test 10 | fun `test equals, hashCode, and toString`() { 11 | EqualsVerifier.forClass(IntegralProgression::class.java) 12 | // Required per https://github.com/jqno/equalsverifier/issues/1082. 13 | .withPrefabValues( 14 | Integral::class.java, 15 | mockk(relaxed = true), 16 | mockk(relaxed = true), 17 | ) 18 | .verify() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_05_1_unsigned_types/UnsignedTypesInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._05_1_unsigned_types 2 | 3 | // Kotlin 1.5 on supports unsigned types and "u/U"-suffixed literals 4 | 5 | val uByte: UByte = 255u // or uppercase 255U 6 | val hexUByte: UByte = 0x01u 7 | val binUByte: UByte = 0b0000_0001u 8 | val uShort: UShort = UShort.MAX_VALUE 9 | val uInt = 0u // UInt inferred type if literal fits 10 | val uLong = 0xFF_FF_FF_FF_01u // ULong inferred when literal doesn't fit UInt 11 | 12 | // 'lateinit' modifier is not allowed on properties of unsigned types 13 | //lateinit var lateUInt: UInt 14 | -------------------------------------------------------------------------------- /subprojects/ksp-builder-generator/processor/src/main/kotlin/org/sdkotlin/buildergen/processor/BuilderGenSymbolProcessorProvider.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.buildergen.processor 2 | 3 | import com.google.devtools.ksp.processing.SymbolProcessor 4 | import com.google.devtools.ksp.processing.SymbolProcessorEnvironment 5 | import com.google.devtools.ksp.processing.SymbolProcessorProvider 6 | 7 | class BuilderGenSymbolProcessorProvider : SymbolProcessorProvider { 8 | 9 | override fun create( 10 | environment: SymbolProcessorEnvironment, 11 | ): SymbolProcessor = 12 | BuilderGenSymbolProcessor( 13 | environment.codeGenerator, 14 | environment.logger, 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /subprojects/effective-kotlin/src/main/kotlin/org/sdkotlin/meetup/effectivejava/item62/AvoidStrings.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.meetup.effectivejava.item62 2 | 3 | /* Effective Java 4 | Item 62: Avoid strings where other types are more appropriate 5 | */ 6 | 7 | // Data classes in Kotlin are so easy! 8 | 9 | data class EmailAddress(val address: String, val name: String) 10 | 11 | fun send(emailAddress: String) { 12 | println("Sending to $emailAddress") 13 | } 14 | 15 | // vs. 16 | 17 | fun send(emailAddress: EmailAddress) { 18 | println("Sending to ${emailAddress.address}") 19 | } 20 | 21 | // It's safer and easier to add a "name" property to the data class in the future! 22 | -------------------------------------------------------------------------------- /subprojects/equalsverifier-with-kotlin/src/test/kotlin/org/sdkotlin/equalsverifier/recursive/DataClassWithSealedTypePropertyTest.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.equalsverifier.recursive 2 | 3 | import nl.jqno.equalsverifier.EqualsVerifier 4 | import org.junit.jupiter.api.Test 5 | 6 | internal class DataClassWithSealedTypePropertyTest { 7 | 8 | @Test 9 | fun `test equals, hashCode, and toString`() { 10 | EqualsVerifier.forClass(DataClassWithSealedTypeProperty::class.java) 11 | // Required per https://github.com/jqno/equalsverifier/issues/1081. 12 | .withPrefabValues( 13 | Foo::class.java, 14 | FooImpl(1), 15 | FooImpl(2), 16 | ) 17 | .verify() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /subprojects/tdd-in-kotlin/src/test/kotlin/org/sdkotlin/tdd/assertj/FloatingPointAssertionsTest.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.tdd.assertj 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.assertj.core.api.Assertions.within 5 | import org.assertj.core.api.Assertions.withinPercentage 6 | import org.junit.jupiter.api.Test 7 | 8 | private const val OFFSET = 0.00001 9 | 10 | internal class FloatingPointAssertionsTest { 11 | 12 | @Test 13 | fun `test double assertion`() { 14 | 15 | val expected = 0.3 16 | val actual: Double = 0.1 + 0.2 17 | 18 | assertThat(actual) 19 | .isEqualTo(expected, within(OFFSET)) 20 | .isCloseTo(expected, withinPercentage(1.0)) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /subprojects/effective-kotlin/src/main/kotlin/org/sdkotlin/meetup/effectivejava/item58/PreferForEach.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.meetup.effectivejava.item58 2 | 3 | /* Effective Java 4 | Item 58: Prefer for-each loops to traditional for loops 5 | */ 6 | 7 | // Kotlin only has for-each loops! 8 | 9 | fun main() { 10 | 11 | val shapes = arrayOf("Circle", "Triangle", "Rectangle") 12 | 13 | /* Doesn't compile... 14 | for (int i; i < shapes.size; i++) { 15 | println("The shape is ${shapes[i]}") 16 | } 17 | */ 18 | 19 | for (i: Int in shapes.indices) { 20 | println("The shape is ${shapes[i]}") 21 | } 22 | 23 | for (shape: String in shapes) { 24 | println("Again the shape is $shape") 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/java/org/sdkotlin/intro/java/_24_arrays/ArraysInJava.java: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.java._24_arrays; 2 | 3 | public class ArraysInJava { 4 | 5 | public static void main(final String[] args) { 6 | 7 | // Java has array literals. 8 | 9 | final int[] ints = {1, 2, 3}; 10 | 11 | // Array literals can be used with local variable type inference if you 12 | // also include the initializer. 13 | 14 | //var nope = {1, 2, 3}; // Does not compile. 15 | 16 | final var inferredInts = new int[]{1, 2, 3}; 17 | 18 | // Array literals can be used for multidimensional arrays. 19 | 20 | final var twoDInferredInts = new int[][]{{1, 2, 3}, {4, 5, 6}}; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /subprojects/tdd-in-kotlin/src/test/kotlin/org/sdkotlin/tdd/testfixtures/InternalComponentTest.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.tdd.testfixtures 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.junit.jupiter.api.Test 5 | import org.sdkotlin.tdd.testfixtures.InternalComponentTestDouble.Companion.TEST_INT 6 | import org.sdkotlin.tdd.testfixtures.InternalComponentTestDouble.Companion.TEST_STRING 7 | 8 | internal class InternalComponentTest { 9 | 10 | @Test 11 | fun testInternalComponent() { 12 | 13 | val internalComponent: InternalComponent = InternalComponentTestDouble() 14 | 15 | assertThat(internalComponent.getInt()).isEqualTo(TEST_INT) 16 | 17 | assertThat(internalComponent.getString()).isEqualTo(TEST_STRING) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_04_imports/ImportsInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._04_imports 2 | 3 | // Imports in Kotlin are very similar to Java. 4 | 5 | import java.math.BigDecimal 6 | import kotlin.math.PI 7 | 8 | // If there is a name collision, the 'as' keyword can be used. 9 | 10 | import org.sdkotlin.intro.kotlin._04_imports.otherpackage.Thing1 as Thing2 11 | 12 | class Thing1 13 | 14 | var lawOfLargeNumbers = BigDecimal.TEN 15 | 16 | var thing1 = Thing1() 17 | var thing2 = Thing2() 18 | 19 | fun main() { 20 | println(PI) 21 | println(lawOfLargeNumbers) 22 | println("Thing1 type: " + thing1::class.qualifiedName) 23 | println("Thing2 type: " + thing2::class.qualifiedName) 24 | } 25 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/java/org/sdkotlin/intro/java/_10_selection/SelectionInJava.java: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.java._10_selection; 2 | 3 | public class SelectionInJava { 4 | 5 | public static void main(final String[] args) { 6 | 7 | if (true) { 8 | System.out.println(true); 9 | } else if (false) { 10 | System.out.println(false); 11 | } else { 12 | System.out.println("Huh?"); 13 | } 14 | 15 | final var x = (true) ? 1 : 0; 16 | 17 | switch (x) { 18 | case 0: 19 | System.out.println("x == 0"); 20 | case 1: 21 | System.out.println("x == 1"); 22 | case 2: 23 | System.out.println("x == 1 (Oops, forgot the break!)"); 24 | default: 25 | System.out.println("Double oops!"); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /subprojects/di-with-koin/src/test/kotlin/org/sdkotlin/koin/hello/SimpleHelloControllerTest.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.koin.hello 2 | 3 | import com.nhaarman.mockitokotlin2.doReturn 4 | import com.nhaarman.mockitokotlin2.mock 5 | import org.assertj.core.api.Assertions.assertThat 6 | import org.junit.jupiter.api.Test 7 | 8 | internal class SimpleHelloControllerTest { 9 | 10 | @Test 11 | fun `should say hello`() { 12 | 13 | val expectedGreeting = "Hi!" 14 | 15 | val helloService = mock { 16 | on { getGreeting() } doReturn expectedGreeting 17 | } 18 | 19 | val helloController = SimpleHelloController(helloService) 20 | 21 | val greeting = helloController.sayHello() 22 | 23 | assertThat(greeting).isEqualTo(expectedGreeting) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.caching=true 2 | org.gradle.configuration-cache=true 3 | org.gradle.configuration-cache.parallel=true 4 | org.gradle.configureondemand=true 5 | org.gradle.jvmargs=-Xmx4g 6 | org.gradle.kotlin.dsl.skipMetadataVersionCheck=false 7 | org.gradle.parallel=true 8 | 9 | # KSP and Gradle Versions Plugin `dependencyUpdates` task fails with: 10 | #org.gradle.unsafe.isolated-projects=true 11 | 12 | # Explicitly enable filesystem watching so Gradle doesn't go through its 13 | # default of trying to evaluate whether it can be enabled automatically, which 14 | # can be slow on Windows machines with mapped network drives: 15 | # https://github.com/gradle/gradle/issues/17955. 16 | org.gradle.vfs.watch=true 17 | 18 | dependency.analysis.print.build.health=true 19 | -------------------------------------------------------------------------------- /subprojects/effective-kotlin/src/main/kotlin/org/sdkotlin/meetup/effectivejava/item3and4/Singletons.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.meetup.effectivejava.item3and4 2 | 3 | /* Effective Java 4 | Item 3: Enforce the singleton property with a private constructor or an enum type 5 | Item 4: Enforce noninstantiability with a private constructor 6 | */ 7 | 8 | // Kotlin has the singleton pattern built into the language using `object` 9 | // definitions. 10 | 11 | object HelloSingleton { 12 | 13 | const val DEFAULT_MOOD = "fine" 14 | 15 | private var mood = DEFAULT_MOOD 16 | 17 | fun howYouDoin() { 18 | mood = "great" 19 | println("${mood.replaceFirstChar { it.uppercase() }}, thanks for asking!") 20 | } 21 | } 22 | 23 | fun main() { 24 | 25 | HelloSingleton.howYouDoin() 26 | } 27 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_XX_try_w_resources/TryWithResourcesInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._XX_try_w_resources 2 | 3 | import java.io.IOException 4 | import java.nio.file.Files 5 | import java.nio.file.Paths 6 | import java.util.stream.Collectors.joining 7 | 8 | fun main() { 9 | 10 | val classLoader = Thread.currentThread().contextClassLoader 11 | val resource = classLoader.getResource("hello.txt") 12 | ?: throw IllegalArgumentException("File not found!") 13 | val path = Paths.get(resource.toURI()) 14 | 15 | try { 16 | Files.lines(path).use { lines -> 17 | 18 | val data = lines.collect(joining("\n")) 19 | 20 | println(data) 21 | } 22 | } catch (e: IOException) { 23 | e.printStackTrace() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_16_static/StaticInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._16_static 2 | 3 | import org.sdkotlin.intro.java._16_static.StaticInJava 4 | 5 | class StaticInKotlin { 6 | 7 | companion object { 8 | 9 | const val CONSTANT = "Unchanging" 10 | 11 | @JvmStatic 12 | fun saySomething() { 13 | println(CONSTANT) 14 | } 15 | } 16 | 17 | val instanceProperty = "Hello" 18 | fun instanceFunction() = "World" 19 | } 20 | 21 | fun main() { 22 | 23 | StaticInKotlin.saySomething() 24 | StaticInKotlin.CONSTANT 25 | 26 | StaticInKotlin().instanceFunction() 27 | 28 | StaticInJava.saySomething() 29 | 30 | // StaticInJava().saySomething() // Does not compile 31 | // StaticInKotlin().saySomething() // Does not compile 32 | } 33 | -------------------------------------------------------------------------------- /subprojects/tdd-in-kotlin/src/main/kotlin/org/sdkotlin/tdd/fizzbuzz/FizzBuzz.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.tdd.fizzbuzz 2 | 3 | fun main() { 4 | 5 | println("Function-oriented...") 6 | fizzBuzz1to100(::println) 7 | 8 | println("Object-oriented...") 9 | fizzBuzz1to100(ConsolePrinter()) 10 | } 11 | 12 | fun fizzBuzz1to100() { 13 | (1..100).forEach { println(fizzBuzzOf(it)) } 14 | } 15 | 16 | fun fizzBuzz1to100(printFunction: (String) -> Unit) { 17 | (1..100).forEach { printFunction(fizzBuzzOf(it)) } 18 | } 19 | 20 | fun fizzBuzz1to100(printer: Printer) { 21 | fizzBuzz1to100 { fizzBuzz -> printer.print(fizzBuzz) } 22 | } 23 | 24 | internal fun fizzBuzzOf(n: Int): String { 25 | if (n % 15 == 0) return "FizzBuzz" 26 | if (n % 5 == 0) return "Buzz" 27 | if (n % 3 == 0) return "Fizz" 28 | return n.toString() 29 | } 30 | -------------------------------------------------------------------------------- /subprojects/equalsverifier-with-kotlin/src/main/kotlin/org/sdkotlin/equalsverifier/delegation/DelegatedEquals.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.equalsverifier.delegation 2 | 3 | interface Foo { 4 | val foo: Int 5 | } 6 | 7 | interface Bar { 8 | val bar: Int 9 | } 10 | 11 | data class BarImpl(override val bar: Int) : Bar 12 | 13 | class FooBarImpl(barValue: Int) : Foo, Bar by BarImpl(barValue) { 14 | 15 | override val foo = -bar 16 | 17 | override fun equals(other: Any?): Boolean { 18 | if (this === other) return true 19 | if (other !is FooBarImpl) return false 20 | return bar == other.bar 21 | } 22 | 23 | override fun hashCode(): Int = bar 24 | override fun toString(): String = "FooBarImpl(foo=$bar)" 25 | } 26 | 27 | interface Baz { 28 | val bar: Bar 29 | } 30 | 31 | data class BazImpl(override val bar: Bar) : Baz 32 | -------------------------------------------------------------------------------- /subprojects/di-with-koin/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 2 | 3 | plugins { 4 | id("org.sdkotlin.buildlogic.kotlin-project") 5 | id("org.sdkotlin.buildlogic.test.integration-test-suite") 6 | id("org.sdkotlin.buildlogic.test.unit-test-suite") 7 | id("org.sdkotlin.buildlogic.mockito-kotlin-project") 8 | } 9 | 10 | dependencies { 11 | 12 | api(platform("org.sdkotlin.platforms:app-platform")) 13 | 14 | implementation(libs.bundles.koin.jvm) 15 | 16 | compileOnly(libs.jetbrains.annotations) 17 | 18 | integrationTestImplementation(platform("org.sdkotlin.platforms:test-platform")) 19 | 20 | integrationTestImplementation(libs.bundles.koin.test.junit5) 21 | } 22 | 23 | tasks { 24 | 25 | withType().configureEach { 26 | compilerOptions { 27 | optIn.add("kotlin.RequiresOptIn") 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /subprojects/tdd-in-kotlin/src/test/kotlin/org/sdkotlin/tdd/mockk/MockKValueClassParameterTest.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.tdd.mockk 2 | 3 | import io.mockk.every 4 | import io.mockk.mockk 5 | import org.assertj.core.api.Assertions.assertThat 6 | import org.junit.jupiter.api.Disabled 7 | import org.junit.jupiter.api.Test 8 | 9 | interface ValueClassSuperType 10 | 11 | @JvmInline 12 | value class ValueClass(val s: String) : ValueClassSuperType 13 | 14 | data class TestDataClass(val v: ValueClassSuperType) 15 | 16 | class MockKValueClassParameterTest { 17 | 18 | @Test 19 | @Disabled("https://github.com/mockk/mockk/issues/1342") 20 | fun `test value class parameter`() { 21 | 22 | val testValue = ValueClass("testing") 23 | 24 | val mock = mockk { 25 | every { v } returns testValue 26 | } 27 | 28 | assertThat(mock.v).isEqualTo(testValue) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /config/detekt/detekt.yml: -------------------------------------------------------------------------------- 1 | build: 2 | maxIssues: 0 3 | excludeCorrectable: false 4 | 5 | config: 6 | validation: true 7 | warningsAsErrors: true 8 | checkExhaustiveness: false 9 | # when writing own rules with new properties, 10 | # exclude the property path e.g.: 'my_rule_set,.*>.*>[my_property]' 11 | excludes: '' 12 | 13 | processors: 14 | active: true 15 | exclude: 16 | - 'DetektProgressListener' 17 | 18 | console-reports: 19 | active: true 20 | exclude: 21 | - 'ProjectStatisticsReport' 22 | - 'ComplexityReport' 23 | - 'NotificationReport' 24 | #- 'FindingsReport' 25 | - 'FileBasedFindingsReport' 26 | 27 | output-reports: 28 | active: true 29 | exclude: 30 | - 'HtmlOutputReport' 31 | - 'TxtOutputReport' 32 | - 'XmlOutputReport' 33 | - 'SarifOutputReport' 34 | #- 'MdOutputReport' 35 | 36 | junit: 37 | active: true 38 | InvalidTestFactoryReturnType: 39 | active: true 40 | -------------------------------------------------------------------------------- /subprojects/tdd-in-kotlin/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.sdkotlin.buildlogic.global-exec") 3 | id("org.sdkotlin.buildlogic.kotlin-project") 4 | id("org.sdkotlin.buildlogic.test.test-fixtures-project") 5 | id("org.sdkotlin.buildlogic.test.unit-test-suite") 6 | id("org.sdkotlin.buildlogic.mockk-project") 7 | id("org.sdkotlin.buildlogic.mockito-kotlin-project") 8 | } 9 | 10 | dependencies { 11 | 12 | testImplementation(platform("org.sdkotlin.platforms:test-platform")) 13 | 14 | testImplementation(testFixtures(projects.subprojects.tddInKotlin)) 15 | 16 | testImplementation(libs.kotest.assertions.core.jvm) 17 | testImplementation(libs.kotest.assertions.table.jvm) 18 | testImplementation(libs.kotest.framework.engine) 19 | testImplementation(libs.kotest.runner.junit5) 20 | testImplementation(libs.mockk) 21 | 22 | testRuntimeOnly(platform("org.sdkotlin.platforms:app-platform")) 23 | 24 | testRuntimeOnly(libs.slf4j.simple) 25 | } 26 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_08_5_infixfunctions/InfixFunctionsInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._08_5_infixfunctions 2 | 3 | import kotlin.random.Random 4 | 5 | // Kotlin supports defining member and extension functions that can be called 6 | // using infix notation. 7 | 8 | infix fun Int.plusOrMinus(other: Int) = 9 | if (Random.nextBoolean()) { 10 | this + other 11 | } else { 12 | this - other 13 | } 14 | 15 | // Backticks can be used to escape nonstandard function names. This could be 16 | // combined with infix functions to simulate new operators, but backticks would 17 | // again be required at the call site. 18 | 19 | infix fun Int.`+-`(other: Int) = this.plusOrMinus(other) 20 | 21 | fun main() { 22 | 23 | val result = 1 plusOrMinus 2 24 | 25 | val alternateResult = 1 `+-` 2 26 | 27 | println("1 plusOrMinus 2: $result") 28 | println("1 `+-` 2: $alternateResult") 29 | } 30 | -------------------------------------------------------------------------------- /subprojects/equalsverifier-with-kotlin/src/main/kotlin/org/sdkotlin/equalsverifier/recursive/DataClassWithSealedTypeProperty.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.equalsverifier.recursive 2 | 3 | data class DataClassWithSealedTypeProperty( 4 | val foo: Foo, 5 | ) 6 | 7 | sealed interface Foo { 8 | 9 | val value: Int 10 | 11 | companion object { 12 | fun get(value: Int): Foo = 13 | FooEnum.INSTANCES[value] ?: UnknownFooImpl(value) 14 | } 15 | } 16 | 17 | enum class FooEnum( 18 | private val delegate: Foo, 19 | ) : Foo by delegate { 20 | 21 | FOO_1(1), 22 | FOO_2(2), 23 | ; 24 | 25 | constructor( 26 | value: Int, 27 | ) : this(FooImpl(value)) 28 | 29 | companion object { 30 | val INSTANCES: Map = 31 | entries.associateBy { enum -> enum.value } 32 | } 33 | } 34 | 35 | internal data class FooImpl( 36 | override val value: Int, 37 | ) : Foo 38 | 39 | internal data class UnknownFooImpl( 40 | override val value: Int, 41 | ) : Foo 42 | -------------------------------------------------------------------------------- /subprojects/effective-kotlin/src/main/kotlin/org/sdkotlin/meetup/idioms/Destructuring.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.meetup.idioms 2 | 3 | // Use data classes for "multiple return values". 4 | 5 | data class Point3D( 6 | val x: Double = 0.0, 7 | val y: Double = 0.0, 8 | val z: Double = 0.0, 9 | ) 10 | 11 | fun getOrigin() = Point3D() 12 | 13 | fun printOrigin() { 14 | val (x, y, z) = getOrigin() 15 | println("x: $x, y: $y, z: $z") 16 | } 17 | 18 | // Use destructuring with Maps and Arrays. 19 | 20 | val arrayOfMonths = arrayOf("Jan.", "Feb.", "etc.") 21 | val mapOfWeekdays = mapOf(1 to "Sunday", 2 to "Monday", 3 to "etc.") 22 | 23 | fun printMonthsAndWeekdays() { 24 | 25 | for ((index, value) in arrayOfMonths.withIndex()) { 26 | println("Month #${index + 1} is $value") 27 | } 28 | 29 | for ((key, value) in mapOfWeekdays) { 30 | println("Weekday #$key is $value") 31 | } 32 | } 33 | 34 | fun main() { 35 | printOrigin() 36 | printMonthsAndWeekdays() 37 | } 38 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/test/kotlin/org/sdkotlin/intro/kotlin/_14_0_dataclasses/PolyglotPersonTest.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._14_0_dataclasses 2 | 3 | import nl.jqno.equalsverifier.EqualsVerifier 4 | import org.assertj.core.api.Assertions.assertThat 5 | import org.junit.jupiter.api.Test 6 | 7 | internal class PolyglotPersonTest { 8 | 9 | @Test 10 | fun `test equals, hashCode, and toString`() { 11 | EqualsVerifier.forClass(PolyglotPerson::class.java).verify() 12 | } 13 | 14 | @Test 15 | fun `test data class deep equals for immutable lists`() { 16 | val kotlinJavaPerson = PolyglotPerson( 17 | "Fran", listOf( 18 | ProgrammingLanguage("Kotlin"), ProgrammingLanguage("Java") 19 | ) 20 | ) 21 | 22 | val javaKotlinPerson = PolyglotPerson( 23 | "Fran", listOf( 24 | ProgrammingLanguage("Java"), ProgrammingLanguage("Kotlin") 25 | ) 26 | ) 27 | 28 | assertThat(kotlinJavaPerson).isNotEqualTo(javaKotlinPerson) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /subprojects/sorting-in-kotlin/src/main/kotlin/org/sdkoltin/sort/Swap.kt: -------------------------------------------------------------------------------- 1 | package org.sdkoltin.sort 2 | 3 | /** 4 | * In-place swaps the elements at the given indices. 5 | * 6 | * @param i the first index. 7 | * @param j the second index. 8 | * @throws IllegalStateException if the list size is less than 2. 9 | * @throws IllegalArgumentException if a given index is out of bounds, or if 10 | * the given indices are equal. 11 | */ 12 | fun MutableList.swap(i: Int, j: Int) { 13 | 14 | check(size >= 2) { 15 | "Can't swap elements for a list with size $size!" 16 | } 17 | 18 | require(i in 0 until size) { 19 | "Index i = $i is out of bounds for list with size $size!" 20 | } 21 | 22 | require(j in 0 until size) { 23 | "Index j = $j is out of bounds for list with size $size!" 24 | } 25 | 26 | require(i != j) { 27 | "Can't swap elements for the same i and j index $i" 28 | } 29 | 30 | val temp = this[j] 31 | this[j] = this[i] 32 | this[i] = temp 33 | } 34 | -------------------------------------------------------------------------------- /subprojects/di-with-koin/src/main/kotlin/org/sdkotlin/koin/hello/helloModule.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.koin.hello 2 | 3 | import org.koin.core.qualifier.named 4 | import org.koin.dsl.module 5 | 6 | const val DECLARED_COMPONENT = "declared component" 7 | const val DECLARED_COMPONENT_CONTAINER = "declared component container" 8 | 9 | internal val helloModule = module { 10 | 11 | // An unqualified GreetingService 12 | single { 13 | RandomGreetingService() 14 | } 15 | 16 | // A named GreetingService 17 | single(named()) { 18 | EnglishGreetingService() 19 | } 20 | 21 | single { 22 | SimpleHelloController(get()) 23 | } 24 | 25 | // Injection will fail if DECLARED_COMPONENT isn't externally declared 26 | // prior, e.g. with getKoin().declare(...) 27 | single(named(DECLARED_COMPONENT_CONTAINER)) { 28 | ExternalComponentContainer( 29 | get(named(DECLARED_COMPONENT)) 30 | ) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /subprojects/tdd-in-kotlin/src/test/kotlin/org/sdkotlin/tdd/sorting/KotestSortTest.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.tdd.sorting 2 | 3 | import io.kotest.core.spec.style.StringSpec 4 | import io.kotest.data.forAll 5 | import io.kotest.data.headers 6 | import io.kotest.data.row 7 | import io.kotest.data.table 8 | import io.kotest.matchers.shouldBe 9 | 10 | internal class KotestSortTest : StringSpec({ 11 | 12 | "should return elements in order for" { 13 | val unsortedArrays = table( 14 | headers("unsortedArray", "expectedArray"), 15 | row(emptyArray(), emptyArray()), 16 | row(arrayOf(1), arrayOf(1)), 17 | row(arrayOf(1, 2), arrayOf(1, 2)), 18 | row(arrayOf(2, 1), arrayOf(1, 2)), 19 | row(arrayOf(1, 2, 3), arrayOf(1, 2, 3)), 20 | row(arrayOf(2, 1, 3), arrayOf(1, 2, 3)), 21 | row(arrayOf(1, 3, 2), arrayOf(1, 2, 3)), 22 | row(arrayOf(3, 2, 1), arrayOf(1, 2, 3)) 23 | ) 24 | forAll(unsortedArrays) { unsortedArray, sortedArray -> 25 | sort(unsortedArray) shouldBe sortedArray 26 | } 27 | } 28 | }) 29 | -------------------------------------------------------------------------------- /subprojects/tdd-in-kotlin/src/test/kotlin/org/sdkotlin/tdd/sorting/JUnit5LoopingSortTest.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.tdd.sorting 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.junit.jupiter.api.Test 5 | 6 | internal class JUnit5LoopingSortTest { 7 | 8 | @Test 9 | fun `test sort`() { 10 | 11 | // Assemble 12 | val testCases = listOf( 13 | emptyArray() to emptyArray(), 14 | arrayOf(1) to arrayOf(1), 15 | arrayOf(1, 2) to arrayOf(1, 2), 16 | arrayOf(2, 1) to arrayOf(1, 2), 17 | arrayOf(1, 2, 3) to arrayOf(1, 2, 3), 18 | arrayOf(2, 1, 3) to arrayOf(1, 2, 3), 19 | arrayOf(1, 3, 2) to arrayOf(1, 2, 3), 20 | arrayOf(3, 2, 1) to arrayOf(1, 2, 3), 21 | ) 22 | 23 | // Avoid: Fails fast (consider `@TestFactory` instead). 24 | for ((unsortedArray, sortedArray) in testCases) { 25 | 26 | // Act 27 | val sortResult = sort(unsortedArray) 28 | 29 | // Assert 30 | assertThat(sortResult) 31 | .isEqualTo(sortedArray) 32 | .isNotSameAs(unsortedArray) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/java/org/sdkotlin/intro/java/_XX_try_w_resources/TryWithResourcesInJava.java: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.java._XX_try_w_resources; 2 | 3 | import java.io.IOException; 4 | import java.net.URISyntaxException; 5 | import java.nio.file.Files; 6 | import java.nio.file.Paths; 7 | 8 | import static java.util.stream.Collectors.joining; 9 | 10 | public class TryWithResourcesInJava { 11 | 12 | public static void main(final String[] args) throws URISyntaxException { 13 | 14 | final var classLoader = Thread.currentThread().getContextClassLoader(); 15 | final var resource = classLoader.getResource("hello.txt"); 16 | if (resource == null) { 17 | throw new IllegalArgumentException("File not found!"); 18 | } 19 | final var path = Paths.get(resource.toURI()); 20 | 21 | try (final var lines = Files.lines(path)) { 22 | 23 | final var data = lines.collect(joining("\n")); 24 | 25 | System.out.println(data); 26 | } catch (final IOException e) { 27 | e.printStackTrace(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /subprojects/effective-kotlin/src/main/kotlin/org/sdkotlin/meetup/effectivejava/item17/MinimizeMutability.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.meetup.effectivejava.item17 2 | 3 | /* Effective Java 4 | Item 17: Minimize mutability 5 | */ 6 | 7 | // In Kotlin, immutability is just a `val` away. 8 | 9 | const val FUGGETABOUTIT = "Not gonna do it" 10 | 11 | data class ThreadSafe(val readOnly: String = "Can't touch this") { 12 | 13 | fun immutable(): String { 14 | val value = "Don't even" 15 | // value += "Nope" // Doesn't compile 16 | return value 17 | } 18 | } 19 | 20 | // The base collection APIs are immutable by default in Kotlin. 21 | 22 | val immutableList: List = listOf("Not", "gonna", "do", "it", "!") 23 | 24 | fun main() { 25 | 26 | //FUGGETABOUTIT = "As if" // Doesn't compile 27 | println(FUGGETABOUTIT) 28 | 29 | val threadSafe = ThreadSafe() 30 | // threadSafe.readOnly = "Sorry" // Doesn't compile 31 | println(threadSafe.readOnly) 32 | println(threadSafe.immutable()) 33 | 34 | //immutableList[0] = "Are" // Does not compile. 35 | 36 | println(immutableList) 37 | } 38 | -------------------------------------------------------------------------------- /subprojects/di-with-koin/src/it/kotlin/org/sdkotlin/koin/it/hello/HelloControllerIT.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.koin.it.hello 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.junit.jupiter.api.Test 5 | import org.junit.jupiter.api.extension.RegisterExtension 6 | import org.koin.test.KoinTest 7 | import org.koin.test.inject 8 | import org.koin.test.junit5.KoinTestExtension 9 | import org.sdkotlin.koin.hello.HelloController 10 | import org.sdkotlin.koin.hello.helloModule 11 | import org.sdkotlin.koin.it.hello.test.TestGreetingService 12 | import org.sdkotlin.koin.it.hello.test.testHelloModule 13 | 14 | internal class HelloControllerIT : KoinTest { 15 | 16 | private val helloController: HelloController by inject() 17 | 18 | @JvmField 19 | @RegisterExtension 20 | val koinTestExtension = KoinTestExtension.create { 21 | modules(listOf(helloModule, testHelloModule)) 22 | } 23 | 24 | @Test 25 | fun `Integration test the hello controller`() { 26 | 27 | val response = helloController.sayHello() 28 | 29 | assertThat(response).isEqualTo(TestGreetingService.TEST_GREETING) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/java/org/sdkotlin/intro/java/_11_iteration/IterationInJava.java: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.java._11_iteration; 2 | 3 | public class IterationInJava { 4 | 5 | public static void main(final String[] args) { 6 | 7 | for (int i = 1; i <= 5; i++) { 8 | System.out.println("Counting... " + i); 9 | } 10 | 11 | for (int i = 5; i >= 1; i--) { 12 | System.out.println("Down... " + i); 13 | } 14 | 15 | for (int i = 1; i <= 5; i += 2) { 16 | System.out.println("Odds... " + i); 17 | } 18 | 19 | final char[] alphabet = "abcdefghijklmnopqrstuvwxyz".toCharArray(); 20 | 21 | for (final char l : alphabet) { 22 | System.out.println("Letters... " + l); 23 | } 24 | 25 | for (int i = 0; i < alphabet.length; i++) { 26 | System.out.println("Letter " + (i + 1) + " of the alphabet is " + alphabet[i]); 27 | } 28 | 29 | var i = 1; 30 | 31 | while (i <= 5) { 32 | System.out.println("Counting between 1 and 5: " + i++); 33 | } 34 | 35 | var j = 1; 36 | 37 | do { 38 | System.out.println("Counting between 1 and 5: " + j++); 39 | } while (j <= 5); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /subprojects/tdd-in-kotlin/src/test/kotlin/org/sdkotlin/tdd/sorting/JUnit5DynamicTestSortTest.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.tdd.sorting 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.junit.jupiter.api.DynamicTest.dynamicTest 5 | import org.junit.jupiter.api.TestFactory 6 | 7 | internal class JUnit5DynamicTestSortTest { 8 | 9 | @TestFactory 10 | fun `test sort`() = 11 | // Assemble 12 | listOf( 13 | emptyArray() to emptyArray(), 14 | arrayOf(1) to arrayOf(1), 15 | arrayOf(1, 2) to arrayOf(1, 2), 16 | arrayOf(2, 1) to arrayOf(1, 2), 17 | arrayOf(1, 2, 3) to arrayOf(1, 2, 3), 18 | arrayOf(2, 1, 3) to arrayOf(1, 2, 3), 19 | arrayOf(1, 3, 2) to arrayOf(1, 2, 3), 20 | arrayOf(3, 2, 1) to arrayOf(1, 2, 3), 21 | ).map { (unsortedArray, expectedArray) -> 22 | dynamicTest( 23 | "sort(${unsortedArray.contentToString()}) is ${expectedArray.contentToString()}" 24 | ) { 25 | // Act 26 | val sortedArray = sort(unsortedArray) 27 | 28 | // Assert 29 | assertThat(sortedArray) 30 | .isEqualTo(expectedArray) 31 | .isNotSameAs(unsortedArray) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /subprojects/user-defined-integrals-in-kotlin/src/test/kotlin/org/sdkotlin/integral/IntegralRangeTest.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.integral 2 | 3 | import io.mockk.mockk 4 | import nl.jqno.equalsverifier.EqualsVerifier 5 | import org.junit.jupiter.api.Test 6 | 7 | class IntegralRangeTest { 8 | 9 | @Test 10 | fun `test equals, hashCode, and toString`() { 11 | 12 | EqualsVerifier.forClass(IntegralRange::class.java) 13 | .withIgnoredFields("endExclusive", $$$"$$delegate_0") 14 | // Required per https://github.com/jqno/equalsverifier/issues/1082. 15 | .withPrefabValues( 16 | Integral::class.java, 17 | mockk(relaxed = true), 18 | mockk(relaxed = true), 19 | ) 20 | .verify() 21 | } 22 | 23 | @Test 24 | fun `test equals, hashCode, and toString with examples`() { 25 | 26 | val zero = SignedIntegral(0) 27 | val one = SignedIntegral(1) 28 | val two = SignedIntegral(2) 29 | val red: ClosedRange = zero..one 30 | val blue: ClosedRange = one..two 31 | 32 | EqualsVerifier.forExamples(red, blue) 33 | .withIgnoredFields("endExclusive", $$$"$$delegate_0") 34 | .verify() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /subprojects/equalsverifier-with-kotlin/src/test/kotlin/org/sdkotlin/equalsverifier/delegation/FooBarImplTest.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.equalsverifier.delegation 2 | 3 | import nl.jqno.equalsverifier.EqualsVerifier 4 | import nl.jqno.equalsverifier.Mode 5 | import org.assertj.core.api.Assertions.assertThat 6 | import org.junit.jupiter.api.Test 7 | 8 | class FooBarImplTest { 9 | 10 | @Test 11 | fun `test equals, hashCode, and toString`() { 12 | 13 | EqualsVerifier.forClass(FooBarImpl::class.java) 14 | .withIgnoredFields(Foo::foo.name) 15 | // Mockito skip or prefab values required per 16 | // https://github.com/jqno/equalsverifier/issues/1083. 17 | .set(Mode.skipMockito()) 18 | //.withPrefabValues(BarImpl::class.java, BarImpl(1), BarImpl(2)) 19 | .verify() 20 | } 21 | 22 | @Test 23 | fun `test equals for equal`() { 24 | val actual = FooBarImpl(1) 25 | val expected = FooBarImpl(1) 26 | assertThat(actual).isEqualTo(expected) 27 | } 28 | 29 | @Test 30 | fun `test equals for not equal`() { 31 | val actual = FooBarImpl(1) 32 | val expected = FooBarImpl(2) 33 | assertThat(actual).isNotEqualTo(expected) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /subprojects/effective-kotlin/src/main/kotlin/org/sdkotlin/meetup/effectivejava/item2/ConsiderBuilders.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.meetup.effectivejava.item2 2 | 3 | /* Effective Java 4 | Item 2: Consider a builder when faced with many constructor parameters 5 | */ 6 | 7 | // Kotlin supports named and default parameters, which make the telescoping 8 | // constructor and method patterns, and in many cases the builder pattern, 9 | // obsolete. 10 | 11 | data class NutritionFacts( 12 | val servingSize: Int, 13 | val servings: Int, 14 | val calories: Int = 0, 15 | val fat: Int = 0, 16 | val sodium: Int = 0, 17 | val carbohydrate: Int = 0, 18 | ) { 19 | init { 20 | require(servingSize > 0) { "servingSize must be greater than 0" } 21 | } 22 | } 23 | 24 | fun main() { 25 | 26 | val sodaNutritionFacts = 27 | NutritionFacts(servingSize = 12, servings = 2, calories = 180) 28 | 29 | val pizzaNutritionFacts = 30 | NutritionFacts( 31 | servingSize = 1, 32 | servings = 12, 33 | calories = 250, 34 | fat = 12, 35 | carbohydrate = 24, 36 | ) 37 | 38 | println("Soda facts: $sodaNutritionFacts") 39 | println("Pizza facts: $pizzaNutritionFacts") 40 | } 41 | -------------------------------------------------------------------------------- /subprojects/effective-kotlin/src/main/kotlin/org/sdkotlin/meetup/effectivejava/item10through13/CommonMethods.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.meetup.effectivejava.item10through13 2 | 3 | /* Effective Java 4 | Item 10: Obey the general contract when overriding equals 5 | Item 11: Always override hashCode when you override equals 6 | Item 12: Always override toString 7 | Item 13: Override clone judiciously 8 | */ 9 | 10 | // Data classes do all these things correctly for you out of the box. 11 | 12 | data class Person(val name: String, var age: Int) 13 | 14 | fun `with data classes`() { 15 | 16 | val sally = Person("Sally", 22) 17 | val sandy = Person("Sandy", 32) 18 | 19 | // equals 20 | 21 | println("Sally == Sandy?: ${sally == sandy}") 22 | 23 | // toString 24 | 25 | println("sally: $sally") 26 | 27 | // "clone" 28 | 29 | val suzzy = sally.copy(name = "Suzzy") 30 | 31 | // hashCode 32 | 33 | val people = 34 | mapOf(sally.name to sally, sandy.name to sandy, suzzy.name to suzzy) 35 | 36 | people.forEach { (name, person) -> 37 | println("$name's hash code is: ${person.hashCode()}") 38 | } 39 | } 40 | 41 | fun main() { 42 | `with data classes`() 43 | } 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://github.com/sdkotlin/sd-kotlin-talks/actions/workflows/gradle.yml/badge.svg)](https://github.com/sdkotlin/sd-kotlin-talks/actions/workflows/gradle.yml) 2 | [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=sdkotlin_sd-kotlin-talks&metric=alert_status)](https://sonarcloud.io/dashboard?id=sdkotlin_sd-kotlin-talks) 3 | 4 | # San Diego Kotlin User Group Talks 5 | 6 | Code examples from 7 | the [San Diego Kotlin User Group](https://www.meetup.com/sd-kotlin/) meetings on 8 | the [Kotlin](http://kotlinlang.org/) programming language. 9 | 10 | ## Building 11 | 12 | The project is built 13 | with [Gradle](https://docs.gradle.org/current/userguide/userguide.html) and uses 14 | the [Gradle Wrapper](https://docs.gradle.org/current/userguide/gradle_wrapper.html#gradle_wrapper_reference), 15 | which can be invoked from the project root once 16 | the [Gradle prerequisites](https://docs.gradle.org/current/userguide/installation.html#sec:prerequisites) 17 | are met. 18 | 19 | The project currently requires Gradle to be invoked with a Java Development 20 | Kit (JDK) version 21 or higher. 21 | 22 | The primary build task is `build`. 23 | -------------------------------------------------------------------------------- /subprojects/effective-kotlin/src/main/kotlin/org/sdkotlin/meetup/effectivejava/item16/AccessorsOverFields.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.meetup.effectivejava.item16 2 | 3 | /* Effective Java 4 | Item 16: In public classes, use accessor methods, not public fields 5 | */ 6 | 7 | // Kotlin doesn't have fields, only properties with their associated accessors 8 | // and private backing fields. 9 | 10 | data class Programmer(val name: String) { 11 | var languages: String = "Kotlin" 12 | set(value) { 13 | when (value) { 14 | "Kotlin", "Java" -> { 15 | println("""DEBUG: Appending "$value" to "$field"""") 16 | field += ", and $value" 17 | } 18 | else -> println("""DEBUG: Ignoring "$value"""") 19 | } 20 | } 21 | get() = "$field!" 22 | 23 | val favoriteIde = "IntelliJ" 24 | get() = "$field. 🙂" 25 | } 26 | 27 | fun main() { 28 | 29 | val programmer = Programmer("Jim") 30 | 31 | // Look, Ma, "field access" 32 | programmer.languages = "Java" 33 | programmer.languages = "Kotlin" 34 | programmer.languages = "Perl" 35 | 36 | println("${programmer.name} knows ${programmer.languages}") 37 | println("Their favorite IDE is ${programmer.favoriteIde}") 38 | } 39 | -------------------------------------------------------------------------------- /subprojects/ksp-builder-generator/processor/test-project/src/test/kotlin/org/sdkotlin/buildergen/processor/it/TestEntityBuilderTest.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.buildergen.processor.it 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.junit.jupiter.api.Test 5 | import org.sdkotlin.buildergen.api.builder.Builder 6 | 7 | internal class TestEntityBuilderTest { 8 | 9 | companion object { 10 | const val TEST_INT = 1 11 | const val TEST_STRING = "Testing" 12 | } 13 | 14 | @Test 15 | fun `test generated builder`() { 16 | 17 | val testEntityBuilder = TestEntityBuilder() 18 | 19 | testEntityBuilder.testInt = TEST_INT 20 | testEntityBuilder.testNullableInt = null 21 | testEntityBuilder.testString = TEST_STRING 22 | testEntityBuilder.testNullableString = null 23 | 24 | assertThat(testEntityBuilder).isInstanceOf(Builder::class.java) 25 | 26 | val testEntity: TestEntity = testEntityBuilder.build() 27 | 28 | assertThat(testEntity.testInt).isEqualTo(TEST_INT) 29 | assertThat(testEntity.testNullableInt).isNull() 30 | assertThat(testEntity.testString).isEqualTo(TEST_STRING) 31 | assertThat(testEntity.testNullableString).isNull() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_32_delegation/LateInit.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._32_delegation 2 | 3 | import kotlin.properties.ReadWriteProperty 4 | import kotlin.reflect.KProperty 5 | 6 | class LateInit( 7 | val createException: (s: String) -> Exception, 8 | ) : ReadWriteProperty { 9 | 10 | private object EMPTY 11 | 12 | private var value: Any? = EMPTY 13 | 14 | override fun getValue( 15 | thisRef: Any, 16 | property: KProperty<*>, 17 | ): T { 18 | if (value == EMPTY) { 19 | throw createException("${property.name} isn't initialized") 20 | } else { 21 | @Suppress("UNCHECKED_CAST") // Must be EMPTY or a T. 22 | return value as T 23 | } 24 | } 25 | 26 | override fun setValue( 27 | thisRef: Any, 28 | property: KProperty<*>, 29 | value: T, 30 | ) { 31 | if (this.value != EMPTY) { 32 | throw createException("${property.name} already initialized") 33 | } 34 | this.value = value 35 | } 36 | } 37 | 38 | inline fun lateInit( 39 | noinline createException: (s: String) -> Exception = 40 | { IllegalStateException(it) }, 41 | ): ReadWriteProperty = 42 | LateInit(createException) 43 | -------------------------------------------------------------------------------- /subprojects/tdd-in-kotlin/src/test/kotlin/org/sdkotlin/tdd/fizzbuzz/FizzBuzzMockKTest.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.tdd.fizzbuzz 2 | 3 | import io.mockk.mockk 4 | import io.mockk.verify 5 | import io.mockk.verifyOrder 6 | import org.junit.jupiter.api.Test 7 | 8 | internal class FizzBuzzMockKTest { 9 | 10 | @Test 11 | fun `test fizzBuzz1To100() prints 100 FizzBuzzes with MockK Printer`() { 12 | 13 | val mockPrinter = mockk() 14 | 15 | fizzBuzz1to100(mockPrinter) 16 | 17 | verify(exactly = 100) { 18 | mockPrinter.print(any()) 19 | } 20 | 21 | verifyOrder { 22 | mockPrinter.print("1") 23 | mockPrinter.print("2") 24 | mockPrinter.print("Fizz") 25 | mockPrinter.print("Buzz") 26 | mockPrinter.print("FizzBuzz") 27 | } 28 | } 29 | 30 | @Test 31 | fun `test fizzBuzz1To100() prints 100 FizzBuzzes with MockK function object`() { 32 | 33 | val mockPrintLn = mockk<(String) -> Unit>() 34 | 35 | fizzBuzz1to100(mockPrintLn) 36 | 37 | verify(exactly = 100) { 38 | mockPrintLn(any()) 39 | } 40 | 41 | verifyOrder { 42 | mockPrintLn("1") 43 | mockPrintLn("2") 44 | mockPrintLn("Fizz") 45 | mockPrintLn("Buzz") 46 | mockPrintLn("FizzBuzz") 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /subprojects/user-defined-integrals-in-kotlin/src/main/kotlin/org/sdkotlin/integral/IntegralRange.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.integral 2 | 3 | class IntegralRange>( 4 | start: I, 5 | endInclusive: I, 6 | ) : ClosedRange, OpenEndRange, 7 | Progression by IntegralProgression( 8 | start, 9 | endInclusive, 10 | step = start.one 11 | ) { 12 | 13 | override val start: I = first 14 | 15 | override val endInclusive: I = last 16 | 17 | override val endExclusive: I by lazy { 18 | check(endInclusive != endInclusive.maxValue) { 19 | "Cannot return the exclusive upper bound of a range that includes MAX_VALUE." 20 | } 21 | endInclusive + endInclusive.one 22 | } 23 | 24 | override fun contains(value: I): Boolean = 25 | value in start..endInclusive 26 | 27 | override fun isEmpty(): Boolean = start > endInclusive 28 | 29 | override fun equals(other: Any?): Boolean = 30 | other is IntegralRange<*> && (isEmpty() && other.isEmpty() || 31 | start == other.start && endInclusive == other.endInclusive) 32 | 33 | override fun hashCode(): Int = 34 | if (isEmpty()) -1 else (31 * start.hashCode() + endInclusive.hashCode()) 35 | 36 | override fun toString(): String = "$start..$endInclusive" 37 | } 38 | -------------------------------------------------------------------------------- /subprojects/tdd-in-kotlin/src/test/kotlin/org/sdkotlin/tdd/sorting/JUnit5ParameterizedSortTest.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.tdd.sorting 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.junit.jupiter.params.ParameterizedTest 5 | import org.junit.jupiter.params.provider.Arguments 6 | import org.junit.jupiter.params.provider.Arguments.arguments 7 | import org.junit.jupiter.params.provider.MethodSource 8 | 9 | internal class JUnit5ParameterizedSortTest { 10 | 11 | companion object { 12 | @JvmStatic 13 | fun arrays() = listOf( 14 | Arguments.of(emptyArray(), emptyArray()), 15 | arguments(arrayOf(1), arrayOf(1)), 16 | arguments(arrayOf(1, 2), arrayOf(1, 2)), 17 | arguments(arrayOf(2, 1), arrayOf(1, 2)), 18 | arguments(arrayOf(1, 2, 3), arrayOf(1, 2, 3)), 19 | arguments(arrayOf(2, 1, 3), arrayOf(1, 2, 3)), 20 | arguments(arrayOf(1, 3, 2), arrayOf(1, 2, 3)), 21 | arguments(arrayOf(3, 2, 1), arrayOf(1, 2, 3)) 22 | ) 23 | } 24 | 25 | @ParameterizedTest(name = "sort({0}) is {1}") 26 | @MethodSource("arrays") 27 | fun `test sort`(unsortedArray: Array, sortedArray: Array) { 28 | assertThat(sort(unsortedArray)).isEqualTo(sortedArray) 29 | .isNotSameAs(unsortedArray) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /subprojects/effective-kotlin/src/main/kotlin/org/sdkotlin/meetup/idioms/Nulls.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.meetup.idioms 2 | 3 | data class Person(val name: String, val address: Address? = null) 4 | 5 | data class Address( 6 | val street1: String, val street2: String? = null, 7 | val city: String, 8 | val state: String, val zipCode: String, 9 | ) 10 | 11 | /** 12 | * Handle nulls in chained property accessor and method calls 13 | */ 14 | fun whichState(person: Person?): String { 15 | return person?.address?.state ?: "Unknown" 16 | } 17 | 18 | /** 19 | * Handle nulls with a little less conversation, a little more action, using 20 | * Kotlin's Elvis operator 21 | * 22 | * @throws IllegalArgumentException if an address is not available 23 | */ 24 | fun sendCard(person: Person) { 25 | 26 | val address = person.address ?: throw IllegalArgumentException( 27 | "Can't send a card to a person if they don't have an address" 28 | ) 29 | 30 | println("Sending a card to ${person.name} at $address") 31 | } 32 | 33 | fun main() { 34 | 35 | val sally = Person(name = "Sally") 36 | 37 | println("Sally lives in ${whichState(sally)}") 38 | 39 | try { 40 | sendCard(sally) 41 | } catch (e: IllegalArgumentException) { 42 | println(e.message) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/java/org/sdkotlin/intro/java/_06_equality/EqualityInJava.java: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.java._06_equality; 2 | 3 | import java.util.Objects; 4 | 5 | public class EqualityInJava { 6 | 7 | public static void main(final String[] args) { 8 | 9 | // False. 10 | System.out.println("\"foo\"==new String(\"foo\"): " 11 | + ("foo" == new String("foo"))); 12 | 13 | // True only because of the string pool. 14 | System.out.println("\"foo\"==\"foo\": " 15 | + ("foo" == "foo")); 16 | 17 | // True because of the integer pool. 18 | System.out.println("127==127: " 19 | + (Integer.valueOf(Byte.MAX_VALUE) == Integer.valueOf(Byte.MAX_VALUE))); 20 | 21 | // Likely false, but may be true depending on VM and its settings for the integer pool. 22 | System.out.println("128==128: " 23 | + (Integer.valueOf(Byte.MAX_VALUE + 1) == Integer.valueOf(Byte.MAX_VALUE + 1))); 24 | 25 | // Correct logical comparison. Null safe only when the left side is known to not be null. 26 | System.out.println("\"foo\".equals(new String(\"foo\")): " 27 | + "foo".equals(new String("foo"))); 28 | 29 | // Null safe. 30 | System.out.println("Objects.equals(null, null): " 31 | + Objects.equals(null, null)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /subprojects/effective-kotlin/src/main/kotlin/org/sdkotlin/meetup/effectivejava/item50/MakeDefensiveCopies.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.meetup.effectivejava.item50 2 | 3 | /* Effective Java 4 | Item 50: Make defensive copies when needed 5 | */ 6 | 7 | // Kotlin data classes come with `copy()`. 8 | 9 | data class Person(val name: String, var age: Int) 10 | 11 | object OlympicTriplets { 12 | val leila = Person("Leila", 33) 13 | val liina = leila.copy(name = "Liina") 14 | val lily = liina.copy(name = "Lily") 15 | 16 | override fun toString(): String { 17 | return "{ $leila, $liina, $lily }" 18 | } 19 | } 20 | 21 | fun good(triplets: OlympicTriplets) { 22 | 23 | // Copy Leila so as not to change her age on the passed parameter 24 | val leilaClone = triplets.leila.copy() 25 | 26 | leilaClone.age++ 27 | 28 | println("Leila's clone is ${leilaClone.age} years old.") 29 | } 30 | 31 | fun bad(triplets: OlympicTriplets) { 32 | 33 | println("Unless we don't make a defensive copy, in which case...") 34 | triplets.leila.age++ 35 | } 36 | 37 | fun main() { 38 | 39 | good(OlympicTriplets) 40 | 41 | println("The original Leila is still ${OlympicTriplets.leila.age}.") 42 | 43 | bad(OlympicTriplets) 44 | 45 | println("The original Leila is now ${OlympicTriplets.leila.age}.") 46 | } 47 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_15_singletons/SingletonInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._15_singletons 2 | 3 | // Kotlin has built-in support for the singleton pattern. 4 | 5 | object KotlinSingleton { 6 | 7 | const val notQuitePi = 3.14 8 | 9 | fun doIt() { 10 | println("The most wonderful thing about tiggers is I'm the only one!") 11 | } 12 | } 13 | 14 | // Kotlin 1.8 will introduce data objects, which override toString(). 15 | 16 | data object TooStringy { 17 | 18 | const val cheesy = "String Cheese" 19 | 20 | // Data objects override toString without including any properties, like: 21 | //override fun toString() = "${TooStringy::class.simpleName}" 22 | 23 | // You can still provide your own override to include them: 24 | //override fun toString() = "${TooStringy::class.simpleName}[cheesy=$cheesy]" 25 | } 26 | 27 | fun main() { 28 | 29 | // Singleton instances are available by their type name. 30 | 31 | val singleton = KotlinSingleton 32 | 33 | singleton.doIt() 34 | 35 | // No need to assign them to a var. 36 | 37 | println(KotlinSingleton.notQuitePi) 38 | 39 | // Singletons do not have constructors. 40 | 41 | //KotlinSingleton().doIt() 42 | 43 | // Regular objects don't override toString(). 44 | 45 | println(KotlinSingleton) 46 | 47 | // Data objects do. 48 | 49 | println(TooStringy) 50 | } 51 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_06_strings/StringsInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._06_strings 2 | 3 | import org.sdkotlin.intro.java._06_strings.StringsInJava.STRING 4 | 5 | fun main() { 6 | 7 | // Kotlin supports string templates. 8 | 9 | val yay = "yay" 10 | 11 | val template = "Kotlin supports String templates, $yay!\n\n" 12 | 13 | println(template) 14 | 15 | val otherTemplate = "You can use arbitrary expressions if you" + 16 | "add curly braces: ${1 + 1}" 17 | 18 | println(otherTemplate) 19 | 20 | val rawString = """ 21 | You can use "raw" strings for more substantial bodies of text. 22 | val someString = "That gets you out of " 23 | + "concatenation-wrapping, and lets you use literal whitespace " 24 | + "instead of escape sequences such as \t, \n, etc." 25 | You can still use string templates in raw strings: $STRING 26 | You can still put arbitrary expressions in those string templates with 27 | curly braces. ${"\n".repeat(2)} 28 | 29 | You can trim leading indentation. 30 | """.trimIndent() 31 | 32 | val prefixedRawString = """ 33 | > You can also trim to a prefix string, 34 | > as with email-style quotations. 35 | > The default prefix is "|" for `.trimMargin()`. 36 | > > Trimming is not eager. 37 | """.trimMargin("> ") 38 | 39 | println(template + rawString + prefixedRawString) 40 | } 41 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_08_3_extensionfunctions/ExtensionFunctionsInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._08_3_extensionfunctions 2 | 3 | // Kotlin allows us to add functions to an existing class by prefixing the 4 | // function name with the type it's extending. 5 | 6 | // You can refer to the instance the extension is called on (the "receiver") 7 | // with the usual 'this' keyword. 8 | 9 | fun String.shuffled() = this.toList().shuffled().joinToString(separator = "") 10 | 11 | // This is handy for adding things to the stdlib or other libraries that you 12 | // feel are missing. 13 | 14 | fun String.bedazzled() = "***$this***" 15 | 16 | fun main() { 17 | 18 | val shuffled = "Alphabet".shuffled() 19 | 20 | println("Alphabet.shuffled(): $shuffled") 21 | 22 | val bedazzled = "Snazzy".bedazzled() 23 | 24 | println("Snazzy.bedazzled(): $bedazzled") 25 | 26 | // Extension functions are resolved statically by the receiver's 27 | // declared or inferred type. In other words, they are not polymorphic. 28 | 29 | abstract class Animal 30 | 31 | class Lion : Animal() 32 | 33 | fun Animal.name() = "Animal" 34 | fun Lion.name() = "Simba" 35 | 36 | val genericLion: Animal = Lion() 37 | val specificLion = Lion() 38 | 39 | println("Lion as Animal's name: ${genericLion.name()}") 40 | println("Lion as Lion's name: ${specificLion.name()}") 41 | } 42 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_18_inheritance/InheritanceInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._18_inheritance 2 | 3 | class Class 4 | 5 | // "Effective Java, 3rd Edition. Item 19: Design and 6 | // Document for Inheritance or Else Prohibit It" 7 | 8 | // Classes are final by default in Kotlin (see bytecode) 9 | //class Subclass : Class() 10 | 11 | // You can "open" them like so... 12 | open class BaseClass(var name: String = "Cliff") { 13 | 14 | constructor(nameAsInt: Int) : this(name = nameAsInt.toString()) 15 | 16 | // Functions are final by default in Kotlin as well 17 | fun finalFunction() = println("Can't touch this!") 18 | 19 | // But they can be opened too 20 | open fun openFunction() = println("Base class implementation") 21 | } 22 | 23 | // Subclasses must call a constructor of their superclass. 24 | class Subclass : BaseClass(nameAsInt = 1) { 25 | 26 | // You can't override a final function 27 | //override fun finalFunction() = println("nope!") 28 | 29 | // You can explicitly override open functions 30 | override fun openFunction() = println("Subclass implementation") 31 | } 32 | 33 | fun main() { 34 | 35 | println(Class()) 36 | 37 | val baseClass = BaseClass() 38 | baseClass.finalFunction() 39 | baseClass.openFunction() 40 | 41 | val subclass = Subclass() 42 | subclass.finalFunction() 43 | subclass.openFunction() 44 | } 45 | -------------------------------------------------------------------------------- /subprojects/tdd-in-kotlin/src/test/kotlin/org/sdkotlin/tdd/fizzbuzz/FizzBuzzMockitoKotlinTest.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.tdd.fizzbuzz 2 | 3 | import com.nhaarman.mockitokotlin2.any 4 | import com.nhaarman.mockitokotlin2.inOrder 5 | import com.nhaarman.mockitokotlin2.mock 6 | import com.nhaarman.mockitokotlin2.times 7 | import com.nhaarman.mockitokotlin2.verify 8 | import org.junit.jupiter.api.Test 9 | 10 | internal class FizzBuzzMockitoKotlinTest { 11 | 12 | @Test 13 | fun `test fizzBuzz1To100() prints 100 FizzBuzzes with Mockito mock Printer`() { 14 | 15 | val mockPrinter = mock() 16 | 17 | fizzBuzz1to100(mockPrinter) 18 | 19 | verify(mockPrinter, times(100)).print(any()) 20 | 21 | inOrder(mockPrinter) { 22 | mockPrinter.print("1") 23 | mockPrinter.print("2") 24 | mockPrinter.print("Fizz") 25 | mockPrinter.print("Buzz") 26 | mockPrinter.print("FizzBuzz") 27 | } 28 | } 29 | 30 | @Test 31 | fun `test fizzBuzz1To100() prints 100 FizzBuzzes with Mockito mock function object`() { 32 | 33 | val mockPrinter = mock<(String) -> Unit>() 34 | 35 | fizzBuzz1to100(mockPrinter) 36 | 37 | verify(mockPrinter, times(100)).invoke(any()) 38 | 39 | // Or... 40 | verify(mockPrinter, times(100))(any()) 41 | 42 | inOrder(mockPrinter) { 43 | mockPrinter("1") 44 | mockPrinter("2") 45 | mockPrinter("Fizz") 46 | mockPrinter("Buzz") 47 | mockPrinter("FizzBuzz") 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Gradle and cache/restore any 2 | # dependencies to improve the workflow execution time. For more information see: 3 | # https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle 4 | 5 | name: Gradle Build 6 | 7 | on: 8 | push: 9 | branches: [ "main" ] 10 | pull_request: 11 | branches: [ "main" ] 12 | 13 | jobs: 14 | build: 15 | 16 | runs-on: ubuntu-latest 17 | permissions: 18 | contents: read 19 | 20 | steps: 21 | - uses: actions/checkout@v4 22 | with: 23 | lfs: 'true' 24 | - name: Set up JDK 21 25 | uses: actions/setup-java@v4 26 | with: 27 | java-version: '21' 28 | distribution: 'temurin' 29 | 30 | # Configure Gradle for optimal use in GiHub Actions, including caching of downloaded dependencies. 31 | # See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md 32 | - name: Setup Gradle 33 | uses: gradle/actions/setup-gradle@v3 34 | with: 35 | build-scan-publish: true 36 | build-scan-terms-of-use-url: "https://gradle.com/terms-of-service" 37 | build-scan-terms-of-use-agree: "yes" 38 | 39 | # Useful for observing eager configuration 40 | - name: Run Gradle Help 41 | run: ./gradlew help 42 | 43 | - name: Run Gradle Build 44 | run: ./gradlew build 45 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_19_abstractclasses/AbstractClassesInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._19_abstractclasses 2 | 3 | // Kotlin supports abstract classes and members. 4 | 5 | // Abstract classes are implicitly open. 6 | 7 | abstract class DinnerTemplate { 8 | 9 | protected open fun cookFood() { 10 | println("It's pasta for dinner") 11 | } 12 | 13 | // Abstract members are implicitly open as well. 14 | 15 | protected abstract fun serve() 16 | 17 | protected abstract fun enjoy() 18 | 19 | fun haveDinner() { 20 | cookFood() 21 | serve() 22 | enjoy() 23 | } 24 | } 25 | 26 | class NiceMeal : DinnerTemplate() { 27 | 28 | override fun serve() { 29 | println("Paper plates and plasticware") 30 | } 31 | 32 | override fun enjoy() { 33 | println("Slrrrp") 34 | } 35 | } 36 | 37 | abstract class NotPastaAgainTemplate : DinnerTemplate() { 38 | 39 | // An open member function can be overridden as abstract. 40 | abstract override fun cookFood() 41 | } 42 | 43 | class FancyMeal : NotPastaAgainTemplate() { 44 | 45 | override fun cookFood() { 46 | println("Chef's special") 47 | } 48 | 49 | override fun serve() { 50 | println("Fine china and silverware") 51 | } 52 | 53 | override fun enjoy() { 54 | println("Bon appetit") 55 | } 56 | } 57 | 58 | fun main() { 59 | 60 | val niceMeal = NiceMeal() 61 | val fancyMeal = FancyMeal() 62 | 63 | niceMeal.haveDinner() 64 | fancyMeal.haveDinner() 65 | } 66 | -------------------------------------------------------------------------------- /subprojects/ksp-builder-generator/processor/src/main/kotlin/org/sdkotlin/buildergen/processor/BuilderGenSymbolProcessor.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.buildergen.processor 2 | 3 | import com.google.devtools.ksp.processing.CodeGenerator 4 | import com.google.devtools.ksp.processing.KSPLogger 5 | import com.google.devtools.ksp.processing.Resolver 6 | import com.google.devtools.ksp.processing.SymbolProcessor 7 | import com.google.devtools.ksp.symbol.KSAnnotated 8 | import com.google.devtools.ksp.symbol.KSClassDeclaration 9 | import com.google.devtools.ksp.validate 10 | import org.sdkotlin.buildergen.api.annotations.GeneratedBuilder 11 | 12 | internal class BuilderGenSymbolProcessor( 13 | private val codeGenerator: CodeGenerator, 14 | private val logger: KSPLogger, 15 | ) : SymbolProcessor { 16 | 17 | override fun process(resolver: Resolver): List { 18 | 19 | val annotationClass = GeneratedBuilder::class 20 | 21 | val symbols = resolver.getSymbolsWithAnnotation( 22 | annotationClass.qualifiedName 23 | ?: throw IllegalArgumentException( 24 | "Could not get $annotationClass qualified name" 25 | ) 26 | ) 27 | 28 | val annotatedSymbols: List = 29 | symbols.filter { !it.validate() }.toList() 30 | 31 | symbols 32 | .filter { it is KSClassDeclaration && it.validate() } 33 | .forEach { 34 | it.accept( 35 | visitor = BuilderGenVisitor(codeGenerator, logger), 36 | data = Unit 37 | ) 38 | } 39 | 40 | return annotatedSymbols 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /subprojects/kotlin-dl/src/main/kotlin/org/sdkotlin/kotlindl/FashionMnist.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.kotlindl 2 | 3 | import org.jetbrains.kotlinx.dl.api.core.Sequential 4 | import org.jetbrains.kotlinx.dl.api.core.layer.core.Dense 5 | import org.jetbrains.kotlinx.dl.api.core.layer.core.Input 6 | import org.jetbrains.kotlinx.dl.api.core.layer.reshaping.Flatten 7 | import org.jetbrains.kotlinx.dl.api.core.loss.Losses 8 | import org.jetbrains.kotlinx.dl.api.core.metric.Metrics 9 | import org.jetbrains.kotlinx.dl.api.core.optimizer.Adam 10 | import org.jetbrains.kotlinx.dl.dataset.embedded.fashionMnist 11 | 12 | private const val EPOCHS = 5 13 | private const val TRAINING_BATCH_SIZE = 100 14 | private const val TEST_BATCH_SIZE = 1000 15 | private const val NUM_LABELS = 10 16 | private const val NUM_CHANNELS = 1L 17 | private const val IMAGE_SIZE = 28L 18 | 19 | internal val model = Sequential.of( 20 | Input(IMAGE_SIZE, IMAGE_SIZE, NUM_CHANNELS), 21 | Flatten(), 22 | Dense(300), 23 | Dense(100), 24 | Dense(NUM_LABELS) 25 | ) 26 | 27 | fun main() { 28 | 29 | val (train, test) = fashionMnist() 30 | 31 | model.use { 32 | 33 | it.compile( 34 | optimizer = Adam(), 35 | loss = Losses.SOFT_MAX_CROSS_ENTROPY_WITH_LOGITS, 36 | metric = Metrics.ACCURACY 37 | ) 38 | 39 | it.fit( 40 | dataset = train, 41 | epochs = EPOCHS, 42 | batchSize = TRAINING_BATCH_SIZE 43 | ) 44 | 45 | val accuracy = it.evaluate( 46 | dataset = test, 47 | batchSize = TEST_BATCH_SIZE 48 | ).metrics[Metrics.ACCURACY] 49 | 50 | println("Accuracy: $accuracy") 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_12_properties/PropertiesInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._12_properties 2 | 3 | // Top level variables and class fields in Kotlin are really all properties. 4 | 5 | val readOnly = 0 6 | // They have default getters (and for 'var', setters) that can be overridden. 7 | get() { 8 | 9 | // If you later decide you need to add some behavior to the getter, 10 | // you can. 11 | 12 | println("Audit readOnly get.") 13 | 14 | // The underlying field is available in getters and setters using the 15 | // keyword 'field'. 16 | 17 | return field 18 | } 19 | 20 | var readWrite = 1 21 | get() { 22 | println("Audit readWrite get.") 23 | return field 24 | } 25 | set(value) { 26 | println("Audit readWrite set: $value.") 27 | field = value 28 | } 29 | 30 | // You can use access modifiers to restrict the getter or setter, and omit the 31 | // either to use the default. 32 | 33 | var readPrivateWrite = 1 34 | private set(value) { 35 | println("Audit readWrite set: $value.") 36 | field = value 37 | } 38 | 39 | // Getters and setters can be computed. 40 | 41 | var width = 1 42 | var height = 1 43 | 44 | val area: Int 45 | get() = width * height 46 | 47 | fun main() { 48 | println("readOnly is $readOnly") 49 | 50 | println("readWrite is $readWrite") 51 | readWrite++ 52 | println("After readWrite++ it's $readWrite") 53 | 54 | println("Only this file can readPrivateWrite++: ${readPrivateWrite++}") 55 | 56 | println("Area: $area") 57 | width++ 58 | height++ 59 | println("New area: $area") 60 | } 61 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_08_4_operatorfunctions/OperatorOverloadingInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._08_4_operatorfunctions 2 | 3 | // Kotlin supports limited operator overloading via specific member or 4 | // extension functions. 5 | 6 | data class Zebra(var name: String = "Zebra") { 7 | 8 | // Operators are overloaded for a type by way of 'operator' funs that 9 | // have well known names. For '++' it's 'inc()', for '--' it's 'dec()', etc. 10 | 11 | operator fun inc() = Zebra("More $name") 12 | 13 | // See the Kotlin docs for the full list and their contracts: 14 | // https://kotlinlang.org/docs/reference/operator-overloading.html 15 | } 16 | 17 | // Operator overloading is one of the core Kotlin language features that 18 | // can be leveraged for creating internal DSLs. For example, overloading 19 | // 'invoke()' can be used to make a String executable. 20 | 21 | operator fun String.invoke() = println("To the moon, $this!") 22 | 23 | // Note that not all operators are overloadable, e.g. '=', '===', '!=='. 24 | 25 | // There is no mechanism for defining additional first-class operators, 26 | // though this can be simulated with infix functions to be covered next. 27 | 28 | // Like extension functions, operator overloads are scoped and must be 29 | // imported to have any effect. 30 | 31 | // Member operator overloads shadow extension operator overloads, which 32 | // prevents overriding the base operator implementations for common types. 33 | 34 | operator fun Int.minus(other: Int) = this + other 35 | 36 | fun main() { 37 | 38 | var zebra = Zebra() 39 | 40 | println(zebra) 41 | 42 | zebra++ 43 | 44 | println(zebra) 45 | 46 | "Alice"() 47 | 48 | println("1 - 1: ${1 - 1}") 49 | } 50 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/java/org/sdkotlin/intro/java/_05_variables/VariablesAndTypesInJava.java: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.java._05_variables; 2 | 3 | public class VariablesAndTypesInJava { 4 | 5 | // Fields get default values in Java 6 | 7 | boolean bool; // = false 8 | Boolean Bool; // = null 9 | byte b; // = 0 10 | Byte B; // = null 11 | char c; // = 'u0000', i.e. NULL 12 | Character C; // = null 13 | short s; // = 0 14 | Short S; // = null 15 | int i; // = 0 16 | Integer I; // = null 17 | long l; // = 0L 18 | Long L; // = null 19 | float f; // = 0.0f 20 | Float F; // = null 21 | double d; // = 0.0d 22 | Double D; // = null 23 | 24 | // They are mutable unless declared 'final' 25 | 26 | final int j = 0; 27 | 28 | void funWithVariables() { 29 | i++; 30 | 31 | // Cannot assign a value to final variable 'j' 32 | //j++; 33 | 34 | // Local variables are not given default values. 35 | int anotherI; 36 | 37 | // It's a compile time error to try to use them before they're initialized 38 | //System.out.println(anotherI); 39 | 40 | anotherI = 1; 41 | 42 | System.out.println(anotherI); 43 | 44 | // You can have late initialized immutable local variables in Java 45 | final int yetAnotherI; 46 | 47 | yetAnotherI = 1; 48 | 49 | // Once initialized they can't be changed 50 | //yetAnotherI++; 51 | } 52 | 53 | void funWithJava11TypeInference() { 54 | 55 | // Java 11 brings type inference for local variables 56 | 57 | var typeInferenceInJava11 = "This is new"; 58 | 59 | final var immutable = "Like 'val' in Kotlin"; 60 | 61 | // Type inferred variables must be initialized 62 | //var notGonnaDoIt; 63 | } 64 | 65 | // Type inference is not supported for fields in Java 66 | //var notHotDog = 1; 67 | } 68 | -------------------------------------------------------------------------------- /subprojects/user-defined-integrals-in-kotlin/src/test/kotlin/org/sdkotlin/integral/SignedIntegral.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.integral 2 | 3 | @JvmInline 4 | value class SignedIntegral(private val i: Int) : Integral { 5 | 6 | companion object { 7 | 8 | val MIN_VALUE: SignedIntegral = SignedIntegral(Int.MIN_VALUE) 9 | 10 | val MAX_VALUE: SignedIntegral = SignedIntegral(Int.MAX_VALUE) 11 | 12 | val ZERO: SignedIntegral = SignedIntegral(0) 13 | 14 | val ONE: SignedIntegral = SignedIntegral(1) 15 | 16 | val NEGATIVE_ONE: SignedIntegral = SignedIntegral(-1) 17 | } 18 | 19 | override val minValue: SignedIntegral 20 | get() = MIN_VALUE 21 | 22 | override val maxValue: SignedIntegral 23 | get() = MAX_VALUE 24 | 25 | override val zero: SignedIntegral 26 | get() = ZERO 27 | 28 | override val one: SignedIntegral 29 | get() = ONE 30 | 31 | override fun plus(other: SignedIntegral): SignedIntegral = 32 | SignedIntegral(i + other.i) 33 | 34 | override fun minus(other: SignedIntegral): SignedIntegral = 35 | SignedIntegral(i - other.i) 36 | 37 | override fun unaryMinus(): SignedIntegral = 38 | SignedIntegral(-i) 39 | 40 | override fun rem(other: SignedIntegral): SignedIntegral = 41 | SignedIntegral(i % other.i) 42 | 43 | override fun compareTo(other: SignedIntegral): Int = 44 | i.compareTo(other.i) 45 | 46 | operator fun rangeTo(other: SignedIntegral): ClosedRange = 47 | IntegralRange(this, other) 48 | 49 | operator fun rangeUntil(other: SignedIntegral): OpenEndRange = 50 | IntegralRange(this, other - ONE) 51 | 52 | override fun toString(): String = i.toString() 53 | } 54 | 55 | infix fun SignedIntegral.downTo(to: SignedIntegral): IntegralProgression = 56 | IntegralProgression.fromClosedRange(this, to, SignedIntegral.NEGATIVE_ONE) 57 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | gradlePluginPortal() 5 | } 6 | 7 | includeBuild("build-logic") 8 | } 9 | 10 | plugins { 11 | id("com.gradle.develocity") version "3.18" 12 | } 13 | 14 | dependencyResolutionManagement { 15 | @Suppress("UnstableApiUsage") 16 | repositories { 17 | mavenCentral() 18 | } 19 | } 20 | 21 | enableFeaturePreview("STABLE_CONFIGURATION_CACHE") 22 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") 23 | 24 | develocity { 25 | buildScan { 26 | publishing.onlyIf { !System.getenv("CI").isNullOrEmpty() } 27 | termsOfUseUrl.set("https://gradle.com/help/legal-terms-of-use") 28 | termsOfUseAgree.set("yes") 29 | } 30 | } 31 | 32 | rootProject.name = "sd-kotlin-talks" 33 | 34 | includeBuild("platforms") 35 | 36 | gradle.beforeProject { 37 | // Set group and version properties for all projects 38 | group = "org.sdkotlin" 39 | version = "1.0.0-SNAPSHOT" 40 | } 41 | 42 | include("subprojects:constant-dependencies:constant-consumer") 43 | include("subprojects:constant-dependencies:constant-producer") 44 | include("subprojects:di-with-koin") 45 | include("subprojects:effective-kotlin") 46 | include("subprojects:equalsverifier-with-kotlin") 47 | include("subprojects:kotlin-dl") 48 | include("subprojects:kotlin-for-java-devs") 49 | include("subprojects:kotlin-for-java-devs-client") 50 | include("subprojects:kotlin-reflect") 51 | include("subprojects:ksp-builder-generator:api:annotations") 52 | include("subprojects:ksp-builder-generator:api:builder") 53 | include("subprojects:ksp-builder-generator:processor") 54 | include("subprojects:ksp-builder-generator:processor:test-project") 55 | include("subprojects:sorting-in-kotlin") 56 | include("subprojects:tdd-in-kotlin") 57 | include("subprojects:testing-with-mokkery") 58 | include("subprojects:typed-errors-in-kotlin") 59 | include("subprojects:user-defined-integrals-in-kotlin") 60 | -------------------------------------------------------------------------------- /subprojects/effective-kotlin/src/main/kotlin/org/sdkotlin/meetup/idioms/ControlStructuresAsExpressions.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.meetup.idioms 2 | 3 | import org.json.JSONException 4 | import org.json.JSONObject 5 | import java.time.Month 6 | import java.time.Month.APRIL 7 | import java.time.Month.AUGUST 8 | import java.time.Month.DECEMBER 9 | import java.time.Month.FEBRUARY 10 | import java.time.Month.JANUARY 11 | import java.time.Month.JULY 12 | import java.time.Month.JUNE 13 | import java.time.Month.MARCH 14 | import java.time.Month.MAY 15 | import java.time.Month.NOVEMBER 16 | import java.time.Month.OCTOBER 17 | import java.time.Month.SEPTEMBER 18 | 19 | // Use `if` as an expression in place of the absent ternary operator. 20 | 21 | fun toBinary(boolean: Boolean): Byte { 22 | // return (boolean) ? 1 : 0 // Doesn't compile! 23 | return if (boolean) 1 else 0 24 | } 25 | 26 | // Use `when` as an expression for terse transformer functions. 27 | 28 | fun Month.toQuarter() = when (this) { 29 | JANUARY, FEBRUARY, MARCH -> 1 30 | APRIL, MAY, JUNE -> 2 31 | JULY, AUGUST, SEPTEMBER -> 3 32 | OCTOBER, NOVEMBER, DECEMBER -> 4 33 | } 34 | 35 | // Use `try` as an expression for setting values from functions that may throw 36 | // exceptions. 37 | 38 | fun parseJson(json: String): String { 39 | return try { 40 | JSONObject(json).getString("usergroup") 41 | } catch (e: JSONException) { 42 | "Unknown" 43 | } 44 | } 45 | 46 | fun main() { 47 | 48 | val boolean = true 49 | val binary = toBinary(boolean) 50 | println("Binary equivalent of $boolean is $binary") 51 | 52 | val month = JUNE 53 | val quarter = month.toQuarter() 54 | println("$month is in quarter $quarter") 55 | 56 | val partialJson = "'usergroup' : 'SD Kotlin'" 57 | val completeJson = "{ $partialJson }" 58 | println("Correct JSON parses to \"${parseJson(completeJson)}\"") 59 | println("Incorrect JSON parses to \"${parseJson(partialJson)}\"") 60 | } 61 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_05_0_variables_and_types/VariablesAndTypesInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._05_0_variables_and_types 2 | 3 | // var's are mutable, val's are read only 4 | 5 | // Pascal notation is used: name first, then, optionally or as needed, the type 6 | // after a colon (Same as Pascal, Scala, Swift, Go, Rust, etc.) 7 | 8 | // Variables aren't defaulted, and must be initialized 9 | 10 | // Kotlin does its best to abstract away primitives vs. wrapper types 11 | 12 | val aBoolean: Boolean = true // Boolean 13 | val aByte: Byte = 0 // Byte, type required because no literal 14 | val anotherByte = 0.toByte() // Alternatively, as literals are objects 15 | val aLiteralHexByte: Byte = 0xF // Kotlin supports hex literals 16 | val aLiteralBinaryByte: Byte = 0b0000_0000 // Kotlin supports binary literals 17 | val aCharacter = 'c' // Char, can't be treated as a number 18 | val aString = "Go, Kotlin!" // String 19 | val aShort = 0.toShort() // 16-bit Short, no literal 20 | val anInteger = 0 // 32-bit Int 21 | val aLong = 1_000_000L // 64-bit Long, lowercase 'l' not supported 22 | val aFloat = .0f // 32-bit Float, 'F' also supported 23 | val aDouble = 0.0 // 64-bit Double, 'd/D' not supported 24 | 25 | val i = 0 26 | var j = 0 27 | 28 | fun withVariables() { 29 | //i++ // Does not compile 30 | j++ 31 | } 32 | 33 | fun main() { 34 | println("Boolean: " + aBoolean) 35 | println("Byte: " + aByte) 36 | println("Other Byte: " + anotherByte) 37 | println("Hex Byte: " + aLiteralHexByte) 38 | println("Binary Byte: " + aLiteralBinaryByte) 39 | println("Char: " + aCharacter) 40 | println("String: " + aString) 41 | println("Short: " + aShort) 42 | println("Int: " + anInteger) 43 | println("Long: " + aLong) 44 | println("Float: " + aFloat) 45 | println("Double: " + aDouble) 46 | println("variable Int j: " + j) 47 | withVariables() 48 | println("variable Int j: " + j) 49 | } 50 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/java/org/sdkotlin/intro/java/_14_dataclasses/DataClassesInJava.java: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.java._14_dataclasses; 2 | 3 | import java.util.Objects; 4 | 5 | public class DataClassesInJava { 6 | 7 | public static void main(final String[] args) { 8 | System.out.println(new Person("Luke")); 9 | } 10 | } 11 | 12 | class Person { 13 | 14 | private String name; 15 | private String favoriteProgrammingLanguage; 16 | 17 | public Person(final String name) { 18 | this(name, "Kotlin"); 19 | } 20 | 21 | public Person(final String name, final String favoriteProgrammingLanguage) { 22 | this.name = name; 23 | this.favoriteProgrammingLanguage = favoriteProgrammingLanguage; 24 | } 25 | 26 | public String getName() { 27 | return name; 28 | } 29 | 30 | public void setName(final String name) { 31 | this.name = name; 32 | } 33 | 34 | public String getFavoriteProgrammingLanguage() { 35 | return favoriteProgrammingLanguage; 36 | } 37 | 38 | public void setFavoriteProgrammingLanguage(final String favoriteProgrammingLanguage) { 39 | this.favoriteProgrammingLanguage = favoriteProgrammingLanguage; 40 | } 41 | 42 | @Override 43 | public boolean equals(final Object o) { 44 | if (this == o) return true; 45 | if (o == null || getClass() != o.getClass()) return false; 46 | final var person = (Person) o; 47 | return Objects.equals(name, person.name) && 48 | Objects.equals(favoriteProgrammingLanguage, person.favoriteProgrammingLanguage); 49 | } 50 | 51 | @Override 52 | public int hashCode() { 53 | return Objects.hash(name, favoriteProgrammingLanguage); 54 | } 55 | 56 | @Override 57 | public String toString() { 58 | final var sb = new StringBuilder("Person{"); 59 | sb.append("name='").append(name).append('\''); 60 | sb.append(", favoriteProgrammingLanguage='").append(favoriteProgrammingLanguage).append('\''); 61 | sb.append('}'); 62 | return sb.toString(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_17_constants/ConstantsInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._17_constants 2 | 3 | // Kotlin compiles vals to properties, meaning in Java they have a private 4 | // field and a getter. 5 | 6 | val REGULAR_VAL = "val" 7 | 8 | // A private val gets a private getter. 9 | 10 | private val PRIVATE_VAL = "private val" 11 | 12 | // Add 'const' to make a val compile-time constant. It becomes a public static 13 | // final field in Java. It is also inlined at compile time. In a sense, 14 | // 'const' is to 'val' as 'inline' is to 'fun'. Note that if code is compiled 15 | // against a const in a library, and the value changes in the library, the code 16 | // won't reflect the change until it is recompiled. 17 | 18 | const val CONST_VAL = "const val" 19 | 20 | // Only primitive types and String can be const, as it is known they can be 21 | // inlined into expressions at compile-time. 22 | 23 | //const val CONST_NON_PRIMITIVE_VAL = BigInteger.ONE 24 | 25 | // This includes primitive Arrays. 26 | 27 | //const val CONST_PRIMITIVE_ARRAY_VAL = intArrayOf(1) 28 | 29 | // The unsigned types are now considered primitives. 30 | 31 | const val U_CONST = 1u 32 | 33 | class ConstantsInKotlinCompanion { 34 | 35 | // Const vals must be either top level, in a companion object, or in an 36 | // object. 37 | 38 | //const val NOPE = false 39 | 40 | companion object { 41 | 42 | // Unlike regular vals, const vals can be used in annotations. 43 | 44 | @Deprecated(CONST_VAL) 45 | val COMPANION_VAL = "val in companion object" 46 | 47 | //@Deprecated(REGULAR_VAL) 48 | const val COMPANION_CONST_VAL = "const val in companion object" 49 | } 50 | } 51 | 52 | object ConstantsInKotlinObject { 53 | 54 | val OBJECT_VAL = "val in object" 55 | 56 | const val OBJECT_CONST_VAL = "const val in object" 57 | } 58 | 59 | fun main() { 60 | println("Regular val: $REGULAR_VAL") 61 | println("Const val: $CONST_VAL") 62 | } 63 | -------------------------------------------------------------------------------- /subprojects/typed-errors-in-kotlin/src/main/kotlin/org/sdkotlin/typederrors/union/ConvertersWithUnionTypes.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.typederrors.union 2 | 3 | data class ConverterError( 4 | val e: Exception, 5 | ) 6 | 7 | fun interface Converter { 8 | operator fun invoke(input: I): O 9 | } 10 | 11 | // If Kotlin had union types (https://youtrack.jetbrains.com/issue/KT-13108/)... 12 | 13 | /* 14 | object StringToIntConverter : Converter { 15 | 16 | override fun invoke(input: String): ConverterError | Int = 17 | try { 18 | input.toInt() 19 | } catch (e: NumberFormatException) { 20 | ConverterError(e) 21 | } 22 | } 23 | 24 | object IntToStringConverter : Converter { 25 | override fun invoke(input: Int): String = 26 | input.toString() 27 | } 28 | 29 | // We can generalize over the `EitherConverter` supertype 30 | fun withUnionConverter( 31 | input: I, 32 | converter: Converter, 33 | ): E | O = 34 | converter(input) 35 | 36 | fun main() { 37 | 38 | val stringToIntSuccess = 39 | withUnionConverter("1", StringToIntConverter) 40 | 41 | println( 42 | // Would be an Int 43 | "withUnionConverter(\"1\", StringToIntConverter): $stringToIntSuccess" 44 | ) 45 | 46 | val stringToIntFailure = 47 | withUnionConverter("Nope", StringToIntConverter) 48 | 49 | println( 50 | // Would be a ConverterError 51 | "withUnionConverter(\"Nope\", StringToIntConverter): $stringToIntFailure" 52 | ) 53 | 54 | val intToStringSuccess = 55 | // The `Converter` subtype is substitutable... 56 | withUnionConverter(1, IntToStringConverter) 57 | 58 | println( 59 | // Would be a String 60 | "withUnionConverter(1, IntToStringConverter): $intToStringSuccess" 61 | ) 62 | 63 | // We can call the union `Converter` directly 64 | val intToStringDirect = IntToStringConverter(1) 65 | 66 | println( 67 | // Would be a String 68 | "direct IntToStringConverter(1): $intToStringDirect" 69 | ) 70 | } 71 | */ 72 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/java/org/sdkotlin/intro/java/_28_variance/VarianceInJava.java: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.java._28_variance; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class VarianceInJava { 7 | 8 | public static void main(final String[] args) { 9 | 10 | // Arrays are covariant in Java 11 | 12 | final String[] strings = new String[]{"Hello", "World"}; 13 | 14 | final Object[] objects = strings; 15 | 16 | // However, as they are both producers and consumers, that means 17 | // type-safety is not ensured by the compiler. 18 | 19 | try { 20 | objects[0] = 1; 21 | } catch (final ArrayStoreException e) { 22 | System.out.println(e); 23 | } 24 | 25 | // Item 28 in the third edition of "Effective Java" is "Prefer Lists to 26 | // Arrays" for this very reason. Collections are invariant in Java. 27 | 28 | final List listOfStrings = new ArrayList<>(); 29 | listOfStrings.add("Hello"); 30 | 31 | //final List nope = listOfStrings; // Does not compile. 32 | 33 | // Unless a use-site wildcard declaration is used. 34 | 35 | final List listOfThings = listOfStrings; 36 | 37 | // This is like List in Kotlin. It makes the list a covariant 38 | // producer. 39 | 40 | // We can read objects from it. 41 | 42 | final Object stringyThing = listOfThings.get(0); 43 | 44 | // But the compiler stops us from writing to it. 45 | 46 | //listOfThings.add("Testing"); // Does not compile. 47 | 48 | // We can use-site declare a contravariant consumer as well. 49 | 50 | final List listOfStringyThings = new ArrayList<>(); 51 | 52 | // We can write to it. 53 | 54 | listOfStringyThings.add("Testing"); 55 | 56 | // But not read from it. 57 | 58 | //final String alsoNope = listOfStringyThings.get(0); // Does not compile. 59 | 60 | // Not Strings at least. 61 | 62 | final Object unknownThing = listOfStringyThings.get(0); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_11_iteration/IterationInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._11_iteration 2 | 3 | import kotlin.random.Random.Default.nextBoolean as randomBoolean 4 | 5 | fun main() { 6 | 7 | // Kotlin lacks the C-style for loop, preferring the for-each loop with 8 | // ranges. 9 | 10 | for (i in 1..5) { 11 | println("Counting... $i") 12 | } 13 | 14 | //for (var i = 0; i <= 10; i++) { 15 | // 16 | //} 17 | 18 | // We can also use the functional approach. 19 | 20 | (1..5).forEach { i -> println("Again... $i") } 21 | 22 | // Ranges are inclusive of the end. Use 'until' for exclusive. 23 | 24 | (1 until 5).forEach { i -> println("And again... $i") } 25 | 26 | // The 'repeat' higher-order function is another option. 27 | 28 | repeat(5) { i -> println("Yet again... $i") } 29 | 30 | // You can "i--". 31 | 32 | for (i in 5 downTo 1) { 33 | println("Down... $i") 34 | } 35 | 36 | // You can skip a few. 37 | 38 | for (i in 1..5 step 2) { 39 | println("Odds... #$i") 40 | } 41 | 42 | // You can define ranges for characters and then get them as a list. 43 | 44 | val alphabet = ('a'..'z').toList() 45 | 46 | // You can for-each that list. 47 | 48 | for (l in alphabet) { 49 | println("Letters... $l") 50 | } 51 | 52 | // You can enumerate the indices of a list, and then access the list 53 | // elements by index. 54 | 55 | for (i in alphabet.indices) { 56 | println("Letter ${i + 1} of the alphabet is ${alphabet[i]}") 57 | } 58 | 59 | // You can also enumerate the destructured index and value from a list. 60 | 61 | for ((index, character) in alphabet.withIndex()) { 62 | println("Letter $index of the alphabet is $character") 63 | } 64 | 65 | // Kotlin also has while and do-while loops. 66 | 67 | var keepGoing = true 68 | 69 | while (keepGoing) { 70 | println("Taking my chances!") 71 | keepGoing = randomBoolean() 72 | } 73 | 74 | do { 75 | println("Will it ever end?") 76 | } while (randomBoolean()) 77 | } 78 | -------------------------------------------------------------------------------- /subprojects/user-defined-integrals-in-kotlin/src/main/kotlin/org/sdkotlin/integral/Octal.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.integral 2 | 3 | @JvmInline 4 | value class Octal internal constructor( 5 | internal val data: Int, 6 | ) : Integral { 7 | 8 | companion object { 9 | 10 | val MIN_VALUE = Octal(Int.MIN_VALUE) 11 | val MAX_VALUE = Octal(Int.MAX_VALUE) 12 | val NEGATIVE_ONE = Octal(-1) 13 | val ZERO = Octal(0) 14 | val ONE = Octal(1) 15 | 16 | internal fun octalRemainder(v1: Octal, v2: Octal): Octal = 17 | Octal((v1.toLong() % v2.toLong()).toInt()) 18 | } 19 | 20 | internal constructor(octalValue: String) : this(octalValue.toInt(8)) 21 | 22 | override val minValue: Octal get() = MIN_VALUE 23 | override val maxValue: Octal get() = MAX_VALUE 24 | override val zero: Octal get() = ZERO 25 | override val one: Octal get() = ONE 26 | 27 | override operator fun plus(other: Octal): Octal = 28 | Octal(data.plus(other.data)) 29 | 30 | override operator fun minus(other: Octal): Octal = 31 | Octal(data.minus(other.data)) 32 | 33 | override operator fun unaryMinus(): Octal = 34 | Octal(-data) 35 | 36 | override operator fun rem(other: Octal): Octal = 37 | octalRemainder(this, other) 38 | 39 | fun mod(other: Octal): Octal = rem(other) 40 | 41 | override fun compareTo(other: Octal): Int = 42 | data.compareTo(other.data) 43 | 44 | operator fun rangeTo(other: Octal): ClosedRange = 45 | IntegralRange(this, other) 46 | 47 | operator fun rangeUntil(other: Octal): OpenEndRange = 48 | IntegralRange(this, other - other.one) 49 | 50 | fun toUInt(): UInt = data.toUInt() 51 | 52 | fun toInt(): Int = data 53 | 54 | fun toLong(): Long = data.toLong() 55 | 56 | fun toUOctal(): UOctal = UOctal(data) 57 | 58 | override fun toString() = "%o".format(data) 59 | } 60 | 61 | fun Int.toOctal(): Octal = Octal(this) 62 | 63 | fun String.toOctal(): Octal = Octal(this) 64 | 65 | infix fun Octal.downTo(to: Octal): IntegralProgression = 66 | IntegralProgression.fromClosedRange(this, to, Octal.NEGATIVE_ONE) 67 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/java/org/sdkotlin/intro/java/_15_singletons/SingletonsInJava.java: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.java._15_singletons; 2 | 3 | // This is one of the ways to implement the singleton pattern in Java... 4 | class JavaSingletonWithStaticFinalField { 5 | 6 | public static final JavaSingletonWithStaticFinalField 7 | INSTANCE = new JavaSingletonWithStaticFinalField(); 8 | 9 | private JavaSingletonWithStaticFinalField() { 10 | } 11 | 12 | public void doIt() { 13 | System.out.println( 14 | "The most wonderful thing about tiggers is I'm the only one!" 15 | ); 16 | } 17 | } 18 | 19 | // This is another way to implement the singleton pattern in Java... 20 | class JavaSingletonWithSynchronizedMethod { 21 | 22 | private static JavaSingletonWithSynchronizedMethod instance; 23 | 24 | private JavaSingletonWithSynchronizedMethod() { 25 | } 26 | 27 | public static synchronized JavaSingletonWithSynchronizedMethod getInstance() { 28 | if (instance == null) { 29 | instance = new JavaSingletonWithSynchronizedMethod(); 30 | } 31 | return instance; 32 | } 33 | 34 | public void doIt() { 35 | System.out.println( 36 | "The most wonderful thing about tiggers is I'm the only one!" 37 | ); 38 | } 39 | } 40 | 41 | public class SingletonsInJava { 42 | 43 | public static void main(final String[] args) { 44 | 45 | final JavaSingletonWithStaticFinalField 46 | javaSingletonWithStaticFinalField; 47 | 48 | // Does not compile... 49 | //var singleton = new javaSingletonWithStaticFinalField(); 50 | 51 | javaSingletonWithStaticFinalField = 52 | JavaSingletonWithStaticFinalField.INSTANCE; 53 | javaSingletonWithStaticFinalField.doIt(); 54 | 55 | final JavaSingletonWithSynchronizedMethod 56 | javaSingletonWithSynchronizedMethod; 57 | 58 | // Does not compile... 59 | //var singleton = new JavaSingletonWithSynchronizedMethod(); 60 | 61 | javaSingletonWithSynchronizedMethod = 62 | JavaSingletonWithSynchronizedMethod.getInstance(); 63 | javaSingletonWithSynchronizedMethod.doIt(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /subprojects/di-with-koin/src/it/kotlin/org/sdkotlin/koin/it/hello/HelloModuleIT.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.koin.it.hello 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.assertj.core.api.Assertions.assertThatExceptionOfType 5 | import org.junit.jupiter.api.Test 6 | import org.junit.jupiter.api.extension.RegisterExtension 7 | import org.koin.core.error.InstanceCreationException 8 | import org.koin.core.qualifier.named 9 | import org.koin.test.KoinTest 10 | import org.koin.test.get 11 | import org.koin.test.inject 12 | import org.koin.test.junit5.KoinTestExtension 13 | import org.sdkotlin.koin.hello.DECLARED_COMPONENT 14 | import org.sdkotlin.koin.hello.DECLARED_COMPONENT_CONTAINER 15 | import org.sdkotlin.koin.hello.ExternalComponent 16 | import org.sdkotlin.koin.hello.ExternalComponentContainer 17 | import org.sdkotlin.koin.hello.HelloController 18 | import org.sdkotlin.koin.hello.RandomGreetingService 19 | import org.sdkotlin.koin.hello.helloModule 20 | 21 | private const val TESTING = "testing" 22 | 23 | internal class HelloModuleIT : KoinTest { 24 | 25 | private val helloController: HelloController by inject() 26 | 27 | @JvmField 28 | @RegisterExtension 29 | val koinTestExtension = KoinTestExtension.create { 30 | modules(helloModule) 31 | } 32 | 33 | @Test 34 | fun `test the hello module`() { 35 | 36 | val response = helloController.sayHello() 37 | 38 | assertThat(response).isIn(RandomGreetingService.GREETINGS) 39 | } 40 | 41 | @Test 42 | fun `test declaring an external component`() { 43 | 44 | assertThatExceptionOfType(InstanceCreationException::class.java).isThrownBy { 45 | get(named(DECLARED_COMPONENT_CONTAINER)) 46 | } 47 | 48 | val externalComponent = ExternalComponent(TESTING) 49 | 50 | getKoin().declare(externalComponent, named(DECLARED_COMPONENT)) 51 | 52 | val externalComponentContainer = 53 | get(named(DECLARED_COMPONENT_CONTAINER)) 54 | 55 | assertThat(externalComponentContainer.externalComponent.value) 56 | .isEqualTo(TESTING) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /subprojects/sorting-in-kotlin/src/test/kotlin/org/sdkoltin/sort/SwapKtTest.kt: -------------------------------------------------------------------------------- 1 | package org.sdkoltin.sort 2 | 3 | import org.assertj.core.api.Assertions.assertThatIllegalArgumentException 4 | import org.assertj.core.api.Assertions.assertThatIllegalStateException 5 | import org.junit.jupiter.api.Test 6 | 7 | internal class SwapKtTest { 8 | 9 | @Test 10 | fun `test swap for empty list`() { 11 | 12 | val emptyList = mutableListOf() 13 | 14 | assertThatIllegalStateException().isThrownBy { 15 | 16 | emptyList.swap(0, 1) 17 | } 18 | } 19 | 20 | @Test 21 | fun `test swap for single element list`() { 22 | 23 | val singleElementList = mutableListOf(1) 24 | 25 | assertThatIllegalStateException().isThrownBy { 26 | 27 | singleElementList.swap(0, 1) 28 | } 29 | } 30 | 31 | @Test 32 | fun `test swap for i out of bounds`() { 33 | 34 | val mutableList = mutableListOf(1, 2) 35 | 36 | assertThatIllegalArgumentException().isThrownBy { 37 | 38 | val i = mutableList.size 39 | 40 | mutableList.swap(i, 0) 41 | } 42 | } 43 | 44 | @Test 45 | fun `test swap for i less than zero`() { 46 | 47 | val mutableList = mutableListOf(1, 2) 48 | 49 | assertThatIllegalArgumentException().isThrownBy { 50 | 51 | val i = -1 52 | 53 | mutableList.swap(i, 0) 54 | } 55 | } 56 | 57 | @Test 58 | fun `test swap for j out of bounds`() { 59 | 60 | val mutableList = mutableListOf(1, 2) 61 | 62 | assertThatIllegalArgumentException().isThrownBy { 63 | 64 | val j = mutableList.size 65 | 66 | mutableList.swap(0, j) 67 | } 68 | } 69 | 70 | @Test 71 | fun `test swap for j less than zero`() { 72 | 73 | val mutableList = mutableListOf(1, 2) 74 | 75 | assertThatIllegalArgumentException().isThrownBy { 76 | 77 | val j = -1 78 | 79 | mutableList.swap(0, j) 80 | } 81 | } 82 | 83 | @Test 84 | fun `test swap for i same as j `() { 85 | 86 | val mutableList = mutableListOf(1, 2) 87 | 88 | assertThatIllegalArgumentException().isThrownBy { 89 | 90 | val i = 0 91 | val j = 0 92 | 93 | mutableList.swap(i, j) 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /subprojects/user-defined-integrals-in-kotlin/src/main/kotlin/org/sdkotlin/integral/UOctal.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.integral 2 | 3 | @JvmInline 4 | value class UOctal internal constructor( 5 | internal val data: Int, 6 | ) : Integral { 7 | 8 | companion object { 9 | 10 | val MIN_VALUE = UOctal(Int.MIN_VALUE) 11 | val MAX_VALUE = UOctal(Int.MAX_VALUE) 12 | val ZERO = UOctal(0) 13 | val ONE = UOctal(1) 14 | 15 | internal fun uOctalRemainder(v1: UOctal, v2: UOctal): UOctal = 16 | UOctal((v1.toLong() % v2.toLong()).toInt()) 17 | } 18 | 19 | internal constructor(octalValue: String) : this(octalValue.toInt(8)) 20 | 21 | override val minValue: UOctal get() = MIN_VALUE 22 | override val maxValue: UOctal get() = MAX_VALUE 23 | override val zero: UOctal get() = ZERO 24 | override val one: UOctal get() = ONE 25 | 26 | override operator fun plus(other: UOctal): UOctal = 27 | UOctal(data.plus(other.data)) 28 | 29 | override operator fun minus(other: UOctal): UOctal = 30 | UOctal(data.minus(other.data)) 31 | 32 | override operator fun unaryMinus(): UOctal = 33 | UOctal(-data) 34 | 35 | override operator fun rem(other: UOctal): UOctal = 36 | uOctalRemainder(this, other) 37 | 38 | fun mod(other: UOctal): UOctal = rem(other) 39 | 40 | override fun compareTo(other: UOctal): Int = 41 | data.compareTo(other.data) 42 | 43 | operator fun rangeTo(other: UOctal): ClosedRange = 44 | IntegralRange(this, other) 45 | 46 | operator fun rangeUntil(other: UOctal): OpenEndRange = 47 | IntegralRange(this, other - other.one) 48 | 49 | fun toUInt(): UInt = data.toUInt() 50 | 51 | fun toInt(): Int = data 52 | 53 | fun toLong(): Long = data.toLong() and 0xFFFF_FFFF 54 | 55 | fun toOctal(): Octal = data.toOctal() 56 | 57 | override fun toString() = "%o".format(data) 58 | } 59 | 60 | fun UInt.toUOctal(): UOctal = UOctal(this.toInt()) 61 | 62 | fun String.toUOctal(): UOctal = UOctal(this) 63 | 64 | infix fun UOctal.downTo(to: UOctal): IntegralProgression = 65 | IntegralProgression.fromClosedRange(this, to, Octal.NEGATIVE_ONE.toUOctal()) 66 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_13_constructors/ConstructorsAndInitializersInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._13_constructors 2 | 3 | // Kotlin has unique syntax for constructors. 4 | 5 | // Classes can have a primary constructor, that is declared on the same line. 6 | 7 | class Rebel constructor(val name: String, var jedi: Boolean) 8 | 9 | // If there are no access modifiers or annotations the constructor 10 | // keyword can be omitted. 11 | 12 | class Imperial(val name: String, var sith: Boolean) 13 | 14 | // Parameters with 'var' and 'val' automatically become class properties, 15 | // otherwise the parameters are just constructor arguments variables that 16 | // can be used during the initialization of the class. 17 | 18 | class Ewok(nameArg: String, var yubYub: Boolean = true) { 19 | 20 | // Note that properties can still be declared outside the primary 21 | // constructor if they're not to be parameterized at construction time. 22 | 23 | // You can think of the body of the class being the primary constructor 24 | // body, with property initializers and initializer blocks referencing the 25 | // arguments. Property initializers and initializer blocks are executed in 26 | // the order they appear. 27 | 28 | init { 29 | println(nameArg) 30 | println(yubYub) 31 | //println(name) // Not declared yet; does not compile. 32 | } 33 | 34 | val name = nameArg.uppercase() 35 | 36 | init { 37 | println(name) 38 | } 39 | } 40 | 41 | // As with functions, named and default arguments take care of most of the need 42 | // for overloaded constructors. Secondary constructors allow for different 43 | // annotations and modifiers. 44 | 45 | class Hutt(val name: String) { 46 | 47 | var isJabba: Boolean = false 48 | 49 | constructor(name: String, isJabba: Boolean) : this(name) { 50 | this.isJabba = isJabba 51 | } 52 | 53 | constructor(name: String, i: Int) : this(name, i == 1) 54 | } 55 | 56 | fun main() { 57 | val luke = Rebel("Luke", true) 58 | val han = Rebel("Han", false) 59 | val vader = Imperial("Darth Vader", true) 60 | val wicket = Ewok("Wicket") 61 | val jabba = Hutt("Jabba", true) 62 | } 63 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_25_varargfunctions/VarargsInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._25_varargfunctions 2 | 3 | fun main() { 4 | 5 | // Kotlin supports a variable number arguments (`vararg`) for a single 6 | // function parameter. 7 | 8 | fun zeroOrMoreThings(vararg things: String) { 9 | 10 | // They are passed as an array of the declared parameter type. 11 | 12 | println("things: ${things.contentDeepToString()}") 13 | } 14 | 15 | zeroOrMoreThings() // The passed array could be size 0. 16 | zeroOrMoreThings("one thing") 17 | zeroOrMoreThings("one thing", "two thing") 18 | 19 | // There can be only one vararg parameter, even if the types are different. 20 | 21 | //fun doesNotCompile(vararg someThings: String, vararg otherThings: Int) {} 22 | 23 | // Arrays can be passed as the vararg argument via the spread operator `*`. 24 | 25 | val arrayOfThings = arrayOf("one thing", "two thing") 26 | 27 | zeroOrMoreThings(*arrayOfThings) 28 | 29 | // A vararg parameter can be combined with other non-vararg parameters, in 30 | // which case it usually comes last in the parameter list. 31 | 32 | fun thingAndMaybeMore(oneThing: String, vararg moreThings: String) { 33 | println("oneThing: $oneThing") 34 | println("moreThings: ${moreThings.contentDeepToString()}") 35 | } 36 | 37 | thingAndMaybeMore("one thing") 38 | thingAndMaybeMore("one thing", "two thing") 39 | thingAndMaybeMore("one thing", "two thing", "three thing") 40 | 41 | // It doesn't have to come last if named arguments are used. 42 | 43 | fun varargAndInt(vararg things: String, howManyThings: Int) { 44 | println("things: ${things.contentDeepToString()}") 45 | println("howManyThings: $howManyThings") 46 | } 47 | 48 | varargAndInt(howManyThings = 0) 49 | varargAndInt("one thing", howManyThings = 1) 50 | varargAndInt("one thing", "two thing", howManyThings = 2) 51 | 52 | // Or if the last argument is a function. 53 | 54 | fun higherOrderVararg(vararg things: String, f: (String) -> Unit) { 55 | things.forEach { f(it) } 56 | } 57 | 58 | higherOrderVararg("one thing", "two thing", "red thing", "blue thing") { 59 | println(it) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /subprojects/effective-kotlin/src/main/kotlin/org/sdkotlin/meetup/effectivejava/item18and19/FavorComposition.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.meetup.effectivejava.item18and19 2 | 3 | /* Effective Java 4 | Item 18: Favor composition over inheritance 5 | Item 19: Design and document for inheritance or else prohibit it 6 | */ 7 | 8 | // Kotlin classes are final by default. 9 | 10 | class CantTouchThis 11 | 12 | //class HammerTime : CantTouchThis() // Doesn't compile 13 | 14 | // If you open them, the functions and properties that can be overridden must 15 | // also be documented. 16 | 17 | open class JeepWranglerSport { 18 | open val name = "Jeep Wrangler Sport" 19 | open fun howMuchFun() = "🙂" 20 | } 21 | 22 | // And then you document them again at the point where they are overridden. 23 | 24 | class JeepWranglerRubicon : JeepWranglerSport() { 25 | override val name = "Jeep Wrangler Rubicon" 26 | override fun howMuchFun() = "😃" 27 | } 28 | 29 | fun `with prohibiting or documenting for inheritance`() { 30 | val jeepWranglerSport = JeepWranglerSport() 31 | val jeepWranglerRubicon = JeepWranglerRubicon() 32 | 33 | println("The ${jeepWranglerSport.name} is ${jeepWranglerSport.howMuchFun()}") 34 | println("The ${jeepWranglerRubicon.name} is ${jeepWranglerRubicon.howMuchFun()}") 35 | } 36 | 37 | // Kotlin has built-on support for the Decorator pattern with delegates, which 38 | // in many cases can be used as a more loosely coupled alternative to 39 | // inheritance. It even works for closed classes! 40 | 41 | interface DoThings { 42 | fun gitRDone() 43 | } 44 | 45 | class DoCoolThings : DoThings { 46 | override fun gitRDone() { 47 | println("Doin' cool things.") 48 | } 49 | } 50 | 51 | class DoEvenCoolerThings(private val delegate: DoThings) : 52 | DoThings by delegate { 53 | 54 | override fun gitRDone() { 55 | println("Doin' chill things.") 56 | delegate.gitRDone() 57 | } 58 | } 59 | 60 | fun `with decorators`() { 61 | val doCoolThings: DoThings = DoCoolThings() 62 | val doEvenCoolerThings: DoThings = DoEvenCoolerThings(doCoolThings) 63 | 64 | doEvenCoolerThings.gitRDone() 65 | } 66 | 67 | fun main() { 68 | `with prohibiting or documenting for inheritance`() 69 | `with decorators`() 70 | } 71 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_21_smartcasts/SmartCastsInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._21_smartcasts 2 | 3 | var variableProperty: Any = 1 // To be used later. 4 | 5 | fun main() { 6 | 7 | // `Any` is the base type in Kotlin, similar to `Object` in Java. 8 | 9 | val thing: Any = "String" 10 | 11 | // Kotlin supports type checks with the `is` operator. 12 | // Combine with the boolean not operator, `!`, to negate the check. 13 | 14 | if (thing is String) { 15 | println("I'm a $thing!") 16 | } else if (thing !is Int) { 17 | println("I'm not an Int!") 18 | } 19 | 20 | // When a type is verified via a conditional, Kotlin automatically casts 21 | // the reference to that type. 22 | 23 | //thing.trim() // No `trim()` function on `Any`. 24 | 25 | if (thing is String) { 26 | println("The ${thing.trim()} API is available here!") 27 | } 28 | 29 | // This works for any provable control flow. 30 | 31 | if (thing !is String) return 32 | 33 | println("I'm a ${thing.trim()} from now on!") 34 | 35 | // Including with `when`. 36 | 37 | when (val otherThing: Any = "Other String") { 38 | is String -> println("otherThing: ${otherThing.trim()}") 39 | is Int -> println("otherThing + 1: ${otherThing.plus(1)}") 40 | } 41 | 42 | // Smart casts don't work for variable properties, as they could be changed 43 | // by another thread in between the check and the usage. 44 | 45 | if (variableProperty is Int) { 46 | // Some other thread could `variableProperty = "Foo"` at this point. 47 | //variableProperty.plus(1) // Does not compile 48 | } 49 | 50 | // See the Kotlin docs for other scenarios not eligible for smart casting: 51 | // https://kotlinlang.org/docs/reference/typecasts.html#smart-casts 52 | 53 | // Unsafe (i.e. runtime checked) casts can be done with the `as` operator. 54 | 55 | try { 56 | val integer = thing as Int 57 | } catch (e: ClassCastException) { 58 | println("${thing}s aren't Ints!") 59 | } 60 | 61 | // A preferred alternative is to use the safe cast operator, `as?`, which 62 | // returns null if the cast fails at runtime instead of throwing an 63 | // exception. 64 | 65 | val maybeAnInteger: Int? = thing as? Int 66 | 67 | println("maybeAnInteger: $maybeAnInteger") 68 | } 69 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_XX_coroutines/CoroutinesInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._XX_coroutines 2 | 3 | import kotlinx.coroutines.async 4 | import kotlinx.coroutines.awaitAll 5 | import kotlinx.coroutines.coroutineScope 6 | import kotlinx.coroutines.delay 7 | import kotlinx.coroutines.launch 8 | import kotlinx.coroutines.runBlocking 9 | import java.lang.Thread.sleep 10 | import kotlin.concurrent.thread 11 | import kotlin.system.measureTimeMillis 12 | 13 | const val ONE_THOUSAND = 1_000 14 | const val ONE_MILLION = 1_000_000 15 | 16 | fun main() { 17 | 18 | lotsOfCoroutines() 19 | 20 | asyncSum() 21 | 22 | // OutOfMemoryError! 23 | // (Doing this last, as it may not fail gracefully on JDK < 10.) 24 | lotsOfThreads() 25 | } 26 | 27 | private fun lotsOfCoroutines() = runBlocking { 28 | 29 | val elapsedTime = measureTimeMillis { 30 | coroutineScope { 31 | // Launch a lot of coroutines! 32 | repeat(ONE_MILLION) { 33 | launch { 34 | delay(ONE_THOUSAND.toLong()) 35 | } 36 | } 37 | } 38 | // The custom scope will wait (without blocking any threads) 39 | // on the launched coroutines 40 | } 41 | 42 | println( 43 | "Created $ONE_MILLION coroutines that sleep for $ONE_THOUSAND " + 44 | "milliseconds in $elapsedTime total milliseconds!" 45 | ) 46 | } 47 | 48 | private fun asyncSum() = runBlocking { 49 | 50 | val elapsedTime = measureTimeMillis { 51 | coroutineScope { 52 | val deferredInts = List( 53 | ONE_MILLION 54 | ) { i -> 55 | async { 56 | i + 1 57 | } 58 | } 59 | 60 | // The custom scope won't automatically wait on all the promises 61 | // to be fulfilled, hence the async nature, but it will cause all 62 | // the scoped coroutines to cancel if one of them fails. 63 | deferredInts.awaitAll() 64 | } 65 | } 66 | 67 | println( 68 | "The async initialization of a list with sequential numbers " + 69 | "between 1 and $ONE_MILLION took $elapsedTime milliseconds." 70 | ) 71 | } 72 | 73 | private fun lotsOfThreads() { 74 | 75 | (1..ONE_MILLION).forEach { 76 | try { 77 | thread { 78 | sleep(ONE_THOUSAND.toLong()) 79 | } 80 | } catch (e: OutOfMemoryError) { 81 | println("I can't do it, Captain! $it is just too many threads!") 82 | return 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_10_selection/SelectionInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._10_selection 2 | 3 | import kotlin.random.Random.Default.nextBoolean as randomBoolean 4 | 5 | fun main() { 6 | 7 | // If blocks in Kotlin are as in Java. 8 | 9 | if (true) { 10 | println(true) 11 | } else if (false) { 12 | println(false) 13 | } else { 14 | println("Huh?") 15 | } 16 | 17 | // Except they're expressions! 18 | // As such, no ternary operator. 19 | 20 | val randomBinaryInt = if (randomBoolean()) 1 else 0 21 | 22 | // Instead of `switch-case` we have the more powerful and elegant `when`. 23 | 24 | when (randomBinaryInt) { 25 | 26 | // The argument is "matched" against the conditions. First match wins. 27 | 100 -> { 28 | println("x is 100") 29 | } 30 | 31 | // Multiple conditions can be separated by commas to form "or" logic. 32 | // Curly braces are optional for one-liners. 33 | 0, 1 -> println("x is 0 or 1") 34 | 35 | // In addition to equality, 'in' and '!in' can be used to match against 36 | // ranges. 37 | in 2..10 -> println("x is between 2 and 10 (inclusive)") 38 | 39 | // 'is', and '!is' can be used to match against x's type. 40 | is Number -> println("x is a number") 41 | } 42 | 43 | // When can also be used as an expression. 44 | 45 | val mythRating = when (randomBoolean()) { 46 | true -> "confirmed!" 47 | false -> "busted!" 48 | // Used as an expression, `else` is required unless the compiler can 49 | // tell the conditions are exhaustive (e.g. both booleans, all 50 | // constants of an Enum, or all types of a sealed class). 51 | } 52 | 53 | println("The myth is $mythRating") 54 | 55 | // There is a feature request to support sealed `when`s in the case where 56 | // it's only used for side effects: 57 | // https://youtrack.jetbrains.com/issue/KT-12380 58 | 59 | // In the meantime we can use a trivial utility function to force handling 60 | // the `when` as an expression. 61 | 62 | when (randomBoolean()) { 63 | false -> println("0") 64 | // Then if it's not exhaustive we'll get the desired compiler error. 65 | true -> println("1") 66 | }.sealed() 67 | } 68 | 69 | /** 70 | * A utility extension function intended to be used to trigger the handling of 71 | * `when` as an expression. 72 | */ 73 | fun Unit.sealed() = this 74 | -------------------------------------------------------------------------------- /subprojects/typed-errors-in-kotlin/src/main/kotlin/org/sdkotlin/typederrors/raise/ConvertersWithRaise.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.typederrors.raise 2 | 3 | import arrow.core.raise.Raise 4 | import arrow.core.raise.either 5 | 6 | data class ConverterError( 7 | val e: Exception, 8 | ) 9 | 10 | fun interface ContextConverter { 11 | context(C) 12 | fun convert(input: I): O 13 | } 14 | 15 | // A `Converter` is `ContextConverter` with anything in context 16 | fun interface Converter : ContextConverter 17 | 18 | object StringToIntConverter : 19 | ContextConverter, String, Int> { 20 | 21 | context(Raise) 22 | override fun convert(input: String): Int = 23 | try { 24 | input.toInt() 25 | } catch (e: NumberFormatException) { 26 | raise(ConverterError(e)) 27 | } 28 | } 29 | 30 | object IntToStringConverter : Converter { 31 | context(Any?) 32 | override fun convert(input: Int): String = input.toString() 33 | } 34 | 35 | // We can generalize over the `ContextConverter` supertype 36 | context(C) 37 | fun withContextConverter( 38 | input: I, 39 | converter: ContextConverter, 40 | ): O = converter.convert(input) 41 | 42 | fun main() { 43 | 44 | val stringToIntSuccess = either { 45 | withContextConverter("1", StringToIntConverter) 46 | } 47 | 48 | println( 49 | "withContextConverter(\"1\", StringToIntConverter): $stringToIntSuccess" 50 | ) 51 | 52 | val stringToIntFailure = either { 53 | withContextConverter("Nope", StringToIntConverter) 54 | } 55 | 56 | println( 57 | "withContextConverter(\"Nope\", StringToIntConverter): $stringToIntFailure" 58 | ) 59 | 60 | // "Not enough information to infer type variable Error"... 61 | val intToStringSuccess = either { 62 | // The `Converter` subtype is substitutable 63 | withContextConverter(1, IntToStringConverter) 64 | } 65 | 66 | println( 67 | "withContextConverter(1, IntToStringConverter): $intToStringSuccess" 68 | ) 69 | 70 | // Direct call to the `Converter` subtype seems to require a `with` for 71 | // `null` or any object (e.g `Unit`). Otherwise, "No required context 72 | // receiver found". 73 | with(null) { 74 | val directIntToStringSuccess = IntToStringConverter.convert(1) 75 | println("direct IntToStringConverter(1): $directIntToStringSuccess") 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /subprojects/tdd-in-kotlin/src/test/kotlin/org/sdkotlin/tdd/sorting/JUnit5SortTest.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.tdd.sorting 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.junit.jupiter.api.Test 5 | 6 | internal class JUnit5SortTest { 7 | 8 | @Test 9 | fun `test empty array`() { 10 | 11 | // Assemble 12 | val unsortedArray = emptyArray() 13 | val sortedArray = emptyArray() 14 | 15 | // Act 16 | val sortResult = sort(unsortedArray) 17 | 18 | // Assert 19 | assertThat(sortResult) 20 | .isEqualTo(sortedArray) 21 | .isNotSameAs(unsortedArray) 22 | } 23 | 24 | @Test 25 | fun `test single element array`() { 26 | 27 | val unsortedArray = arrayOf(1) 28 | val sortedArray = arrayOf(1) 29 | 30 | val sortResult = sort(unsortedArray) 31 | 32 | assertThat(sortResult).isEqualTo(sortedArray) 33 | } 34 | 35 | @Test 36 | fun `test two element sorted array`() { 37 | 38 | val unsortedArray = arrayOf(1, 2) 39 | val sortedArray = arrayOf(1, 2) 40 | 41 | val sortResult = sort(unsortedArray) 42 | 43 | assertThat(sortResult).isEqualTo(sortedArray) 44 | } 45 | 46 | @Test 47 | fun `test two element unsorted array`() { 48 | 49 | val unsortedArray = arrayOf(2, 1) 50 | val sortedArray = arrayOf(1, 2) 51 | 52 | val sortResult = sort(unsortedArray) 53 | 54 | assertThat(sortResult).isEqualTo(sortedArray) 55 | } 56 | 57 | @Test 58 | fun `test three element sorted array`() { 59 | 60 | val unsortedArray = arrayOf(1, 2, 3) 61 | val sortedArray = arrayOf(1, 2, 3) 62 | 63 | val sortResult = sort(unsortedArray) 64 | 65 | assertThat(sortResult).isEqualTo(sortedArray) 66 | } 67 | 68 | @Test 69 | fun `test three element left hand unsorted array`() { 70 | 71 | val unsortedArray = arrayOf(2, 1, 3) 72 | val sortedArray = arrayOf(1, 2, 3) 73 | 74 | val sortResult = sort(unsortedArray) 75 | 76 | assertThat(sortResult).isEqualTo(sortedArray) 77 | } 78 | 79 | @Test 80 | fun `test three element right hand unsorted array`() { 81 | 82 | val unsortedArray = arrayOf(1, 3, 2) 83 | val sortedArray = arrayOf(1, 2, 3) 84 | 85 | val sortResult = sort(unsortedArray) 86 | 87 | assertThat(sortResult).isEqualTo(sortedArray) 88 | } 89 | 90 | @Test 91 | fun `test three element reversed array`() { 92 | 93 | val unsortedArray = arrayOf(3, 2, 1) 94 | val sortedArray = arrayOf(1, 2, 3) 95 | 96 | val sortResult = sort(unsortedArray) 97 | 98 | assertThat(sortResult).isEqualTo(sortedArray) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_20_interfaces/InterfacesInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._20_interfaces 2 | 3 | // Interfaces in Kotlin work mostly as you'd expect coming from Java, with a 4 | // few differences when it comes to properties and constants. 5 | 6 | interface Shape { 7 | 8 | // They can declare abstract functions. 9 | 10 | fun draw() 11 | 12 | // They can declare default functions. 13 | 14 | fun describe() = println("I'm shapely.") 15 | 16 | // They can declare abstract properties. 17 | 18 | val name: String 19 | 20 | // Constants are not supported. 21 | 22 | //const val CONSTANT = false 23 | 24 | // Companion objects are, and you can put constants in those. 25 | 26 | companion object { 27 | 28 | const val COMPANION_CONSTANT = true 29 | } 30 | 31 | // Interfaces can't store state, so any non-abstract properties must be 32 | // computed. 33 | 34 | val abstractName 35 | get() = "Shape" 36 | 37 | // They can be var given a side effect setter. Maybe this could be 38 | // exploited when creating internal DSLs. 39 | 40 | var hmm 41 | get() = "🤔" 42 | set(value) = println("Hmm... $value") 43 | } 44 | 45 | class Circle(override val name: String) : Shape, Serializable { 46 | 47 | override fun draw() = println("O") 48 | 49 | // Default methods can be overridden. 50 | 51 | override fun describe() = println("I'm no square.") 52 | } 53 | 54 | // Multiple interface inheritance is supported, as in Java. 55 | 56 | interface Serializable 57 | 58 | class Triangle : Shape, Serializable { 59 | 60 | override fun draw() = println("▲") 61 | 62 | override val name: String 63 | get() = "Pointy" 64 | } 65 | 66 | // If an interface declares only a single abstract method (SAM), it can be 67 | // declared as a functional interface: 68 | 69 | fun interface ToStringConverter { 70 | fun convert(a: Any?): String 71 | } 72 | 73 | // Then is can be initialized with a lambda via a SAM conversion 74 | 75 | fun withFunctionalInterfaces() { 76 | 77 | val intToStringConverter = ToStringConverter { it.toString() } 78 | 79 | println(intToStringConverter.convert(1)) 80 | } 81 | 82 | fun main() { 83 | val circle = Circle("Roundy") 84 | 85 | print("circle.draw(): ") 86 | circle.draw() 87 | 88 | print("circle.describe(): ") 89 | circle.describe() 90 | 91 | println("circle.name: ${circle.name}") 92 | println("circle.abstractName: ${circle.abstractName}") 93 | println("circle.hmm: ${circle.hmm}") 94 | print("circle.hmm = ") 95 | circle.hmm = "For DSLs maybe?" 96 | 97 | withFunctionalInterfaces() 98 | } 99 | -------------------------------------------------------------------------------- /subprojects/typed-errors-in-kotlin/src/main/kotlin/org/sdkotlin/typederrors/either/ConvertersWithEither.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.typederrors.either 2 | 3 | import arrow.core.Either 4 | import arrow.core.raise.either 5 | 6 | data class ConverterError( 7 | val e: Exception, 8 | ) 9 | 10 | fun interface Converter { 11 | fun convert(input: I): O 12 | } 13 | 14 | object StringToIntConverter : Converter> { 15 | 16 | override fun convert(input: String): Either = either { 17 | try { 18 | input.toInt() 19 | } catch (e: NumberFormatException) { 20 | raise(ConverterError(e)) 21 | } 22 | } 23 | } 24 | 25 | object IntToStringConverter : Converter { 26 | override fun convert(input: Int): String = 27 | input.toString() 28 | } 29 | 30 | // We can generalize over the `Converter` type 31 | fun withConverter( 32 | input: I, 33 | converter: Converter, 34 | ): O = 35 | converter.convert(input) 36 | 37 | fun main() { 38 | 39 | // We can directly call an `Either`-returning `Converter` if we use the 40 | // `either` builder and `bind()` 41 | val directStringToIntSuccess = either { 42 | StringToIntConverter.convert("1").bind() 43 | } 44 | 45 | println( 46 | // Is an `Either.Right` 47 | "StringToIntConverter.convert(\"1\"): $directStringToIntSuccess" 48 | ) 49 | 50 | val directStringToIntFailure = either { 51 | StringToIntConverter.convert("Nope").bind() 52 | } 53 | 54 | println( 55 | // Is an `Either.Left` 56 | "StringToIntConverter.convert(\"Nope\"): $directStringToIntFailure" 57 | ) 58 | 59 | // We can pass an `Either`-returning `Converter` to `withConverter` if we 60 | // use the `either` builder and `bind()` 61 | val stringToIntSuccess = either { 62 | withConverter("1", StringToIntConverter).bind() 63 | } 64 | 65 | println( 66 | // Is an `Either.Right` 67 | "withConverter(\"1\", StringToIntConverter): $stringToIntSuccess" 68 | ) 69 | 70 | val stringToIntFailure = either { 71 | withConverter("Nope", StringToIntConverter).bind() 72 | } 73 | 74 | println( 75 | // Is an `Either.Left` 76 | "withConverter(\"Nope\", StringToIntConverter): $stringToIntFailure" 77 | ) 78 | 79 | // We can directly call `withConverter` with a non-`Either`-returning 80 | // `Converter` 81 | val intToStringSuccess = 82 | withConverter(1, IntToStringConverter) 83 | 84 | println( 85 | // Is a String. 86 | "withConverter(1, IntToStringConverter): $intToStringSuccess" 87 | ) 88 | 89 | // We can also directly call a non-`Either`-returning `Converter` 90 | val directIntToStringSuccess: String = IntToStringConverter.convert(1) 91 | 92 | println( 93 | // Is a String 94 | "IntToStringConverter.convert(1): $directIntToStringSuccess" 95 | ) 96 | } 97 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_08_0_functions/FunctionsInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._08_0_functions 2 | 3 | // Functions are fun in Kotlin 4 | 5 | fun function() {} 6 | 7 | // As with variables, the return type comes second 8 | 9 | fun add(a: Int, b: Int): Int { 10 | return a + b 11 | } 12 | 13 | // One-liners can be defined with "=" instead of curly braces 14 | 15 | fun subtract(a: Int, b: Int): Int = a - b 16 | 17 | // Return types for one-liners are optional when they can be inferred 18 | 19 | fun multiply(a: Int, b: Int) = a * b 20 | 21 | // The rough equivalent of void is "Unit". 22 | // Unit is an actual object, a singleton. 23 | // It is the default return type, so it can be omitted from the signature. 24 | // No return statement is required for functions that return Unit. 25 | 26 | fun voidish(): Unit {} 27 | 28 | // Function parameters are always val 29 | 30 | fun cantTouchThis(i: Int): Int { 31 | 32 | // Val cannot be reassigned 33 | //return ++i 34 | 35 | //You can assign their value to a mutable var 36 | //var j = i 37 | //return ++j 38 | 39 | // Or just try another approach 40 | return i + 1 41 | } 42 | 43 | // Parameters can have default values. 44 | 45 | fun defaultParams(x: Int = 0): Int { 46 | return x 47 | } 48 | 49 | fun useDefaultParams() { 50 | println("Using default: ${defaultParams()}") // Prints 0 51 | println("Overriding default: ${defaultParams(1)}") // Prints 1 52 | } 53 | 54 | fun moreDefaultedParams( 55 | firstName: String, 56 | lastName: String, 57 | favoriteProgrammingLanguage: String = "Kotlin", 58 | domain: String = "example.com", 59 | 60 | // A default can refer to earlier arguments, whether they're defaulted 61 | // or not. 62 | email: String = "$firstName.$lastName@$domain", 63 | ) { 64 | 65 | println("$firstName $lastName $email $favoriteProgrammingLanguage") 66 | } 67 | 68 | fun useNamedParams() { 69 | 70 | // You can name function call arguments to disambiguate defaulted params, 71 | // or just to make function calls with many arguments more clear. 72 | 73 | moreDefaultedParams( 74 | firstName = "John", 75 | lastName = "Smith", 76 | domain = "smithery.com", 77 | ) 78 | 79 | // You can mix positional and named arguments, but the positional ones all 80 | // need to come first. 81 | 82 | moreDefaultedParams( 83 | "John", 84 | lastName = "Smith" 85 | ) 86 | 87 | // Default and named arguments eliminate the need for method overloads in 88 | // many cases! 89 | } 90 | 91 | fun main() { 92 | function() // no-op 93 | println("add(2, 2): ${add(2, 2)}") 94 | println("subtract(3, 1): ${subtract(3, 1)}") 95 | println("multiply(3, 3): ${multiply(3, 3)}") 96 | println("voidish(): ${voidish()}") 97 | println("cantTouchThis(1): ${cantTouchThis(1)}") 98 | useDefaultParams() 99 | useNamedParams() 100 | } 101 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_30_sealedtypes/SealedTypesInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._30_sealedtypes 2 | 3 | import org.sdkotlin.intro.kotlin._30_sealedtypes.otherpackage.AiNPC 4 | 5 | // Kotlin has sealed classes and interfaces to represent a restricted hierarchy 6 | // of types. 7 | 8 | /** 9 | * Direct subtypes of sealed types must be in the same package and module. 10 | * 11 | * @see UnknownPlayer 12 | * 13 | * Also see OtherPackagePlayer.kt, and OtherModulePlayer.kt. 14 | */ 15 | sealed interface Player { 16 | val name: String 17 | val health: Int 18 | } 19 | 20 | // Declaring a subtype to also be sealed extends the depth of the sealed 21 | // hierarchy. 22 | 23 | sealed class HumanPlayer : Player { 24 | abstract val topScore: Int 25 | } 26 | 27 | // Sealed classes are similar to abstract classes. You can't directly 28 | // instantiate them. Any constructors are implicitly protected. 29 | 30 | fun notGonnaDoIt() { 31 | 32 | //val player = HumanPlayer() // Does not compile. 33 | } 34 | 35 | // Direct subclasses of sealed classes that are not sealed determine the 36 | // exhaustively known part of that branch of the sealed type hierarchy. 37 | 38 | data class LocalPlayer( 39 | override val name: String, 40 | override val health: Int = 100, 41 | override val topScore: Int = 0, 42 | ) : HumanPlayer() 43 | 44 | data class RemotePlayer( 45 | override val name: String, 46 | override val health: Int = 100, 47 | override val topScore: Int = 0, 48 | val ipAddress: String, 49 | ) : HumanPlayer() 50 | 51 | /** 52 | * If the direct subclass is abstract or open, its subclasses can be defined in 53 | * other packages or modules. 54 | * 55 | * @see [AiNPC] 56 | */ 57 | abstract class NPC : Player { 58 | abstract fun attack(): String 59 | } 60 | 61 | // Sealed classes work well with sealed `when`s and smart casting. 62 | 63 | fun topScore(player: Player) = when (player) { 64 | is HumanPlayer -> player.topScore.toString() 65 | is NPC -> "N/A" 66 | is UnknownPlayer -> "Unknown" 67 | } 68 | 69 | // Adding a type to the sealed hierarchy later will be called out by the 70 | // compiler for exhaustive `when`s. 71 | 72 | //class AlienPlayer(override val name: String, override var health: Int) : Player 73 | 74 | fun main() { 75 | notGonnaDoIt() 76 | val bowser = AiNPC("Bowser") 77 | val mario = LocalPlayer(name = "Mario", topScore = 65) 78 | val luigi = 79 | RemotePlayer(name = "Luigi", ipAddress = "10.0.0.21", topScore = 70) 80 | 81 | val players = listOf(bowser, mario, luigi) 82 | 83 | players.forEach { player -> 84 | 85 | val intro = when (player) { 86 | is NPC -> "${player.name}! ${player.attack()} with " + 87 | "${player.health} health!" 88 | is RemotePlayer -> "${player.name} from ${player.ipAddress}! " + 89 | "Top Score: ${topScore(player)}." 90 | is HumanPlayer -> "${player.name}! Top Score: ${player.topScore}." 91 | is UnknownPlayer -> "${player.name}! Health: ${player.health}." 92 | } 93 | 94 | println(intro) 95 | } 96 | } 97 | 98 | fun playerFactory(): Player = AiNPC("Big Boss") 99 | -------------------------------------------------------------------------------- /subprojects/user-defined-integrals-in-kotlin/src/main/kotlin/org/sdkotlin/integral/IntegralProgression.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.integral 2 | 3 | class IntegralProgression> internal constructor( 4 | start: I, 5 | endInclusive: I, 6 | override val step: I, 7 | ) : Progression { 8 | 9 | init { 10 | require(step != step.zero) { "Step must be non-zero." } 11 | require(step != step.minValue) { 12 | "Step must be greater than the type's minimum value to avoid overflow on negation." 13 | } 14 | } 15 | 16 | override val first: I = start 17 | 18 | override val last: I = getProgressionLastElement(start, endInclusive, step) 19 | 20 | override fun iterator(): Iterator = 21 | IntegralProgressionIterator(first, last, step) 22 | 23 | override fun isEmpty(): Boolean = 24 | if (step > step.zero) first > last else first < last 25 | 26 | override fun equals(other: Any?): Boolean = 27 | other is IntegralProgression<*> && (isEmpty() && other.isEmpty() || 28 | first == other.first && last == other.last && step == other.step) 29 | 30 | override fun hashCode(): Int = 31 | if (isEmpty()) { 32 | -1 33 | } else { 34 | (31 * (31 * first.hashCode() + last.hashCode()) + step.hashCode()) 35 | } 36 | 37 | override fun toString(): String = 38 | if (step > step.zero) { 39 | "$first..$last step $step" 40 | } else { 41 | "$first downTo $last step ${-step}" 42 | } 43 | 44 | companion object { 45 | 46 | fun > fromClosedRange( 47 | rangeStart: I, 48 | rangeEnd: I, 49 | step: I, 50 | ): IntegralProgression = 51 | IntegralProgression(rangeStart, rangeEnd, step) 52 | 53 | private fun > getProgressionLastElement( 54 | start: I, 55 | end: I, 56 | step: I, 57 | ): I = 58 | when { 59 | step > step.zero -> if (start >= end) { 60 | end 61 | } else { 62 | end - differenceModulo(end, start, step) 63 | } 64 | step < step.zero -> if (start <= end) { 65 | end 66 | } else { 67 | end + differenceModulo(start, end, -step) 68 | } 69 | else -> throw IllegalArgumentException("Step is zero.") 70 | } 71 | 72 | private fun > differenceModulo(a: I, b: I, c: I): I { 73 | val ac = a % c 74 | val bc = b % c 75 | return if (ac >= bc) ac - bc else ac - bc + c 76 | } 77 | } 78 | 79 | private class IntegralProgressionIterator>( 80 | first: I, 81 | private val last: I, 82 | private val step: I, 83 | ) : Iterator { 84 | 85 | private var hasNext: Boolean = 86 | if (step > step.zero) { 87 | first <= last 88 | } else { 89 | first >= last 90 | } 91 | 92 | private var next = if (hasNext) first else last 93 | 94 | override fun hasNext(): Boolean = hasNext 95 | 96 | override fun next(): I { 97 | val value = next 98 | if (value == last) { 99 | if (!hasNext) throw NoSuchElementException() 100 | hasNext = false 101 | } else { 102 | next += step 103 | } 104 | return value 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /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 | 74 | 75 | @rem Execute Gradle 76 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* 77 | 78 | :end 79 | @rem End local scope for the variables with windows NT shell 80 | if %ERRORLEVEL% equ 0 goto mainEnd 81 | 82 | :fail 83 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 84 | rem the _cmd.exe /c_ return code! 85 | set EXIT_CODE=%ERRORLEVEL% 86 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 87 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 88 | exit /b %EXIT_CODE% 89 | 90 | :mainEnd 91 | if "%OS%"=="Windows_NT" endlocal 92 | 93 | :omega 94 | -------------------------------------------------------------------------------- /.junie/guidelines.md: -------------------------------------------------------------------------------- 1 | # SD Kotlin Project Guidelines 2 | 3 | ## Project Overview 4 | 5 | This is a Kotlin-based project demonstrating various Kotlin programming concepts 6 | and best practices through practical examples. The project also contains 7 | reproducers for various issues in the dependencies used. 8 | 9 | The project leverages modern Kotlin features including Coroutines, KSP (Kotlin 10 | Symbol Processing), and functional programming with Arrow. 11 | 12 | As this project is primarily just for code and technology demonstration 13 | purposes, there are no tagged releases or published build outputs. 14 | 15 | ## Technology Stack 16 | 17 | - **Kotlin**: Primary programming language 18 | - **Java**: Comparative code examples 19 | - **Gradle**: Build system 20 | - **Arrow**: Functional programming 21 | - **Koin**: Dependency injection 22 | - **Coroutines**: Asynchronous programming 23 | - **KSP and KotlinPoet**: Code generation 24 | 25 | ## Project Structure 26 | 27 | ```text 28 | . 29 | ├── .github/ # GitHub Actions CI workflows 30 | ├── .idea/ # IntelliJ IDEA configuration 31 | ├── build-logic/ # Included build with custom Gradle build logic 32 | ├── config/detekt/ # Detekt configuration files 33 | ├── detekt-rules/ # Included build with custom Detekt static code analysis rules 34 | ├── gradle/ # Gradle Wrapper configuration and version catalog 35 | ├── platforms/ # Gradle dependency management platforms 36 | └── subprojects/ # Main project modules 37 | ``` 38 | 39 | ## Build & Run 40 | 41 | - The project uses Gradle as the build system 42 | - Build and verify the project (includes unit and integration tests): 43 | `./gradlew build` 44 | - Verify dependency configuration: `./gradlew buildHealth` 45 | - Check for potential dependency updates: `./gradlew dependencyUpdates` 46 | 47 | ## Testing 48 | 49 | - JUnit Jupiter is the primary testing framework 50 | - AssertJ is used for fluent assertions 51 | - Mockk is available for mocking 52 | - Prefer plain Kotlin mocks 53 | - Use 54 | Gradle [test fixtures](https://docs.gradle.org/current/userguide/java_testing.html#sec:java_test_fixtures) 55 | for shared test doubles and utilities 56 | - Tests are organized into unit (`src/test`) and integration (`src/it`) suites 57 | - JUnit 5's `@DynamicTest` feature is used for test parameterization 58 | - Run unit tests: `./gradlew test` 59 | - Run integration tests: `./gradlew integrationTest` 60 | - Run all tests: `./gradlew check` 61 | 62 | ## Version Control 63 | 64 | - The primary development branch is `main` 65 | - Git LFS is used for versioning binaries 66 | 67 | ## Code Style 68 | 69 | - Follow the versioned IntelliJ code style configuration 70 | - Format all changed code before commit 71 | 72 | ## Dependencies 73 | 74 | - Use the Gradle version catalog and platforms for dependency management 75 | - The GitHub Actions CI build will fail if `./gradlew buildHealth` does 76 | 77 | ## Code Quality 78 | 79 | - As the project code sometimes contains partial examples, antipattern 80 | demonstrations, issue reproducers, and uses preview features, it may contain 81 | many compiler, static analysis, and runtime warnings. No effort is made to 82 | suppress these with `@Suppress` annotations, as that would excessively 83 | clutter the examples. 84 | -------------------------------------------------------------------------------- /subprojects/tdd-in-kotlin/src/test/kotlin/org/sdkotlin/tdd/fizzbuzz/FizzBuzzTest.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.tdd.fizzbuzz 2 | 3 | import org.assertj.core.api.Assertions.assertThat 4 | import org.junit.jupiter.api.DynamicTest.dynamicTest 5 | import org.junit.jupiter.api.Test 6 | import org.junit.jupiter.api.TestFactory 7 | 8 | internal class FizzBuzzTest { 9 | 10 | @TestFactory 11 | fun testFizzBuzzOf() = listOf( 12 | 1 to "1", 13 | 2 to "2", 14 | 3 to "Fizz", 15 | 4 to "4", 16 | 5 to "Buzz", 17 | 6 to "Fizz", 18 | 7 to "7", 19 | 7 to "7", 20 | 8 to "8", 21 | 9 to "Fizz", 22 | 10 to "Buzz", 23 | 15 to "FizzBuzz", 24 | 30 to "FizzBuzz", 25 | 99 to "Fizz", 26 | 100 to "Buzz", 27 | ).map { (input, expected) -> 28 | dynamicTest("fizzBuzzOf($input) is \"$expected\"") { 29 | assertThat(fizzBuzzOf(input)).isEqualTo(expected) 30 | } 31 | } 32 | 33 | @Test 34 | fun `test fizzBuzz1To100() prints 100 FizzBuzzes with test Printer`() { 35 | 36 | val printer = TestPrinter() 37 | 38 | fizzBuzz1to100(printer) 39 | 40 | val prints = printer.prints 41 | 42 | assertThat(prints).hasSize(100) 43 | assertThat(prints[0]).isEqualTo("1") 44 | assertThat(prints[1]).isEqualTo("2") 45 | assertThat(prints[2]).isEqualTo("Fizz") 46 | assertThat(prints[4]).isEqualTo("Buzz") 47 | assertThat(prints[14]).isEqualTo("FizzBuzz") 48 | } 49 | 50 | @Test 51 | fun `test fizzBuzz1To100() prints 100 FizzBuzzes with anonymous Printer object`() { 52 | 53 | val printer = object : Printer { 54 | 55 | val prints = mutableListOf() 56 | 57 | override fun print(fizzBuzz: String) { 58 | prints.add(fizzBuzz) 59 | } 60 | } 61 | 62 | fizzBuzz1to100(printer) 63 | 64 | val prints = printer.prints 65 | 66 | assertThat(prints).hasSize(100) 67 | assertThat(prints[0]).isEqualTo("1") 68 | assertThat(prints[1]).isEqualTo("2") 69 | assertThat(prints[2]).isEqualTo("Fizz") 70 | assertThat(prints[4]).isEqualTo("Buzz") 71 | assertThat(prints[14]).isEqualTo("FizzBuzz") 72 | } 73 | 74 | @Test 75 | fun `test fizzBuzz1To100() prints 100 FizzBuzzes with anonymous function object`() { 76 | 77 | val printer = object : (String) -> Unit { 78 | 79 | val prints = mutableListOf() 80 | 81 | override fun invoke(fizzBuzz: String) { 82 | prints.add(fizzBuzz) 83 | } 84 | } 85 | 86 | fizzBuzz1to100(printer) 87 | 88 | val prints = printer.prints 89 | 90 | assertThat(prints).hasSize(100) 91 | assertThat(prints[0]).isEqualTo("1") 92 | assertThat(prints[1]).isEqualTo("2") 93 | assertThat(prints[2]).isEqualTo("Fizz") 94 | assertThat(prints[4]).isEqualTo("Buzz") 95 | assertThat(prints[14]).isEqualTo("FizzBuzz") 96 | } 97 | 98 | @Test 99 | fun `test fizzBuzz1To100() prints 100 FizzBuzzes with closure`() { 100 | 101 | val prints = mutableListOf() 102 | 103 | val mockPrintLn: (String) -> Unit = { print -> prints.add(print) } 104 | 105 | fizzBuzz1to100(mockPrintLn) 106 | 107 | assertThat(prints).hasSize(100) 108 | assertThat(prints[0]).isEqualTo("1") 109 | assertThat(prints[1]).isEqualTo("2") 110 | assertThat(prints[2]).isEqualTo("Fizz") 111 | assertThat(prints[4]).isEqualTo("Buzz") 112 | assertThat(prints[14]).isEqualTo("FizzBuzz") 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_29_enums/EnumsInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._29_enums 2 | 3 | import org.sdkotlin.intro.kotlin._29_enums.State.DONE 4 | import org.sdkotlin.intro.kotlin._29_enums.TrafficLight.CAUTION 5 | import org.sdkotlin.intro.kotlin._29_enums.TrafficLight.GO 6 | import org.sdkotlin.intro.kotlin._29_enums.TrafficLight.STOP 7 | 8 | // Kotlin supports enum classes, which define a fixed set of instances 9 | // (enum constants) that implement the enum's type. 10 | 11 | enum class State { 12 | NOT_DONE, DONE 13 | } 14 | 15 | fun `with states`() { 16 | 17 | // A variable of the enum type can be initialized to only one of those 18 | // instances. 19 | 20 | var currentState = State.NOT_DONE // Enum types are inferred. 21 | 22 | // As a matter of style, enum constants are sometimes imported. 23 | 24 | currentState = DONE 25 | 26 | // Nullability and type safety are enforced as usual. 27 | 28 | //currentState = null // Does not compile. 29 | //currentState = "Not a State" // Does not compile. 30 | } 31 | 32 | // They can have a primary constructor with immutable and mutable properties. 33 | 34 | enum class TrafficLight(val color: String, var lightTime: Int) { 35 | GO("Green", 30), 36 | CAUTION("Yellow", 5), 37 | STOP("Red", 20), 38 | } 39 | 40 | fun `while driving`(light: TrafficLight) { 41 | 42 | println("light.color: ${light.color}") 43 | 44 | // Enums can be exhaustive for `when` expressions. 45 | 46 | val action = when (light) { 47 | GO -> "Beep beep!" 48 | CAUTION -> "Whoa!" 49 | STOP -> "Screech!" 50 | } 51 | 52 | println("action: $action") 53 | 54 | // Do keep versioning in mind, particularly for enums used in API, as 55 | // adding a new instance to the enum will break such expressions everywhere. 56 | 57 | // Also be mindful of mutable enum properties. They are global mutable 58 | // state, with all the thread safety implications entailed. 59 | 60 | GO.lightTime = 40 61 | } 62 | 63 | // Enum classes can declare abstract methods and properties to be implemented 64 | // by anonymous classes for each instance. Use a semicolon to separate the enum 65 | // constants from the rest of the class. 66 | 67 | enum class Trio { 68 | 69 | GUITAR { 70 | override val strings = 6 71 | override fun play() = "Power cord!" 72 | }, 73 | BASS { 74 | override val strings = 4 75 | override fun play() = "Groove!" 76 | }, 77 | DRUMS { 78 | override val strings = 0 79 | override fun play() = "Swing!" 80 | }; 81 | 82 | abstract val strings: Int 83 | abstract fun play(): String 84 | } 85 | 86 | // Enums can't extend classes, but they can implement interfaces. 87 | 88 | interface Dandy { 89 | val dandy: Boolean 90 | } 91 | 92 | interface Inseparable { 93 | val inseparable: Boolean 94 | } 95 | 96 | enum class ThreeMusketeer : Inseparable, Dandy { 97 | ATHOS { 98 | override val dandy = false 99 | }, 100 | PORTHOS { 101 | override val dandy = true 102 | }, 103 | ARAMIS { 104 | override val dandy = false 105 | }; 106 | 107 | // Interfaces can be implemented by each enum constant individually, or 108 | // for the entire set. 109 | 110 | override val inseparable = true 111 | } 112 | 113 | fun main() { 114 | `with states`() 115 | `while driving`(GO) 116 | } 117 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_31_annotations/AnnotationsInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._31_annotations 2 | 3 | import kotlin.DeprecationLevel.WARNING 4 | import kotlin.annotation.AnnotationRetention.SOURCE 5 | import kotlin.annotation.AnnotationTarget.FUNCTION 6 | 7 | // We can use annotations in Kotlin just as we do in Java. 8 | 9 | @Suppress("unused") 10 | fun notUsed() = "YAGNI?" 11 | 12 | // Annotation arguments must be compile-time constants. 13 | 14 | const val unused = "unused" 15 | 16 | @Suppress(unused) 17 | fun alsoUnused() = "Dead code?" 18 | 19 | // Kotlin has an annotation for deprecation that is a bit more featured than 20 | // Java's. 21 | 22 | fun newSchool(why: String) = println(why) 23 | 24 | @Deprecated( 25 | message = "Seek alternate route.", 26 | replaceWith = ReplaceWith("newSchool(why)"), 27 | level = WARNING // or ERROR, HIDDEN 28 | ) 29 | fun oldSchool(why: String) = println(why) 30 | 31 | fun `with deprecation`() { 32 | 33 | val why = "You're my boy, Blue!" 34 | 35 | // IntelliJ offers a quick fix thanks to the `ReplaceWith`. 36 | 37 | oldSchool(why) 38 | } 39 | 40 | // New annotations are declared with `annotation class` instead of Java's 41 | // `@interface`. 42 | 43 | annotation class Great 44 | 45 | // Annotation parameters are declared as primary constructor properties. They 46 | // must be `val`. 47 | 48 | annotation class SuperGreat(val great: String) 49 | 50 | // As in Java, the allowed parameter types are limited: 51 | // - types that correspond to Java primitive types (Int, Long, etc.) 52 | // - strings 53 | // - classes (Foo::class) 54 | // - enums 55 | // - other annotations 56 | // - arrays of the types listed above 57 | 58 | // Annotation parameters can have default values, just like any primary 59 | // constructor parameter. 60 | 61 | annotation class InherentlyGreat(val great: String = "Great!") 62 | 63 | // Annotation classes can even take other annotations classes as parameters. 64 | 65 | annotation class MetaGreat(val great: InherentlyGreat) 66 | 67 | // When supplied as arguments, annotation classes are treated as regular 68 | // classes (i.e., no qualifying `@` is used). 69 | 70 | @MetaGreat(InherentlyGreat()) 71 | fun greatness() = "" 72 | 73 | // As with Java, if an annotation has a parameter named "value" the name can be 74 | // omitted when the parameter is used. 75 | 76 | annotation class Valuable(val value: String) 77 | 78 | @Valuable("This much") 79 | fun valuation() = "" 80 | 81 | // Annotations can have a `vararg` parameter. 82 | 83 | annotation class VarargAnnotation(vararg val value: String) 84 | 85 | @VarargAnnotation("One", "Two") 86 | fun varagAnnotations() = "" 87 | 88 | // Kotlin supports `[]` array literals for annotations. 89 | 90 | annotation class ArrayAnnotation(val value: Array) 91 | 92 | @ArrayAnnotation(["One", "Two"]) 93 | fun arrayAnnotations() = "" 94 | 95 | // As with Java, Kotlin has meta-annotations for controlling where and how 96 | // annotations are used. 97 | 98 | @Target(FUNCTION) // See KDoc for defaults 99 | @Retention(SOURCE) // or BINARY, RUNTIME (default) 100 | @Repeatable // `SOURCE` retention only 101 | @MustBeDocumented // Include in the KDoc for the element 102 | annotation class RedundantAnnotation 103 | 104 | @RedundantAnnotation 105 | @RedundantAnnotation 106 | fun test() = "" 107 | 108 | fun main() { 109 | `with deprecation`() 110 | } 111 | -------------------------------------------------------------------------------- /subprojects/typed-errors-in-kotlin/src/main/kotlin/org/sdkotlin/typederrors/raise/TypedErrorsWithRaise.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.typederrors.raise 2 | 3 | import arrow.core.Either 4 | import arrow.core.raise.Raise 5 | import arrow.core.raise.either 6 | import arrow.core.raise.ensure 7 | import arrow.core.raise.fold 8 | import arrow.core.raise.recover 9 | import org.sdkotlin.typederrors.Fruit 10 | import org.sdkotlin.typederrors.Fruit.Apple 11 | import org.sdkotlin.typederrors.Fruit.Banana 12 | import org.sdkotlin.typederrors.Fruit.Grapes 13 | import org.sdkotlin.typederrors.Fruit.Orange 14 | import org.sdkotlin.typederrors.FruitBasket 15 | import org.sdkotlin.typederrors.FruitBasketImpl 16 | import org.sdkotlin.typederrors.TypedError 17 | import org.sdkotlin.typederrors.TypedError.BadFruitTypedError.BadAppleTypedError 18 | import org.sdkotlin.typederrors.TypedError.BadFruitTypedError.RadioactiveBananaTypedError 19 | import org.sdkotlin.typederrors.TypedError.BadFruitTypedError.ShriveledGrapesTypedError 20 | 21 | class FruitBasketBuilder { 22 | 23 | private val fruit: MutableList = mutableListOf() 24 | 25 | context(Raise) 26 | fun addApple(apple: Apple): FruitBasketBuilder { 27 | 28 | ensure(!apple.hasWorm) { BadAppleTypedError } 29 | fruit.add(apple) 30 | return this 31 | } 32 | 33 | context(Raise) 34 | fun addBanana( 35 | banana: Banana, 36 | microSievertsLimit: Double, 37 | ): FruitBasketBuilder { 38 | 39 | ensure(banana.microSieverts <= microSievertsLimit) { 40 | RadioactiveBananaTypedError 41 | } 42 | fruit.add(banana) 43 | return this 44 | } 45 | 46 | context(Raise) 47 | fun addGrapes(grapes: Grapes): FruitBasketBuilder { 48 | 49 | ensure(!grapes.moreLikeRaisins) { ShriveledGrapesTypedError } 50 | fruit.add(grapes) 51 | return this 52 | } 53 | 54 | fun addOrange(orange: Orange): FruitBasketBuilder { 55 | 56 | fruit.add(orange) 57 | return this 58 | } 59 | 60 | fun build(): FruitBasket = FruitBasketImpl(fruit) 61 | } 62 | 63 | context(Raise) 64 | fun goGroceryShopping(): FruitBasket { 65 | 66 | val isTuesday = true 67 | val microSievertsLimit = 5.0 68 | 69 | val fruitBasketBuilder = FruitBasketBuilder() 70 | 71 | // Can chain. 72 | fruitBasketBuilder 73 | .addApple(Apple(hasWorm = false)) 74 | .addBanana(Banana(microSieverts = 1.1), microSievertsLimit) 75 | .addBanana(Banana(microSieverts = 2.2), microSievertsLimit) 76 | 77 | // Conditional adds via breaking the chain. 78 | if (isTuesday) { 79 | fruitBasketBuilder.addGrapes(Grapes(moreLikeRaisins = true)) 80 | } 81 | 82 | // Iterative adds via breaking the chain. 83 | repeat(3) { 84 | fruitBasketBuilder.addOrange(Orange) 85 | } 86 | 87 | return fruitBasketBuilder.build() 88 | } 89 | 90 | fun main() { 91 | 92 | val eitherResult: Either = 93 | either { 94 | goGroceryShopping() 95 | } 96 | 97 | eitherResult.fold( 98 | ifRight = { println("Shopping result: $it") }, 99 | ifLeft = { println("Shopping error: $it") } 100 | ) 101 | 102 | // Or ... 103 | 104 | when (eitherResult) { 105 | is Either.Right -> { 106 | println("Shopping result: $eitherResult") 107 | } 108 | is Either.Left -> { 109 | println("Shopping error: $eitherResult") 110 | } 111 | } 112 | 113 | // Or... 114 | 115 | recover( 116 | block = { println("Shopping result: ${goGroceryShopping()}") }, 117 | recover = { println("Shopping error: $it") } 118 | ) 119 | 120 | // Or... 121 | 122 | fold( 123 | block = { goGroceryShopping() }, 124 | recover = { println("Shopping error: $it") }, 125 | transform = { println("Shopping result: $it") } 126 | ) 127 | } 128 | -------------------------------------------------------------------------------- /detekt-rules/src/main/kotlin/org/sdkotlin/detekt/junit/InvalidTestFactoryReturnType.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.detekt.junit 2 | 3 | import io.gitlab.arturbosch.detekt.api.CodeSmell 4 | import io.gitlab.arturbosch.detekt.api.Config 5 | import io.gitlab.arturbosch.detekt.api.Debt 6 | import io.gitlab.arturbosch.detekt.api.Entity 7 | import io.gitlab.arturbosch.detekt.api.Issue 8 | import io.gitlab.arturbosch.detekt.api.Rule 9 | import io.gitlab.arturbosch.detekt.api.Severity 10 | import io.gitlab.arturbosch.detekt.api.internal.RequiresTypeResolution 11 | import org.jetbrains.kotlin.psi.KtNamedFunction 12 | import org.jetbrains.kotlin.resolve.BindingContext 13 | import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull 14 | import org.jetbrains.kotlin.types.KotlinType 15 | import org.jetbrains.kotlin.types.typeUtil.supertypes 16 | 17 | /** 18 | * A Detekt rule for ensuring 19 | * [JUnit 5 Dynamic Tests](https://junit.org/junit5/docs/current/user-guide/#writing-tests-dynamic-tests) 20 | * hava a valid inferred return type. 21 | */ 22 | @RequiresTypeResolution 23 | class InvalidTestFactoryReturnType(config: Config) : Rule(config) { 24 | 25 | override val issue = Issue( 26 | id = "InvalidTestFactoryReturnType", 27 | severity = Severity.Defect, 28 | description = "JUnit @TestFactory functions must return a valid type.", 29 | debt = Debt.Companion.FIVE_MINS 30 | ) 31 | 32 | override fun visitNamedFunction(function: KtNamedFunction) { 33 | super.visitNamedFunction(function) 34 | 35 | val isTestFactoryFunction = 36 | function.annotationEntries.any { it.text == "@TestFactory" } 37 | 38 | if (isTestFactoryFunction) { 39 | // Get the resolved return type of the function 40 | val returnType = function.resolveReturnType() 41 | 42 | if (returnType != null && !isValidTestFactoryReturnType(returnType)) { 43 | report( 44 | CodeSmell( 45 | issue, 46 | Entity.from(function), 47 | message = "JUnit @TestFactory functions must return " + 48 | "a valid type. Found: $returnType." 49 | ) 50 | ) 51 | } 52 | } 53 | } 54 | 55 | private fun KtNamedFunction.resolveReturnType(): KotlinType? { 56 | // Get the binding context and resolve the return type 57 | return bindingContext[BindingContext.FUNCTION, this]?.returnType 58 | } 59 | 60 | private fun isValidTestFactoryReturnType(type: KotlinType): Boolean { 61 | // Check if it's a subtype of DynamicNode (including DynamicTest and 62 | // DynamicContainer) 63 | if (type.isDynamicNode() 64 | || type.supertypes().any { it.isDynamicNode() } 65 | ) { 66 | return true 67 | } 68 | 69 | // Check if it's a container (e.g., Stream, Collection, Iterable, 70 | // Iterator, Array) of DynamicNode 71 | if (type.isDynamicNodeContainer()) { 72 | return true 73 | } 74 | 75 | return false 76 | } 77 | 78 | private fun KotlinType.isDynamicNode(): Boolean { 79 | val fqName = 80 | this.constructor.declarationDescriptor?.fqNameOrNull()?.asString() 81 | return fqName == "org.junit.jupiter.api.DynamicNode" 82 | } 83 | 84 | private fun KotlinType.isDynamicNodeContainer(): Boolean { 85 | val fqName = 86 | this.constructor.declarationDescriptor?.fqNameOrNull()?.asString() 87 | val isContainerType = 88 | fqName in listOf( 89 | "kotlin.Array", 90 | "kotlin.collections.Collection", 91 | "kotlin.collections.List", 92 | "kotlin.collections.Set", 93 | "kotlin.collections.Iterable", 94 | "kotlin.collections.Iterator", 95 | "java.util.stream.Stream", 96 | ) 97 | 98 | return isContainerType && arguments.any { arg -> 99 | arg.type.isDynamicNode() 100 | || arg.type.supertypes().any { it.isDynamicNode() } 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /subprojects/effective-kotlin/src/main/kotlin/org/sdkotlin/meetup/effectivejava/item1/ConsiderStaticFactoryMethods.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.meetup.effectivejava.item1 2 | 3 | import java.nio.charset.Charset 4 | import java.time.Month.FEBRUARY 5 | import java.time.Month.JANUARY 6 | import java.time.Month.MARCH 7 | import java.util.SortedMap 8 | import java.util.TreeMap 9 | 10 | /* Effective Java 11 | Item 1: Consider static factory methods instead of constructors 12 | - One advantage of static factory methods is that, unlike constructors, they 13 | have names. 14 | - A second advantage of static factory methods is that, unlike constructors, 15 | they are not required to create a new object each time they’re invoked. 16 | - A third advantage of static factory methods is that, unlike constructors, 17 | they can return an object of any subtype of their return type. 18 | - A fourth advantage of static factories is that the class of the returned 19 | object can vary from call to call as a function of the input parameters. 20 | - A fifth advantage of static factories is that the class of the returned 21 | object need not exist when the class containing the method is written. 22 | */ 23 | 24 | // Kotlin companion object functions, companion extension functions, and 25 | // top-level functions can be used as alternatives to Java's static factory 26 | // methods. 27 | 28 | fun `with Java static factory functions`() { 29 | 30 | val charset = Charset.forName("ASCII") 31 | 32 | println("Java charset is \"${charset.name()}\"") 33 | } 34 | 35 | // In Kotlin a companion object function simulates a Java static function. 36 | 37 | abstract class AlienCharset { 38 | companion object { 39 | fun forName(charsetName: String) = when (charsetName) { 40 | "Klingon" -> KlingonCharset() 41 | "Alienese" -> AlieneseCharset() 42 | else -> throw IllegalArgumentException("Unknown character set") 43 | } 44 | } 45 | 46 | abstract fun name(): String 47 | } 48 | 49 | class KlingonCharset : AlienCharset() { 50 | override fun name() = "Klingon" 51 | } 52 | 53 | class AlieneseCharset : AlienCharset() { 54 | override fun name() = "Alienese" 55 | } 56 | 57 | fun `with Kotlin companion object factory functions`() { 58 | 59 | val alienCharset = AlienCharset.forName("Klingon") 60 | 61 | println("Alien charset is \"${alienCharset.name()}\"") 62 | } 63 | 64 | // We can add such a static factory as a companion object extension function. 65 | 66 | class Groot { 67 | override fun toString() = "I am Groot" 68 | } 69 | 70 | fun String.Companion.valueOf(groot: Groot) = groot.toString() 71 | 72 | fun `with Kotlin companion object extension factory functions`() { 73 | 74 | val grootSays = String.valueOf(Groot()) 75 | 76 | println("Groot says, \"$grootSays\"") 77 | } 78 | 79 | object Grooter { 80 | 81 | fun valueOf(groot: Groot): String = groot.toString() 82 | } 83 | 84 | fun `with Kotlin object factory functions`() { 85 | 86 | val grootSays = Grooter.valueOf(Groot()) 87 | 88 | println("Groot says, \"$grootSays\"") 89 | } 90 | 91 | // We can also create top-level factory functions. 92 | 93 | fun sortedMapOf(vararg pairs: Pair): SortedMap = 94 | TreeMap().apply { putAll(pairs) } 95 | 96 | fun `with Kotlin top-level factory functions`() { 97 | 98 | val months = sortedMapOf(2 to FEBRUARY, 1 to JANUARY, 3 to MARCH) 99 | 100 | println("The months in order are $months") 101 | } 102 | 103 | fun main() { 104 | 105 | `with Java static factory functions`() 106 | `with Kotlin companion object factory functions`() 107 | `with Kotlin companion object extension factory functions`() 108 | `with Kotlin object factory functions`() 109 | `with Kotlin top-level factory functions`() 110 | } 111 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_26_collections/CollectionsInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._26_collections 2 | 3 | import java.util.LinkedList 4 | import java.util.TreeMap 5 | import java.util.TreeSet 6 | 7 | fun main() { 8 | 9 | // As with arrays, collections in Kotlin are created with factory functions. 10 | 11 | val listOfInts: List = listOf(1, 2, 3) 12 | 13 | val otherList: List = List(3) { index -> index + 1 } 14 | 15 | println(otherList) 16 | 17 | val setOfInts = setOf(1, 2, 3) 18 | 19 | // The map factory function takes `Pair`s, which can be constructed with 20 | // the handy infix function `to`. 21 | 22 | val mapOfIntsToStrings = mapOf(1 to "1", 2 to "2", 3 to "3") 23 | 24 | // Collections support the index operator as with arrays. 25 | 26 | println("listOfInts.get(0): ${listOfInts.get(0)}") 27 | println("listOfInts[0]: ${listOfInts[0]}") 28 | 29 | // Except collections are immutable by default in Kotlin. 30 | 31 | //listOfInts.set(0, 100) // Does not compile. 32 | //listOfInts[0] = 100 33 | 34 | // There are factory functions for mutable collections. 35 | 36 | val mutableListOfInts: MutableList = mutableListOf(1, 2, 3) 37 | 38 | mutableListOfInts.set(0, 100) 39 | mutableListOfInts[0] = 100 40 | 41 | // You can also get a mutable copy of an immutable collection. 42 | 43 | val mutableCopiedListOfInts = listOfInts.toMutableList() 44 | val secondListOfInts = mutableCopiedListOfInts.toList() 45 | 46 | // For lists there is a constructor that takes a value factory similar to 47 | // that for Array. 48 | 49 | val listOfStrings = List(10) { index -> index.toString() } 50 | 51 | // You may wonder how List, being an interface, could have a constructor. 52 | // In a bit of DSL trickery, it's really just a top-level function in the 53 | // implicitly imported `kotlin.collections` package with a capitalized name. 54 | 55 | // There is a mutable equivalent. 56 | 57 | val mutableListOfStrings = MutableList(10) { index -> index.toString() } 58 | 59 | // If there is a concern with the short-lived `Pair` creation required for 60 | // `mapOf(...)`, we can create a mutable map, initialize the keys and 61 | // values directly, and then get an immutable reference to it. 62 | 63 | val fastMap = mutableMapOf().also { 64 | it[1] = "1" 65 | it[2] = "2" 66 | it[3] = "3" 67 | }.toMap() 68 | 69 | // There are factory functions for common collection implementations. 70 | 71 | // The `to[Type]()` methods can be used to get the generic immutable 72 | // references for them. 73 | 74 | val arrayListOfInts = arrayListOf(1, 2, 3).toList() 75 | 76 | val hashSetOfInts = hashSetOf(1, 2, 3).toSet() 77 | 78 | val hashMapOfIntsToStrings = hashMapOf(1 to "1", 2 to "2", 3 to "3").toMap() 79 | 80 | // If a factory function doesn't exist in the standard library for the 81 | // collection implementation you need, it's straightforward to add one. 82 | 83 | fun linkedListOf(vararg items: T) = LinkedList().apply { 84 | addAll(items) 85 | } 86 | 87 | fun treeSetOf(vararg items: T) = TreeSet().apply { 88 | addAll(items) 89 | } 90 | 91 | fun treeMapOf(vararg items: Pair) = TreeMap().apply { 92 | putAll(items) 93 | } 94 | 95 | // Iterating collections is as with arrays, and there is a rich set of APIs 96 | // for accessing, mutating, ordering, searching, transforming, filtering, 97 | // grouping, aggregating, and stream processing them. There are also 98 | // collection type specific APIs like `union`, `intersect`, and `subtract` 99 | // for sets. 100 | 101 | // See the Kotlin reference for a thorough overview: 102 | // https://kotlinlang.org/docs/reference/collections-overview.html 103 | } 104 | -------------------------------------------------------------------------------- /subprojects/kotlin-for-java-devs/src/main/kotlin/org/sdkotlin/intro/kotlin/_08_2_tailrecursivefunctions/TailRecursionInKotlin.kt: -------------------------------------------------------------------------------- 1 | package org.sdkotlin.intro.kotlin._08_2_tailrecursivefunctions 2 | 3 | // Kotlin supports tail call elimination. 4 | 5 | // Take this standard recursive implementation for the 6 | // [triangle number](https://en.wikipedia.org/wiki/Triangular_number) of 'n': 7 | 8 | fun triangleNumber(n: Int): Int = 9 | if (n == 0) 0 10 | else n + triangleNumber(n - 1) 11 | 12 | // Simple, but it will throw a stack overflow error if called with too big a 13 | // number. 14 | 15 | // We could fall back on an iterative implementation. 16 | 17 | fun iterativeTriangleNumber(n: Int): Int { 18 | var accumulator = 0 19 | for (i in 1..n) { 20 | accumulator += i 21 | } 22 | return accumulator 23 | } 24 | 25 | // We had to introduce a mutable variable for this. Even though it's a local 26 | // variable, that's still considered bad form in functional programming. 27 | 28 | // Kotlin offers us another option that is both stack efficient and functional 29 | // programming friendly. If we can write the recursion such that the return is 30 | // calculated from a call to the function itself and nothing else, called 31 | // "tail recursion", then the compiler can rewrite the recursion as a loop for 32 | // us (tail call elimination). 33 | 34 | tailrec fun tailTriangleNumber(i: Int, accumulator: Int = 0): Int = 35 | if (i <= 0) accumulator 36 | else tailTriangleNumber(i - 1, i + accumulator) 37 | 38 | // The 'tailrec' keyword is not required for the compiler to do tail call 39 | // optimization, but it is useful in that it will produce a warning if the 40 | // function can't be tail call optimized, now or in the future. 41 | 42 | // Here is a head-recursive implementation with an accumulator. It should stack 43 | // overflow as is, but you can add 'tailrec' to it without a warning because 44 | // the compiler can invert the if block, and then it can be tail call optimized. 45 | 46 | //tailrec 47 | fun headTriangleNumber(n: Int, accumulator: Int = 0): Int = 48 | if (n > 0) headTriangleNumber(n - 1, accumulator + n) 49 | else accumulator 50 | 51 | // Note that in the original implementation the recursive call was not fully in 52 | // tail position because control had to be returned back up the stack for the 53 | // 'n * ...' calculation. The accumulator moves the calculation to before the 54 | // recursive call. 55 | 56 | // The required 'accumulator' parameter defaulted to 1 has dirtied up our 57 | // public API for this function. It's an implementation detail, and the caller 58 | // is never intended to specify any value for it other than 1. 59 | // Kotlin supports local functions, i.e. functions defined inside other 60 | // functions, which allows us to clean this up. 61 | 62 | fun cleanTailTriangleNumber(n: Int): Int { 63 | tailrec fun recurse(i: Int, accumulator: Int = 0): Int = 64 | if (i <= 0) accumulator 65 | else recurse(i - 1, i + accumulator) 66 | return recurse(n) 67 | } 68 | 69 | fun getStackDepth(i: Int = 0): Int = 70 | try { 71 | getStackDepth(i + 1) 72 | } catch (e: StackOverflowError) { 73 | i 74 | } 75 | 76 | fun main() { 77 | println("T5: ${triangleNumber(5)}") 78 | val stackDepth = 79 | getStackDepth() 80 | println("Stack depth: $stackDepth") 81 | // TODO: Why does stack depth change and why does this not succeed? 82 | //println("Recursive T${stackDepth - 1}: ${triangleNumber(stackDepth - 1)}") 83 | try { 84 | println("Recursive T$stackDepth: ${triangleNumber(stackDepth)}") 85 | } catch (e: StackOverflowError) { 86 | println("Stack overflow error with recursive T$stackDepth!") 87 | } 88 | println("Iterative T$stackDepth: ${iterativeTriangleNumber(stackDepth)}") 89 | try { 90 | println("Head recursive T$stackDepth: ${headTriangleNumber(stackDepth)}") 91 | } catch (e: StackOverflowError) { 92 | println("Stack overflow error with head call T$stackDepth!") 93 | } 94 | println("Tail recursive T$stackDepth: ${tailTriangleNumber(stackDepth)}") 95 | } 96 | --------------------------------------------------------------------------------