1. 程式人生 > 程式設計 >解決Kotlin 類在實現多個介面,覆寫多個介面中相同方法衝突的問題

解決Kotlin 類在實現多個介面,覆寫多個介面中相同方法衝突的問題

一、首先來看一個例子

package net.println.kotlin.chapter4

/**
 * @author:wangdong
 * @description:類實現介面的衝突問題
 */

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

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

/**一個類實現了兩個介面,兩個介面中的方法相同,這個類在覆寫的時候就會出現衝突*/
class D: B,C{
  //當下面兩個方法同時存在的時候,就會報方法相同的衝突
  override fun x(): Int {
    return super<B>.x()
  }

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

二、解決衝突的例子

package net.println.kotlin.chapter4

/**
 * @author:wangdong
 * @description:類繼承類,實現介面的方法衝突問題
 * 介面方法可以有預設的實現
 * 簽名一致且返回值相同的衝突
 * 子類(實現類)必須覆寫衝突方法
 * super<[父類(介面)名]>.[方法名]([引數列表])
 */

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

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

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

/**一個類實現了兩個介面,兩個介面中的方法相同,這個類在覆寫的時候就會出現衝突*/
/**採用分支模式解決衝突問題*/
class D(var y: Int = 0):A(),B,C{

  //返回值一定要一樣啊,例如:一定要是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())
}
/**輸出的結果*/
call x(): Int in D

call x(): Int in D

call x(): Int in D

call x(): Int in D

補充知識:Kotlin 如何優雅的實現『多繼承』

這一期給大家講一個有意思的東西。我們都知道 Java 當年高調的調戲 C++ 的時候,除了最愛說的記憶體自動回收之外,還有一個著名的單繼承,任何 Java 類都是 Object 的子類,任何 Java 類有且只有一個父類,不過,它們可以有多個介面,就像這樣:

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

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

這樣用起來真的比 C++ 要簡單得多,不過有時候也會有些麻煩:Java 和 Kotlin 都可以執行在 JVM 上面,我們用一個介面 JVMRunnable 來標識它們的這一身份;現在我們假設這二者對於 JVMRunnable 介面的實現都是一樣的,所以我們將會在 Java 和 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(){    
    ...
  }
}

重複程式碼使我們最不願意看到的,所以我們決定建立一個 JVMLanguage 作為 Java 和 Kotlin 的父類,它提供預設的 runOnJVM 的實現。看上去挺不錯。

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

}

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

當然,我們還知道 Kotlin 可以編譯成 Js 執行,那我們硬生生的把 Kotlin 稱作 JVMLanguage 就有些牽強了,而剛剛我們覺得很完美的寫法呢,其實是不合適的。

簡單的說,繼承和實現介面的區別就是:繼承描述的是這個類『是什麼』的問題,而實現的介面則描述的是這個類『能做什麼』的問題。

Kotlin 與 Java 在能夠執行在 JVM 這個問題上是一致的,可 Java 卻不能像 Kotlin 那樣去執行在前端,Kotlin 和 Java 執行在 JVM 上這個點只能算作一種能力,而不能對其本質定性。

於是我們在 Java 8 當中看到了介面預設實現的 Feature,於是我們的程式碼可以改改了:

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

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

這樣很好,不過,由於介面無法儲存狀態,runOnJVM 這個方法的介面級預設實現仍然非常受限制。

那麼 Kotlin 給我們帶來什麼呢?大家請看下面的程式碼:

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(){    
    ...
  }
}

通過介面代理的方式,我們把 JVMRunnable 的具體實現代理給了 jvmRunnable 這個例項,這個例項當然是可以儲存狀態的,它一方面可以很好地解決我們前面提到的介面預設實現的問題,另一方面也能在提供能力的同時不影響原有類的『本質』。

以上這篇解決Kotlin 類在實現多個介面,覆寫多個介面中相同方法衝突的問題就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支援我們。