Solve the problem that Kotlin class implements multiple interfaces and overrides the same method conflict in multiple interfaces

  • 2021-11-24 02:49:33
  • OfStack

1. Let's start with an example


package net.println.kotlin.chapter4

/**
 * @author:wangdong
 * @description: Conflict problem of class implementation interface 
 */

interface B{
  fun x(): Int = 1
}

interface C{
  fun x(): Int = 0
}

/**1 Classes implement two interfaces, and the methods in both interfaces are the same, so this class will conflict when overridden */
class D: B,C{
  // When the following two methods exist at the same time, the same method conflict will be reported 
  override fun x(): Int {
    return super<B>.x()
  }

  override fun x(): Int {
    return super<C>.x()
  }
}

2. Examples of conflict resolution


package net.println.kotlin.chapter4

/**
 * @author:wangdong
 * @description: Class inherits class and implements method conflict of interface 
 *  Interface methods can have default implementations 
 *  Signature 1 Conflict that causes and returns the same value 
 *  Subclass (implementation class) must override conflicting methods 
 * super<[ Parent class (interface) name ]>.[ Method name ]([ Parameter list ])
 */

abstract class A{
  open fun x(): Int = 5
}

interface B{
  fun x(): Int = 1
}

interface C{
  fun x(): Int = 0
}

/**1 Classes implement two interfaces, and the methods in both interfaces are the same, so this class will conflict when overridden */
/** Using branching mode to solve conflict problem */
class D(var y: Int = 0):A() ,B,C{

  // Return value 1 Be sure 1 Like, for example: 1 It must be Int
  override fun x(): Int {
    println("call x(): Int in D")
    if (y > 0){
      return y
    }else if (y < -200){
      return super<C>.x()
    }else if (y < -100){
      return super<B>.x()
    }else{
      return super<A>.x()
    }
  }
}

fun main(args: Array<String>) {
  println(D(3).x())
  println(D(-10).x())
  println(D(-110).x())
  println(D(-230).x())
}
/** Output result */
call x(): Int in D

call x(): Int in D

call x(): Int in D

call x(): Int in D

Additional knowledge: How Kotlin gracefully implements "multiple inheritance"

This issue will tell you an interesting thing. We all know that when Java flirted with C + + in a high-profile way, besides the favorite memory automatic reclamation, there was also a famous single inheritance. Any Java class is a subclass of Object, and any Java class has only one parent class. However, they can have multiple interfaces, just like this:


public class Java extends Language 
  implements JVMRunnable{  
  ...
}

public class Kotlin extends Language 
  implements JVMRunnable, FERunnable{  
  ...
}

This is really much simpler to use than C + +, but sometimes it is troublesome: Java and Kotlin can both run on JVM, and we use an interface JVMRunnable to identify their 1 identity; Now let's assume that both of them implement the same JVMRunnable interface, so we will write two duplicate pieces of code in Java and Kotlin:


public class Java extends Language 
  implements JVMRunnable{  
  public void runOnJVM(){    
    ...
  }
}

public class Kotlin extends Language 
  implements JVMRunnable, FERunnable{  
  public void runOnJVM(){    
    ...
  }  
  
  public void runOnFE(){    
    ...
  }
}

Repeated code was the last thing we wanted to see, so we decided to create an JVMLanguage as the parent class of Java and Kotlin, which provides the default implementation of runOnJVM. It looks good.


public abstract class JVMLanguage{  
  public void runOnJVM(){    
    ...
  }
}
    
public class Java extends JVMLanguage{

}

public class Kotlin extends JVMLanguage 
  implements FERunnable{  
  
  public void runOnFE(){    
    ...
  }
}

Of course, we also know that Kotlin can be compiled into Js to run, so it is far-fetched for us to call Kotlin JVMLanguage abruptly, but the perfect writing we just thought was actually inappropriate.

Simply put, the difference between inheritance and implementation interface is that inheritance describes the problem of "what" of this class, while implementation interface describes the problem of "what can be done" of this class.

Kotlin and Java can run on JVM, but Java can't run on the front end like Kotlin. The point that Kotlin and Java run on JVM can only be regarded as a kind of capability, but its essence cannot be characterized.

So we saw Feature, the default implementation of the interface, in Java 8, so our code can be changed:


public interface JVMRunnable{
  default void runOnJVM(){    
    ...
  }
}
    
public class Java extends Language 
  implements JVMRunnable{

}
  
public class Kotlin extends Language 
  implements JVMRunnable, FERunnable{  
  public void runOnFE(){    
    ...
  }
}

This is good, but the interface-level default implementation of the runOnJVM method is still very limited because the interface cannot hold state.

So what does Kotlin bring us? Please look at the following code:


abstract class Languageinterface JVMRunnable{  
  fun runOnJVM()
}

class DefaultJVMRunnable : JVMRunnable {  
  override fun runOnJVM() {
    println("running on JVM!")
  }
}

class Java(jvmRunnable: JVMRunnable) 
  : Language(), JVMRunnable by jvmRunnable

class Kotlin(jvmRunnable: JVMRunnable) 
  : Language(), JVMRunnable by jvmRunnable, FERunnable{  
  fun runOnFE(){    
    ...
  }
}

Through the way of interface proxy, we give the concrete implementation proxy of JVMRunnable to the instance of jvmRunnable, which can of course save the state. On the one hand, it can solve the problem of default implementation of the interface mentioned earlier, and on the other hand, it can provide capabilities without affecting the "essence" of the original class.


Related articles: