Kotlin Temelleri-3

Armagan Civelek
6 min readJan 11, 2021

--

Road Map

  1. BASİC

2.CONTROL FLOW

3.FUNCTİONS

  • Functions
  • Lambdas
  • Higher Order Functions

4.KOTLİN OOP

5.COLLECTİONS

6.EXCEPTİON HANDLİNG

3) FUNCTIONS

A) Functions

Kotlinde fonksiyonlar “fun” anahtar kelimesi ile tanımlanır ve aşağıda gösterildiği gibi çağrılır.

//Fonksiyon oluşturuldu.
fun callMe(){
println("Hello")
}
fun main(args : Array<String>){

//Fonksiyon çağrılıyor
callMe()

}
output:
Hello

Function prototip:

fun function_name(param1: data_type, param2: data_type, ...): return_type

Function with argument:

Default Argument:

Fonksiyon parametrelerine default değer geçmek Java’nın aksine kotlinde vardır.

3 parametre alan bu fonksiyon, parametre verilmeden çağırılabilir.

fun newStudent(name:String="student",age:Int=18,gender:String="male")
{
println("$name , $age,$gender")
}
fun main(args: Array<String>)
{
newUser()
}
output:
student , 18,male

İstenilirse sadece seçilen parametrelere değer geçilebilir.

fun main(args: Array<String>)
{
newUser("Armagan Civelek")
}
output:
Armagan Civelek , 18 , male

Name Argument:

Eğer bir parametre değer verilmeden geçilirse bu parametreden sonra gelen tüm parametreleri isimlendirmemiz gerekir. Ne demek istediğimizi aşağıdaki kod bloğuyla daha iyi anlayacağız.

fun main(args: Array<String>)
{
newStudent
("Armagan Civelek",gender="male")
}

Yukarıda ki kod bloğunda newStudent() fonksiyonu çağrılırken “age” parametresi atlanarak “gender” parametresine değer verilmek istenmiştir. Bu gibi durumlarda bunu IDE’ye açıkca belirtmemiz gerekir.

Single-expression functions:

Daha önce “if” kullanırken tek satırlık kullanımlarda süslü parantezleri kullanmadığımızı görmüştük.

Kotlinde fonksiyonlarımız tek satırdan oluşuyorsa bu yapıyı kullanabiliriz.

fun getAnswer()="success"// tek satırlık kullanımlarda kullanırız

Variable number of arguments (Varargs):

Bazen fonksiyona parametre olarak vereceğimiz değişkenin sayısını bilemeyiz. Bu gibi durumlarda yardımımıza “vararg” yapısı koşar.

Bir fonksiyona sadece bir tane “vararg” verebiliriz.

fun getAverage(vararg input: Int): Float {
var sum = 0.0f
for (item in input) {
sum += item
}
return (sum / input.size)
}

val result1 = getAverage(1, 2, 3)
val result2 = getAverage(1, 2, 3, 4, 5)
val result3 = getAverage(1, 2, 3, 4, 5, 6, 7, ....)

Farklı tipte parametreler için, vararg tipi Any olabilir veya genericler kullanılabilir.

fun<T> getUserInfo(vararg  userInfo: T, key:Int)
{

}

Vararg değişkenine göndereceğimiz değeri dizi olarak göndermek istediğimizde; arrayOf’un başına spread(*) operatörü koyulması gerekir.

getUserInfo(*arrayOf("armagan","civelek"),key=4) 

Vararg dan sonra parametre vereceksek yukarıda gözüktüğü gibi name argument kullanılması gerekir. Çünkü derleyici gönderdiğimiz değerlerin hangisinin vararg hangisinin diğer parametre için olduğunu ayırt edemez.

Infix functions:

Okunurluğu arttıran bir fonksiyon yazım biçimdir.

Bir fonksiyonun infix fonksiyon olabilmesi için bazı kurallar vardır :

  • İnfix fonksiyonlar üye fonksiyon(method) ya da extension fonksiyon olmalıdır.
  • Sadece tek bir parametre alabilirler.
  • Bu parametre vararg ve default bir değer olamaz.

Extension fonksiyon ile kullanımı :

infix fun Int.add(x:Int):Int
{
return (this+x)

}
fun main(args: Array<String>)
{
val returnValue= 4 add 5// 4.add(5) bu iki çağrım aynı manadadır.
println(returnValue)
output:
9
}

İnfix fonksiyonlar, aritmetik operatörler, cast işlemleri ve rangeTo operatörüne göre işlem önceliği olarak daha geridir.

  • Yukarıda yazdığımız “add” infix fonksiyonunu baz alacağız.
1 add 4+5 --->  1 add(4 + 5)1  until n * 2  ---> 1until (n * 2)

Diğer taraftan mantıksal işlemlere göre işlem önceliğine sahiptir.

Üye fonksiyon olarak kullanımı:

Class içerisinde kullandığımız fonksiyonlara method deriz.

class ExampleClass()
{

infix fun dowloandImage(imageUrl:String) // en az bir parametre alması gerek.
{

}

fun log()
{
this dowloandImage "www.google.com.tr"
dowloandImage ("www.google.com.tr") // bu çağrımda doğrudur.
dowloandImage "www.google.com.tr" // bu kullanım ise yanlıştır.
}
}

Eğer class dışında infix method çağrılacaksa instance oluşturmak gerekir ve this kullanılmaz.

 val  exampleClassinstance = ExampleClass()
exampleClassinstance dowloandImage "www.google.com.tr"

Function Scope:

Kotlinde fonksiyonlar class bağımlı olmadan, kotlin dosyası içerisinde oluşturulabilir(Top level function). Aynı zamanda local fonksiyonlarda tanımlanabilir. Diğer dillerden aşina olduğumuz sınıf fonksiyonlarını da tanımlayabiliriz.

Top level function:

fun topLevelFunction(){
print("kotlin")
}

Member function:

Method olarak adlandırılır.

class ExampleClass{
fun memberFunction(x: Int) { }
}

Local function:

Kotlinde fonksiyon içerisinde bir diğer fonksiyonu tanımlayabiliriz. Bu lokal fonksiyonlar çağrıldıkları yerden önce tanımlanmalıdırlar. Tıpkı C dilinde ki bir fonksiyon tanımı gibi. Bu local fonksiyona sadece kapsayıcı fonksiyondan ulaşabiliriz. Gizlilik seviyesinin üst derecede olmasını istediğimizde bu yapıyı kullanabiliriz.

fun localFunctionExample()
{
val outherName="Armagan Civelek"
fun subFunction()
{
println("Merhaba : $outherName")

}
subFunction()
}
output:
Merhaba Armagan Civelek

Extention function:

Kotlin, herhangi bir sınıftan miras almak zorunda kalmadan o sınıfa yeni fonksiyonlar eklememize olanak sağlar.

infix fun Int.add(x:Int):Int
{
return (this+x)//this keywordu extend ettiğimiz sınıfın instance ini işaret eder.

}
fun main(args: Array<String>)
{
val returnValue= 4 add 5 // 4 burada extend ettiğimiz sınıfın instance i iken 5 ise fonksiyona geçtiğimiz parametredir.
}

B) Lambda

Lambda fonksiyonlar anonim olarak kullanılabilen, single expression değer döndüren “return” anahtar kelimesi içermeyen, aslen high order fonksiyonlara parametre olarak verilebilen fonksiyon tipleridir. High order fonksiyonları ilerleyen bölümde inceleyeceğiz.

Lambda sytanx:

Aşağıdaki iki kullanım birbirine denktir.

val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }
val sum = { x: Int, y: Int -> x + y }

C) High Order Functions

Kotlin fonksiyonlar “First Class Citizen” dir yani fonksiyonlar bir değişkene parametre olarak verilebilir. Bir fonksiyondan döndürülebilir ve bir değişkende saklanabilir.

fun main(args: Array<String>) {
var lambda = { x: Int, y: Int -> x + y } //1
val resultValue = higherOrderFunc(4, 5, lambda) //2
println(resultValue)//3
}

fun higherOrderFunc(sayi1: Int, sayi2: Int, lmbd: (x: Int, y: Int) -> Int) = lmbd(sayi1, sayi2) //4

1 →İki adet int değişken alıp bunların toplamını geri dönen bir lambda tanımı yapıldı.

2 → higherOrderFunc adlı fonksiyona iki adet int değer ve bir adet lambda expression parametre olarak verildi.

3 → highOrderFunck fonksiyonundan değer ekrana yazıldı.

4 →HighOrderFunc tanımlandı ve içerisinde lambda expression çağrıldı.

High order fonksiyona parametre olarak normal fonksiyon vermek ::

lambda fonksiyonları kullanabildiğimiz gibi normal fonksiyonlarıda highorder fonksiyona paramet olarak verebiliriz.

Bu kullanımda parametre olarak gönderdiğimiz fonksiyonun adının önüne “::” işaretini kullanmamız gerekir.

fun main(args: Array<String>) {
var lambda = { x: Int, y: Int -> x + y
val resultValue = higherOrderFunc(4, 5, ::normalFunc)
println(resultValue)
}

fun normalFunc(x: Int, y: Int) = x + y //expression usin with type ınference

high order fonksiyona parametre olarak gönderdiğimiz fonksiyonu ; lambda expression ve normal fonksiyon olarak gönderdik. Dilersek fonksiyonu direk gönderirken de yazabiliriz.

fun main(args: Array<String>) {
val resultValue = higherOrderFunc(4, 5, {x,y->x+y+y})
val resultValue = higherOrderFunc(4, 5) {x,y->x+y+y}// yukarıdaki kullanıma denktir.
println(resultValue)
}


fun higherOrderFunc(sayi1: Int, sayi2: Int, lmbd: (x: Int, y: Int) -> Int) = lmbd(sayi1, sayi2)

inline :

High order fonksiyonlarımızı tanımlarken fonksiyonlar default olarak noinline tanımlanır. Bir high order fonksiyon aslen derlenirken arka tarafa bir instance i oluşturulur. Bu high order fonksiyonların yoğun kullanımında performans sorunu ortaya çıkartabilir. High order fonksiyonumuzun başına koyacağımız inline anahtar kelimesi ise bunun önüne geçmemize olanak sağlar. Alacağımız sonuç bakımından noinline ve inline kullanımı arasında hiçbir fark bulunmaz.

Kodumuzun java denkliği:

“lmbd” fonksiyonumuz function2 arayüzünü implement ederek, invoke fonksiyonunu override eder.

public static final int higherOrderFunc(int sayi1, int sayi2, @NotNull Function2 lmbd) {
Intrinsics.checkParameterIsNotNull(lmbd, "lmbd");
return ((Number)lmbd.invoke(sayi1, sayi2)).intValue();
}

Aslında olan şudur :

lmbd(new Function( ) {
@Override
public void invoke() {

}
});

inline anahtar kelimesi ile arka planda defalarca obje oluşturulmasının önüne geçmiş oluruz. Bu kısa kelimenin getirisi kendisinden çok daha uzundur.

Neden sürekli inline kullanmıyoruz ?

  • inline fonksiyonlar kapsayıcı sınıfının private üyelerine ulaşamazlar. Bunları kendimiz manuel olarak atamak zorunda kalırız.
  • Arka planda instance oluşturulmaz lakin daha fazla byte kod üretilir. Bu sebeple sadace lambdayı parametre olarak alan daha küçük high order fonsksiyonlarda kullanılması önerilir.

Crossinline :

inline fonksionlarda lambda işlevlerinin kontrol akışını (dönüş ifadesi) uygulamamıza yardımcı olur.

fun main(args: Array<String>) {

normalfunc()

}

fun normalfunc() {
println("normal fonksyion başlıyor")
inlineFunc {
println("inline Func")
return
}
println("normal fonksiyon bitiyor")
}


inline fun inlineFunc(lambda: () -> Unit) {
lambda()

}

yukarıda ki kodun çıktışı alttaki gibidir. Return anahtar kelimesi kullanıldıktan sonra bir üst fonksiyonun normal akışına devam edilemeden tüm fonksiyonlardan çıkılır.

Crossinline ile return kelimesinin inline fonksiyonda kullanılmasının önüne geçebiliriz.

fun main(args: Array<String>) {

normalfunc()


}

fun normalfunc() {
println("normal fonksyion başlıyor")
inlineFunc {
println("inline Func")
return // return is not allowed here
}
println("normal fonksiyon bitiyor")
}


inline fun inlineFunc( crossinline lambda: () -> Unit) {
lambda()

}

Kotlin temelleri dördüncü kısım için tıklayınız…

--

--