Kotlin’de Class Kavramı 2

Aysel Aydin
İyi Programlama
5 min readApr 18, 2020

--

Önceki Bölüm: Kotlin’de Class Kavramı 1

Merhaba arkadaşlar, bir önceki yazımızda Kotlin’de sınıf ve nesne kavramlarını detaylı bir şekilde ele almıştık. Bu yazımıza başlamadan öncesinde okumadıysanız ilk yazıyı okumanızı tavsiye ederim. Bugün Interface, Abstract, Data Class, Enum, Sealed Class, Nested Inner Class kavramlarını detaylı bir şekilde ele alıyor olacağız.

Interface

Interface’ler nesneye yönelimli programlamanın temel yapılarındandır. Class değillerdir, bir class’ın becerilerini tutan sözleşme niteliği taşıyan yapılardır. Class’ların ortak işlemleri varsa bu işlemler interface’te declare edilir ve ilgili class’lara implement edilir. Interface keyword’ü ve ardından interface’in adının yazılmasıyla tanımlanır.

interface Person {
val personName: String
fun personSalary(salary:Int){
println(“Salary $salary”)
}
fun personDepartment(department:String)
}
class PersonDoctor : Person{
override val personName:String = “Aysel Aydin”

override fun personDepartment(department:String){
}
}
  • Interface’ler bir ya da birden fazla metoda sahip olmak zorundadır.
  • Interface’lerin constructor’ı olmaz. Bu yüzden init blogu da kullanılamaz.
  • Interface’ler extend edilmez, implement edilir. “:” operatörü class’larda extend işlemi için kullanılırken, interface’ler için implement amacıyla kullanılır. Bir class’ta extend işlemi bir kez implement işlemi “,” ile ayrılarak birden fazla kez yapılabilir.
  • Interface’ler class’ların yapmasını istediğimiz metotları içerir ve bu metotlar override edilmek istendiği için tanımlanırlar. Bu yüzden interface’lerin final olarak tanımlanmasına izin verilmez.
  • Interface’ler state yani değer tutmazlar.
  • Interface içerisinde tanımlanan metotlar implement edilen class tarafından override edilmek zorundadır. Ama personSalary metodundaki gibi metot bir body’e sahipse bu metodu implement etmek zorunda değiliz. Bir body’e sahip değilse bize bu metodun implement edilmesi zorunluluğuna dair hata verip uyaracaktır.
  • Interface içerisinde kullanılan this o interface’i implement eden class’ı temsil eder.

Abstract Class

  • Interface‘lerin yapabildiği her şeyi yaparlar, artı olarak abstract class’lar state tutabilirler.
  • abstract keyword’ü ile tanımlanır ve class’lara extend edilirler.
abstract class BaseAnimal {    
abstract val name: String
open val animalType: String = ""
//abstract fun printNameWithBody(){
// print("abstract bir metoda body yazılamaz")
//}
abstract fun printName() open fun printName2() {
print("open keywordü olduğu için override edilmek zorunda değildir, body e sahip olabilir")
}
}
class SivasKangali() : BaseAnimal() {
override val name: String
get() = ""
override fun printName() {
print("override etme işlemi zorunlu")
}
override fun printName2() {
print("override etme işlemi isteğe bağlı")
}
}
  • Bir class sadece bir extend işlemi yapabileceği için interface’ye göre daha geniş çaptaki işlemleri yapmak için kullanılmalıdır.
  • Abstract olarak tanımlanmış bir metoda body yazılmaz çünkü extend edildiği class’ta abtract keyword’ü ile tanımlanmış her bir property, metot override edilmek zorundadır. Ama başlarında open keyword’ü varsa abtract class içerisinde tanımlanmış bir metodun extend edildiği class tarafından override edilmesi isteğe bağlıdır.
  • Interface’lere göre constructor ve init bloğuna sahiplerdir ve instance’i yaratılamaz ne kendi içinde ne de dışarıda.

Data Class

Verileri tutmak için bir sınıf oluşturmamız gerektiğinde kullanırız. Bir class’ın data class olmasının bazı şartları vardır.

  • data keywordü ile tanımlanmaları,
  • Primary constructor’a sahip olması,
  • Sahip olduğu primary constructor’ın en az bir tane val ya da var türünde bir değişkene sahip olması gerekir.
data class PersonInfo(
val personName: String,
val personAge: Int
)

Data Class’ları genel olarak herhangi bir body’e sahip olmadan kullanırız. Data class kullandığımızda copy(), equal(), toString(), hashCode() fonksiyonlarının kullanımında özelleştirilmiş bir kullanım sağlar. Bu fonksiyonları tanımladığımız primary constructor üzerinden kullanabilir.

Özellikle copy() metodu işimizi kolaylaştıran bir metottur. Eğer bir data class’ımızın değişkenlerinden birini değiştirip yeni bir atama yapmak istiyorsak, bunun için class’ımızın instance’ını oluşturup değişkenleri tekrardan yazmak yerine copy() metodunun yardımıyla değiştirmek istemediğimiz değişkenleri yazmadan sadece değiştirmek istediğimiz değişkeni değiştirebiliriz.

data class PersonInfo(
var personName: String,
var personAge: Int,
var personSalary = 3000,
var personJob: String,
var personCity: String)
val instancePerson = PersonInfo("Aysel", 29, 5000, "Android Developer", "İstanbul")

Sadece ismin farklı olduğu bir instance’ın daha gerekli olduğunu düşünün.

Bu önce copy() metodunu kullanmadan yapalım.

val instancePerson2 = PersonInfo("Aydın", 29, 5000, "Android Developer", "İstanbul")

Şimdi de aynı işlemi copy() metodunu kullanarak yapalım.

var copyPersonInstance: PersonInfo = instancePerson.copy("Aydın")

Evet bu şekilde değiştirmek istemediğimiz değişkenlere yeniden atama yapmaya gerek kalmadan sadece isim değişkenine yeni atamayı yapmış olduk.

Data Class’lar yukardaki örnekteki gibi default değer alabilirler. Ayrıca data class’lar inner, sealed, open ya da abstract olamaz.

Enum

Type olarak kullanabileceğimiz, sınırlı yapıdaki gruplayabileceğimiz elemanları içeren classlardır. enum keyword’ünün classın önüne eklenmesiyle olur.

enum class DirectionType{
EAST, WEST, SOUTH, NORTH
}
  • Genellikle büyük harfle yazılır. val ya da var ataması yapılmaz.
  • Enum class open, abstract, sealed, inner ya da data olamaz.
  • Herhangi bir class’tan extend alamaz fakat interface’den implement alabilirler. Eğer implement alırsa bir interface’den override işlemleri her bir tanım için yapılmalıdır.

Herhangi bir tanıma DirectionType.EAST şeklinde erişebiliriz. Ekranda bu değerleri göstermek istediğimiz de DirectionType.EAST ‘ i ENUM olarak gördüğü için kabul etmeyecektir. Değere DirectionType.EAST.name ile erişebiliriz.

ENUM classını oluştururken nasıl yazıyorsak ekranda gösterirken de o şekilde gösterilecektir. Farklı şekilde görünmesini istiyorsak

enum class DirectionType{
EAST{
override fun toString():String{
return “East”
}, WEST, SOUTH, NORTH
}

şeklinde toString() metodunu override ederek göstermek istediğimiz şekilde gösterimini sağlayabiliriz.
DirectionType.EAST.name yerine DirectionType.EAST.toString() metodunu çağırarak istediğimiz formatta görünmesini sağlayabiliriz. Bu tüm tanımlar için yapmalıyız.

ENUM classlar içerisinde abstract metotlar yazabiliriz. Sadece ismini değil de aynı şekilde position değerini de almak istiyorsak bunun için abstract bir metot yazıp ve bunu tüm ENUM elemanlarına override edip yapabiliriz bu işimizi görecektir.

enum class DirectionType{
EAST{
override fun printFullValue() {
println("name : ${EAST.name}, position : ${EAST.ordinal}")}
}, WEST, SOUTH, NORTH
abstract fun printFullValue()
}

Bu şekilde ismiyle beraber position değerini de göstermiş olduk. Tabi bunu class içindeki WEST, SOUTH, NORTH tanımlamaları için de yapmayı unutmayın :)

Bir enum class’ı içerisinde her ENUM tanımı için gerekli olan metotlar varsa ve birden fazlaysa abstract metot tanımlamak yerine interface hazırlayıp onu implement etmek daha doğru bir kullanımdır. Ama sadece bir metot tanımlamak işimizi görüyorsa bunu basitçe abstract metot ile yapabiliriz.

Sealed Class

Enum class’ların özelleştirilmiş halleridir. sealed keyword’ü ile tanımlama yapılır. Enum’lara primitive tipler verirken, sealed class’lar için class türünde tanımlamalar yaparız.

sealed class Person {
class Doctor : Person()
class Engineer : Person()
}

fun main(){
val doctorInstance = Person.Doctor()
val engineerInstance = Person.Engineer()
}

Sealed class tek başına abstract’tır. Doğrudan instance’ının oluşturulmasına izin vermez.

Nested, Inner Class

Class içerisinde class tanımlama işlemidir.

class School{
private val schoolName = "Aydın"
class HighSchool{//Nested Class fun printOuterName() {
println("Outher School Name değerine ulaşamayız nested class içerisinden")
}
inner class MedSchool{//inner Class fun printOuterName() {
println("Outher School Name : ${schoolName}")
}
}

Nested class’ın instance’ını oluşturmak istersek eğer bunu outer class üzerinden oluştururuz.

val highSchool = School.HighSchool()

Nested class’lar outer class’ların referansını tutmazlar. Nested class’lar outer class içerisinde tanımlanmış değişkenlere erişemezken, inner class’lar erişebilir.

val medSchool = School().MedSchool()

inner class’ların instance’ını oluştururken outer class’ların referansı tutulur.

Evet arkadaşlar Interface, Abstract, Data Class, Enum, Nested ve inner classları da bu şekilde ele almış olduk. Bir sonraki yazımızda görüşmek dileğiyle :)

--

--