Skip to content

Instantly share code, notes, and snippets.

@dschinkel
Last active January 21, 2023 13:58
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dschinkel/ecdc8c368202a04ab9bac12b27a05b72 to your computer and use it in GitHub Desktop.
Save dschinkel/ecdc8c368202a04ab9bac12b27a05b72 to your computer and use it in GitHub Desktop.
Kotlin Recipes / Example Snippets
/*
MUTABLE MAPS
------------
Note: Its type is inferred by underlying values within
*/
val toRoman = mutableMapOf(
"IIIII" to "V",
"VV" to "X"
)
// get a value: "VV" returns "X"
toRoman["VV"]
/*
MUTABLE LISTS
-------------
*/
private val defaultSkunkWorks : MutableList<SkunkWork> = listOf(
"Coffee Machine",
"XBox",
"Ping Pong Table",
)
/*
MAPS
Map over each item in the list and create a new SkunkWork model with title populated,
then convert it back to a mutable list so we can modify it later
*/
val skunkwork = defaultSkunkWorks.map{ SkunkWork(it) }.toMutableList()
// define a function that returns one
fun findAllSkunkWork(): MutableList<SkunkWork> {
return skunkworkRepository.findAllSkunkWorks()
}
// set a variable to a mutable list returned by a function
val skunkWorks: MutableList<SkunkWork> = findAllSkunkWorks()
// Filtering
val originalMap = mapOf("key1" to 1, "key2" to 2, "something_else" to 3)
val filteredMap = originalMap.filterKeys { it.contains("key") }
// Iterating
items.forEach {
k, v ->
println("$k = $v")
}
for ((k, v) in items) {
println("$k = $v")
}
import kotlinx.coroutines.CompletableDeferred
class BluetoothDiscotech(private val bluetoothInterface: BluetoothInterface) {
suspend fun discoverize() = beginDiscovery()
.run { collectDevices() }
private fun beginDiscovery() = if (bluetoothInterface.isEnabled) {
bluetoothInterface.startDiscovery()
} else {
bluetoothInterface.enable()
bluetoothInterface.bluetoothEnabled + ::onBluetoothEnabled
}
private suspend fun collectDevices() = mutableListOf<BloothDevice>()
.also { devices ->
bluetoothInterface.deviceDiscovered + { devices += it }
waitForDiscoveryToFinish()
}
private suspend fun waitForDiscoveryToFinish() = CompletableDeferred<Unit>()
.apply { bluetoothInterface.discoveryEnded + { complete(Unit) } }
.await()
private fun onBluetoothEnabled(unit: Unit) {
bluetoothInterface.startDiscovery()
bluetoothInterface.bluetoothEnabled - ::onBluetoothEnabled
}
}
/*
DATA CLASSES (Data Models)
------------
Kotlin's way of creating a custom data type
(Data Model / DTO / Class with just Data, POKO, no Behavior / Dumb Data Object, whatever the fuck you wanna call it)
So, when you create one, note that public props can be defined right in the initialization.
- In the example below, title and note become public properties of a SkunkWork Model
*/
/*
Note: The compiler automatically derives the following members from all properties declared in the primary constructor:
equals()/hashCode() pair;
toString() of the form "User(name=John, age=42)";
componentN() functions corresponding to the properties in their order of declaration;
copy() function
*/
data class SkunkWork(val title: String, val note: String? = null)
/*
NULL
----
In Kotlin, the type system distinguishes between references that can hold null (nullable references)
and those that can not (non-null references)
/*
/*
SAFE CALLS
----------
Safe calls is one way in Kotlin to check for null for mutable properties, variables, etc.
If you have declared a variable with val, it's immutable so no need for a safe call like this
Instead of checking with an if statement if a prop or variable is null before trying to use it, you can
instead use the safe call shorthand which is nicer.
Safe calls are also useful in chains.
*/
// Such a chain returns null if any of the properties in it is null
val departmentHead = bob?.department?.head?.name
// STRING TEMPLATES - mix strings with code
val inputOne = "Dave"
val inputTwo = "Schinkel"
val fullName = "$inputOne $inputTwo" // Dave Schinkel
// ELVIS OPERATOR
return toRoman["$inputOne$inputTwo"] ?: "$inputOne$inputTwo"
// NOT NULL ASSERTION OPERATOR
val companyLength = companyName!!.length
/*
FUNCTIONS are first class citizens;
That means they are actual types in Kotli,
which means you can pass them into functions as params,
assign them to variables, etc.
*/
// BASIC FUNCTION, with nullable string return type
fun add(inputOne: String, inputTwo: String): String? {
}
fun comparePermission(selfPermission: Int) =
selfPermission == PackageManager.PERMISSION_GRANTED
// function that takes no arguments and returns a SkunkWork
()->SkunkWork
// function that takes a name and returns a SkunkWork
(name)->SkunkWork
// function that returns another function that returns a SkunkWork
()->()->SkunkWork
// assigning a function to a variable
val getSkunkWork: ()->SkunkWork
// assigning a named function to a variable
fun printSkunkWork(skunkWork: SkunkWork) {
println(skunkwork.name)
}
val skunkWorkName = ::printSkunkWork
/*
FUNCTION LITERALS
There are two kinds in Kotlin:
- Lambda expression
- Anonymous function
- an alternative way to define a function, similar to a lambda;
the big difference is that anonymous functions are more explicit
- It is more clear when we are using them, and return value needs to be specified explicitly
*/
/*
LAMBDAS
- a short way to define a function
- very similar to Java lambdas
- everything is optional except the code body
- the type of the last command within a lambda block is the returned type
*/
val lambdaName : Type = { argumentList -> codeBody }
val square = { number: Int -> number * number }
// lambda that takes an int argument and returns a string
val magnitude100String = { input : Int ->
val magnitude = input * 100
magnitude.toString()
}
// lambda, where argument type is inferred to be type SkunkWork.
// ()->String is just describes the type of variable which is
// describing the lamba function type itself (note: this is optional)
// { println(skunkwork.name) } is the actual lambda
val skunkWorkName: ()->String = { println(skunkwork.name) }
// lambda, where lack of argument type is inferred to be type SkunkWork by what the function returns
// (Int)->String is just describes the type of variable, which is
// just describing the lamba function type itself (not required)
val skunkWorkName: (Int)->String = { id -> println(skunkwork.name) }
/*
SWAP
----
swapping between two variables
*/
// easy way
val inputOne = "AB"
val inputTwo = "CD"
inputOne = inputTwo.also { inputTwo = inputOne }
// manual way, not thread safe
fun swap(pair: MutablePair) {
val temp = pair.one
pair.one = pair.two
pair.two = temp
}
// get last char
inputOne.takeLast(1) // B
/*
EQUALITY
.equals checks not only for structural equality but ALSO reference equality
== or !=
- only checks for structural equality
- an expression like a == b is translated to: a?.equals(b) ?: (b === null)
- if a is not null, it calls the equals(Any?) function,
otherwise (i.e. a is null) it checks that b is referentially equal to null.
*/
!skunkWorksTitle.equals("")
// Find a String
inputOne.contains(inputTwo);
inputOne in inputTwo
/*
kotlin.test framework
Using the kotlin.test which wraps JUnit5 under the hood,
but has some nicer syntax in its test framework
*/
// basic build.gradle.kt to get you up and running
plugins {
id("org.jetbrains.kotlin.jvm").version("1.3.21")
}
repositories {
jcenter()
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
}
// RomanNumeralCalculatorTest.kt
import kotlin.test.Test
import kotlin.test.*
class RomanNumeralCalculatorTest {
// using backticks (a Kotlin feature, preferred)
@Test
internal fun `one plus one is two`() {
val total = Calculator.add("I", "I")
assertEquals("II", total)
}
// using traditional Java camelcase (harder to read especially as test names get long)
@Test
internal fun onePlusOneIsTwo() {
val total = Calculator.add("I", "I")
assertEquals("II", total)
}
}
// Kotlin classes can be declared either as mutable using the var keyword, or as read-only using the val keyword
// Example Class with data hiding (size)
class SkunkWork(val skunkworkRepo: SkunkWorkRepository) {
fun add(title: String) {
skunkworkRepo.addSkunkWork(SkunkWork(title))
}
fun size(): Int {
return skunkworkRepo.findAllSkunkWork().size
}
}
// INTERFACE
interface BluetoothInterface {
}
// ENUM
enum class CameraState {
Ready,
MustRequest,
NoCamera
}
enum class DeviceType(override val value: Int, val displayName: String) : IntEnum {
Unknown(0, "Unknown"),
Classic(1, "Classic"),
LE(2, "Low Energy"),
Dual(3, "Dual");
companion object {
fun fromInt(value: Int): DeviceType =
IntEnum.fromInt(value) ?: DeviceType.Unknown
}
}
// CASTING - unsafe cast - will blow up if cast is not possible
val skunkWork = skunkworkRepository.findById(skunkwork.id) as SkunkWork
// CASTING - safe cast
val skunkWork: String? = skunkworkRepository.findById(skunkwork.id) as? SkunkWork
// mutable
var
/*
val
---
- declares final, read only, reference
- cannot be reassigned (you can't change its reference)
- method parameters are implicitly declared as final val, so you cannot reassign them just like you could in Java
*/
val
// SERIALIZABLE CLASS
// https://proandroiddev.com/kotlinx-serialization-83815cc1c57b - more here on it
data class BloothDevice(
val name: String?,
val macAddress: String,
val type: DeviceType,
val majorClass: BluetoothMajorClass?,
val minorClass: BluetoothMinorClass?,
val services: List<BluetoothServiceClass>
) : Serializable
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment