Delegacja funkcji – suplement
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ć.