Kotlin中扩展函数的多态性 [英] Polymorphism on extension functions in Kotlin

查看:92
本文介绍了Kotlin中扩展函数的多态性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有几个我不控制的类,我已经在几个常见的属性上创建了几个同名的扩展方法。相同名称的扩展函数总是返回相同的值类型,尽管对于每种类型的接收器都以不同的方式进行计算。这是基于内置类型的简化示例,仅用于一个属性:

  // * *不编译** 

//我无法控制的三个样本类扩展名为.len
inline val String.len get()= length
inline val< T> List< T> .len get()= size
inline val< T> Sequence< T> .len get()= count()

//需要使用.len
class Calc< T>(val obj:T){ //这里是问题...
val dbl get()= obj?.len * 2 //将double
//以及其他使用.len和其他方法的其他方法并行扩展


fun main(a:Array< String>){
val s =abc
val l = listOf(5,6,7 )
val q =(10..20步骤2).asSequence()
val cs = Calc(s)
val cl = Calc(l)
val cq = Calc (q)
println(Lens:$ {cs.dbl},$ {cl.dbl},$ {cq.dbl})
}

想象几个其他常见属性,在某些我不能控制的类中以与.len相同的方式扩展。如果我不想在每个类中重复自己,我应该如何构造一个可以在.len(以及其他类属性)上对这三个类进行操作的正确类型的类?



我研究了以下但未找到可行的解决方案:


  • 泛型例如上面的,但不能获得正确的语法。

  • 密封类,但我没有控制这些类。

  • 联合类型,我发现在Kotlin中不受支持。
  • 包装

  • 传递lambda表达式weblog / 2016/06/20 / ad-hoc-polymorphism-in-kotlin /rel =nofollow noreferrer>这个博客的解释,但没有得到它的正确,而且似乎boptimalsu为每种方法传递多个lambda表达式。
  • ong>

    解决方案

    下面是一个用密封类和一个扩展属性将任何东西转换为可以给你< $ c> len 或 double 。不知道它是否具有更好的可读性。

      val任何?.calc get()= when(this){
    是字符串 - > Calc.CalcString(this)
    是List< *> - > Calc.CalcList(this)
    是序列< *> - > Calc.CalcSequense(this)
    else - > Calc.None
    }

    / *或者没有默认回退* /

    val String.calc get()= Calc.CalcString(this)
    val列表* * calcal get()= Calc.CalcList(this)
    val Sequence * .calc get()= Calc.CalcSequense(this)

    / *密封扩展类* /

    密封类Calc {

    abstract val len:Int?

    val dbl:Int?通过lazy(LazyThreadSafetyMode.NONE){len?.let {it * 2}}

    类CalcString(val s:String):Calc(){
    override val len:Int? get()= s.length
    }

    class CalcList< out T>(val l:List< T>):Calc(){
    override val len:Int? get()= l.size
    }

    class CalcSequense< out T>(val s:Sequence< T>):Calc(){
    override val len:Int? get()= s.count()
    }

    对象无:Calc(){
    覆盖val len:Int? get()= null
    }

    }

    fun main(args:Array< String>){
    val s =abc.calc
    val l = listOf(5,6,7).calc
    val q =(10..20 step 2).asSequence()。calc

    println(Lens :$ {s.dbl},$ {l.dbl},$ {q.dbl})
    }


    I have a several classes I don't control, upon which I've already created several identically-named extension methods across several common "attributes". The identically-named extension functions always return the same value type, though calculated in different ways for each type of receiver. Here is a simplified example based on built-in types for just one attribute:

    // **DOES NOT COMPILE**
    
    // three sample classes I don't control extended for .len
    inline val String.len get() = length
    inline val <T> List<T>.len get() = size
    inline val <T> Sequence<T>.len get() = count()
    
    // another class which needs to act on things with .len
    class Calc<T>(val obj:T) {       // HERE IS THE PROBLEM...
      val dbl get() = obj?.len * 2   // dummy property that doubles len
      // ... and other methods that use .len and other parallel extensions 
    }
    
    fun main(a:Array<String>) {
      val s = "abc"
      val l = listOf(5,6,7)
      val q = (10..20 step 2).asSequence()
      val cs = Calc(s)
      val cl = Calc(l)
      val cq = Calc(q)
      println("Lens:  ${cs.dbl}, ${cl.dbl}, ${cq.dbl}")
    }
    

    Imagine several other "common" properties extended in the same manner as .len in some classes I don't control. If I don't want to repeat myself in every class, how do I construct a properly typed class that can operate on .len (and other such properties) generically for these three classes?

    I've researched the following but not found workable solutions yet:

    • generics, in the example above, but can't get the syntax right.
    • sealed classes, but I don't have control of these classes.
    • union types, which I've found aren't supported in Kotlin.
    • wrapper classes, but couldn't get the syntax right.
    • passing lambdas a la this blog explanation, but didn't get it right, and it seemed boptimalsu to pass multiple lambdas around for every method.

    There must be a better way, right?

    解决方案

    Here's a example with sealed classes and a single extension property to convert anything to something which can give you len or double. Not sure if it has better readability thogh.

    val Any?.calc get() = when(this) {
        is String -> Calc.CalcString(this)
        is List<*> -> Calc.CalcList(this)
        is Sequence<*> -> Calc.CalcSequense(this)
        else -> Calc.None
    }
    
    /* or alternatively without default fallback */
    
    val String.calc get() = Calc.CalcString(this)
    val List<*>.calc get() = Calc.CalcList(this)
    val Sequence<*>.calc get() = Calc.CalcSequense(this)
    
    /* sealed extension classes */
    
    sealed class Calc {
    
        abstract val len: Int?
    
        val dbl: Int? by lazy(LazyThreadSafetyMode.NONE) { len?.let { it * 2 } }
    
        class CalcString(val s: String): Calc() {
            override val len: Int? get() = s.length
        }
    
        class CalcList<out T>(val l: List<T>): Calc() {
            override val len: Int? get() = l.size
        }
    
        class CalcSequense<out T>(val s: Sequence<T>): Calc() {
            override val len: Int? get() = s.count()
        }
    
        object None: Calc() {
            override val len: Int? get() = null
        }
    
    }
    
    fun main(args: Array<String>) {
        val s = "abc".calc
        val l = listOf(5,6,7).calc
        val q = (10..20 step 2).asSequence().calc
    
        println("Lens:  ${s.dbl}, ${l.dbl}, ${q.dbl}")
    }
    

    这篇关于Kotlin中扩展函数的多态性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆