Kotlin Language Features Related to Null Handling

Any software engineer with a Java background would find the null handling features in the Kotlin language interesting. Let's summarize this topic with some examples. Nullable types: In Kotlin, types are non-nullable by default. If you want a variable to be able to hold a null value, you need to explicitly declare its type as nullable using the Type? syntax. For example, String? denotes a nullable string, while String represents a non-nullable string. Safe calls (?.): Kotlin introduces the safe call operator (?.) for handling nullable types. It allows you to safely invoke a method or access a property on a nullable object. If the object is null, the expression returns null instead of throwing a NullPointerException. Example: data class Person(val name: String, val age: Int, val address: String?) fun main() {     // Create a person with a nullable address     val person1 = Person("John Doe", 25, "123 Main Street")     val person2 = Person("Jane Doe", 30,...

Visitor Tasarım Kalıbını Hatırlayalım

Visitor kalıbına hangi durumda ihtiyaç duyabiliriz? Bir nesne yapısı üzerinde işlemler yapmak istiyoruz ve bu işlemlerin üzerinde çalıştığı nesne yapısından bağımsız olarak çeşitlenmesini istiyoruz. Bu durumda Visitor kalıbı işimize yarayabilir. Aşağıdaki örnekte bir arabanın parçaları üzerinde clean ve repair işlemleri yapılıyor:

public interface CarElement {
    void accept(CarElementVisitor visitor);
}

public interface CarElementVisitor {
    void visit(Engine engine);
    void visit(Wheel wheel);
}

public class Car implements CarElement {

    private Engine engine;
    private List wheels;

    public Car(Engine engine, Wheel... wheels) {
        this.engine = engine;
        this.wheels = Arrays.asList(wheels);
    }

    @Override
    public void accept(CarElementVisitor visitor) {
        engine.accept(visitor);
        for (Wheel wheel : wheels) {
            wheel.accept(visitor);
        }
    }
}

public class Engine implements CarElement {
    @Override
    public void accept(CarElementVisitor visitor) {
        visitor.visit(this);
    }
}

public class Wheel implements CarElement {
    @Override
    public void accept(CarElementVisitor visitor) {
        visitor.visit(this);
    }
}

public class CleaningVisitor implements CarElementVisitor {
    @Override
    public void visit(Engine engine) {
        System.out.println("Cleaning engine!");
    }

    @Override
    public void visit(Wheel wheel) {
        System.out.println("Cleaning wheel!");
    }
}

public class RepairingVisitor implements CarElementVisitor {
    @Override
    public void visit(Engine engine) {
        System.out.println("Repairing engine!");
    }

    @Override
    public void visit(Wheel wheel) {
        System.out.println("Repairing wheel!");
    }

    @Override
    public void visit(Car car) {
        System.out.println("Repairing car!");
    }
}

public class Program {

    public static void main(String[] args) {

        Engine engine = new Engine();
        Wheel wheel1 = new Wheel();
        Wheel wheel2 = new Wheel();

        Car car = new Car(engine, wheel1, wheel2);

        CarElementVisitor cleaningVisitor = new CleaningVisitor();
        CarElementVisitor repairingVisitor = new RepairingVisitor();

        car.accept(cleaningVisitor);
        car.accept(repairingVisitor);
    }

}

Burada Car sınıfına baktığımızda, üzerindeki "visit" işleminden haberdar fakat Cleaning ve Repairing gibi ayrıntılardan habersiz. Yani, kendi nesne yapısı üzerinde işlemlerin yapılabilmesini sağlıyor ve hatta hangi parçaları üzerinde işlemlerin yapılacağını belirliyor. Fakat işlemlerin somut sınıflarına bağımlı değil. Sadece CarElementVisitor arayüzüne bağımlı. 
Visitor sınıfları ise, Car sınıfının parçaları üzerinde yapılacak işlemin ayrıntılarını biliyor. 


Comments

Popular posts from this blog

Trie Data Structure and Finding Patterns in a Collection of Words

Virtual Memory

NOTES ON COMPUTER ARCHITECTURE: Some important concepts in computer architecture