Kotlin'de uzantılar dediğimiz extensions temel kodlamamızı azaltan sınıflar yada compenent dediğimiz yapılardır. 
Şimdi Kotlin geliştiriciler için işinize yarayacak ve kod yapınızı azaltacak 5 farklı kullanışlı uzantılara hep birlikte bakalım. 

1- IsNull

Normal bir şekilde, boş atanabilirliği bu şekilde kontrol ediyoruz.

if(somthing == null){}
or 
if(something != null){}

Kotlin uzantılarını kullanarak bunu daha anlaşılır hale getirelim.

fun Any?.isNull() = this == null

fun Any?.isNotNull() = !isNull()

Yani kodumuz böyle olacak,

if(something.isNull()){}
or 
if(something.isNotNull()){}

2- Price Amount

Burada bir dizeyi ondalık biçime biçimlendirmek için bir uzantı da var, bu uzantıyı aşırı yükleyebilir ve Double veya Long ile kullanabiliriz.

Diyelim ki bir E-ticaret uygulamamız var ve her ürünün kendi fiyatı var, bu yüzden bu uzantılar fiyatı istediğimiz bir kalıba göre biçimlendirmemize yardımcı olacak.

fun String.toPriceAmount(): String {
    val dec = DecimalFormat("###,###,###.00")
    return dec.format(this.toDouble())
}

fun Double.toPriceAmount(): String {
    val dec = DecimalFormat("###,###,###.00")
    return dec.format(this)
}

Kullanımı ise; 

println("11".toPriceAmount())
println("05".toPriceAmount())
println(12.0.toPriceAmount())

Çıktısı ise; 

11.00
5.00
12.00

3- Date Format

Yaptığımız şey, Tarih Formatı için bir utils sınıfı oluşturmaktı, insanlar bu konuda benimle tartışabilir ancak IMHO, Tarih Formatı için bir uzantı yapmak kodu daha temiz hale getirecektir.

Diyelim ki bir not alma uygulamamız var, böylece her notun yapıldığı bir tarihi de gösteriyor, bu tarihi UnixTime'a dönüştürebilir ve veritabanında saklayabiliriz.

private const val TIME_STAMP_FORMAT = "EEEE, MMMM d, yyyy - hh:mm:ss a"
private const val DATE_FORMAT = "yyyy-MM-dd"

fun Long.getTimeStamp(): String {
    val date = Date(this)
    val simpleDateFormat = SimpleDateFormat(TIME_STAMP_FORMAT, Locale.getDefault())
    simpleDateFormat.timeZone = TimeZone.getDefault()
    return simpleDateFormat.format(date)
}

fun Long.getYearMonthDay(): String {
    val date = Date(this)
    val simpleDateFormat = SimpleDateFormat(DATE_FORMAT, Locale.getDefault())
    simpleDateFormat.timeZone = TimeZone.getDefault()
    return simpleDateFormat.format(date)
}

@Throws(ParseException::class)
fun String.getDateUnixTime(): Long {
    try {
        val simpleDateFormat = SimpleDateFormat(DATE_FORMAT, Locale.getDefault())
        simpleDateFormat.timeZone = TimeZone.getDefault()
        return simpleDateFormat.parse(this)!!.time
    } catch (e: ParseException) {
        e.printStackTrace()
    }
    throw ParseException("Please Enter a valid date", 0)
}

Kullanımı ise; 

val currentTime = System.currentTimeMillis()
println(currentTime.getTimeStamp())
println(currentTime.getYearMonthDay())
println("2020-09-20".getDateUnixTime())

Çıktısı; 

Sunday, September 20, 2020 - 10:48:26 AM
2020-09-20
1600549200000

4- ObjectSerializer

Aslında bu benim en sevdiğim uzantılardan biri çünkü Kotlin uzantılarının gücünü gösteriyor.

Diyelim ki bir flashcards uygulamamız var, bu yüzden kartları bir arkadaşa göndermek veya daha sonra geri yüklemek için dışa aktarmamız gerekiyor, bu ObjectSerializer bilgi kartlarını String veya ByteArray olarak serileştirmemize yardımcı olacak, böylece onları bir dosyada depolayabilir ve geri yükleyebiliriz onları daha sonra seriyi kaldırma uzantısını kullanarak.

Aşağıdaki kodda, her sınıfın Serializable uygulamasını serileştirebildiğimizi görebiliriz, özellikle bu durumla ilgili değil ama daha büyük bir şeye uygularsak bunun bize nasıl yardımcı olacağını düşünebilirsiniz.

@Throws(IOException::class)
    fun Serializable.serialize(): String {
        val serialObj = ByteArrayOutputStream()
        val objStream = ObjectOutputStream(serialObj)
        objStream.writeObject(this)
        objStream.close()
        return encodeBytes(serialObj.toByteArray())
    }

    @Throws(IOException::class, ClassNotFoundException::class)
    fun String?.deserialize(): Any? {
        if (this.isNullOrEmpty()) return null
        val serialObj = ByteArrayInputStream(decodeBytes(this))
        val objStream = ObjectInputStream(serialObj)
        return objStream.readObject()
    }

Kullanımı : 

val kotlinExtention = "Kotlin Extensions ObjectSerializer"
val serializedString = kotlinExtention.serialize()
println(serializedString)
println(serializedString.deserialize() as String)

Çıktısı : 

kmonaaafheaaccelgphegmgjgocaefhihegfgohdgjgpgohdcaepgcgkgfgdhefdgfhcgjgbgmgjhkgfhc
Kotlin Extensions ObjectSerializer

Dosyanın tam sürümünü burada bulabilirsiniz:

import java.io.*
import kotlin.experimental.and

@Throws(IOException::class)
fun Serializable.serialize(): String {
    val serialObj = ByteArrayOutputStream()
    val objStream = ObjectOutputStream(serialObj)
    objStream.writeObject(this)
    objStream.close()
    return encodeBytes(serialObj.toByteArray())
}

@Throws(IOException::class, ClassNotFoundException::class)
fun String?.deserialize(): Any? {
    if (this.isNullOrEmpty()) return null
    val serialObj = ByteArrayInputStream(decodeBytes(this))
    val objStream = ObjectInputStream(serialObj)
    return objStream.readObject()
}

private fun encodeBytes(bytes: ByteArray): String {
    val strBuf = StringBuffer()
    for (i in bytes.indices) {
        strBuf.append(((bytes[i].toInt() shr 4 and 0xF) + 'a'.toInt()).toChar())
        strBuf.append(((bytes[i] and 0xF) + 'a'.toInt()).toChar())
    }
    return strBuf.toString()
}

private fun decodeBytes(str: String): ByteArray {
    val bytes = ByteArray(str.length / 2)
    var i = 0
    while (i < str.length) {
        var c = str[i]
        bytes[i / 2] = (c - 'a' shl 4).toByte()
        c = str[i + 1]
        bytes[i / 2] = bytes[i / 2].plus(((c - 'a'))).toByte()
        i += 2
    }
    return bytes
}

5- Generic Extension Functions

Bunda, jenerik bir tür alan bir uzantı oluşturarak, onu her sınıf için uygulayabilir veya o jenerik uygularken, jenerik gücüne sahip olacağız.

Bu, üçüncü taraf bir kitaplığa veya bir API'ye düzenleme yapmak istediğimizde çok kullanışlıdır, bu nedenle bu sınıfı miras almak yerine, bu standart kodu azaltmamıza yardımcı olacak uzantıları kullanabiliriz.

Bu nedenle, bir uygulamamız varsa ve adların listesini AutoCompleteTextView Bağdaştırıcısına ayarlamak için bu uzantıyı kullanan bir şeyin adını aramamız gerekiyorsa veya hatta bir RandomNames seçtikten sonra ad listesini kopyalamamız gerekse bile, böylece bu, elde etmemize yardımcı olacaktır. Listeyi adlandırın ve bir panoda saklayın.

Aşağıdaki örnekte, Kullanıcıyı genişleten bir Öğrenci sınıfı oluşturdum ve uzantımız bir ad listesi yazdırıyor.

abstract class User(open var id: Int, open var name: String)

data class Student(override var id: Int, override var name: String) : User(id, name)

fun <T : User> List<T>.getUserNameList(): List<String> {
    return this.map { it.name }
}

Kullanımı : 

val listOfUsers = mutableListOf<Student>()
listOfUsers.add(Student(1, "Kotlin"))
listOfUsers.add(Student(2, "Ahmet"))
listOfUsers.add(Student(3, "Mim"))

print(listOfUsers.getUserNameList())

Çıktı : 

[Kotlin, Ahmet, Mim]

Okuduğunuz için teşekkür ederim ve umarım beğenmişsinizdir ve bazılarını uygulamalarınızda kullanırsınız.