Na wykopie pod linkiem do wczorajszego wpisu padło pytanie:

A jak rozwiązywane są konflikty, gdy np rozszerzasz klasę, która ma tę samą nazwę metody co delegowany interface?

Najpierw rzućmy okiem na zachowanie „gołej” Javy:

Listing 1. Java, a metody o takiej samej nazwie

public class App {

	public static void main(String[] args) {
		C c = new SubC();
		c.m();

		SubC subC = new SubC();
		subC.m();

		I i = new SubC();
		i.m();
	}
}


interface I{

	default void m(){
		System.out.println("From I");
	}
}


class C{

	public void m(){
		System.out.println("From C");
	}

}

class SubC extends C implements I{

	@Override
	public void m() {
		super.m();
		System.out.println("From SubC");
	}
}

Ładnie się skompilowało i wypisuje:

Listing 2. Efekty działania

From C
From SubC
From C
From SubC
From C
From SubC

Jednak ten kod nie jest dokładnie tym samym czym kod w Kotlinie. Tu mamy do czynienia z dziedziczeniem i implementacją interfejsu, a nie z delegatami. Jednak możemy wnioskować, że Kotlin powinien zachować się podobnie. Zobaczmy:

Listing 3. Przesłanianie nazw z delegatem

interface I {
    fun m(){
        println("From I") 
    }
}

open class C {
    open fun m() {
        println("From C")
    }
}

class D : I {
    override fun m() {
        println("From D")
    }
}

class SubC(d: D) : C(), I by d {

}

To się nie skompiluje, ponieważ kompilator nie może wybrać implementacji m z pomiędzy C.m i I.m. Wynika to z zaimplementowania metody I.m. Jeżeli usuniemy implementację:

Listing 4. Brak implementacji I.m

interface I {
    fun m()
}

open class C {
    open fun m() {
        println("From C")
    }
}

class D : I {
    override fun m() {
        println("From D")
    }
}

class SubC(d: D) : C(), I by d {

}

Wszystko pójdzie zgodnie z planem i wypisywane będzie From D. Innym rozwiązaniem jest implementacja spornej metody, ale wtedy tracimy „urok” delegatów, bo musimy je samodzielnie skrobać.