【问题标题】:Can an extension function always be translated to a member?扩展函数总是可以翻译成成员吗?
【发布时间】:2018-07-30 02:52:40
【问题描述】:

我有一些扩展函数想移到成员函数中。但我不知道该怎么做,尤其是嵌套泛型类型链。

import Y.*

abstract class File<T>

open class Y private constructor() {
  open class localhost_ {
    @JvmName("usr") operator fun div(a: usr.Companion) = usr<localhost_>()
    @JvmName("bin") operator fun div(a: bin.Companion) = bin<localhost_>()
    @JvmName("etc") operator fun div(a: etc.Companion) = etc<localhost_>()

    companion object: localhost_()
  }

  open class bin<T>: File<T>() { companion object }
  open class sh<T>: File<T>() { companion object }
  open class etc<T>: File<T>() { companion object }
  open class vim<T>: File<T>() { companion object }
  open class usr<T>: File<T>() { companion object }
  open class local<T>: File<T>() { companion object }

  companion object { fun uri(path: Any) = println(path) }
}

operator fun bin<localhost_>.div(a: sh.Companion) = sh<bin<localhost_>>()
operator fun bin<usr<localhost_>>.div(a: vim.Companion) = vim<bin<usr<localhost_>>>()
operator fun etc<localhost_>.div(a: vim.Companion) = vim<etc<localhost_>>()
operator fun usr<localhost_>.div(a: bin.Companion) = bin<usr<localhost_>>()
operator fun usr<localhost_>.div(a: local.Companion) = local<usr<localhost_>>()
operator fun local<usr<localhost_>>.div(a: bin.Companion) = bin<local<usr<localhost_>>>()

/**
 * localhost_/
 * ├── bin/
 * │   └── sh
 * ├── etc/
 * │   └── vim
 * └── usr/
 *     ├── bin/
 *     │   └── vim
 *     └── local/
 *         └── bin/
 */

fun main(a: Array<String>) {
  //Compiles!
  Y.uri(localhost_)
  Y.uri(localhost_/bin)
  Y.uri(localhost_/bin/sh)
  Y.uri(localhost_/etc)
  Y.uri(localhost_/etc/vim)
  Y.uri(localhost_/usr)
  Y.uri(localhost_/usr/bin/vim)
  Y.uri(localhost_/usr/local)
  Y.uri(localhost_/usr/local/bin)


  //Does not compile!
  Y.uri(localhost_/local)
  Y.uri(localhost_/bin/vim)
  Y.uri(localhost_/sh)
  Y.uri(localhost_/bin/local)
  Y.uri(localhost_/etc/local)
  Y.uri(localhost_/etc/sh)
  Y.uri(localhost_/usr/local/usr)
}

如何将接收器具有泛型类型的扩展函数转换为成员函数?有没有办法将运算符函数放在类中,或者扩展是实现这一点的唯一方法?我已经尝试过这样的事情,但它不起作用:

open class usr<T>: File<T>() {
    operator fun <T: usr<localhost_>> div(a: local.Companion) = local<T>()
    operator fun <T: usr<localhost_>> div(a: bin.Companion) = bin<T>()

    companion object
}

【问题讨论】:

    标签: kotlin


    【解决方案1】:

    首先,让我说:这是对除法运算符功能的相当明显的误用。只是为了使用/ 而忽略除数的除法?不过老实说,在代码中使用/ 也是一种有趣的方式;-) 尽管如此,我还是不推荐它。看看你的代码。您现在有很多未使用的除数 ;-) 除了免责声明,让我们来解决您的问题。

    您的具体问题的解决方案并不像您想象的那么好。您对扩展函数所做的事情并不容易移动到成员函数。原因:您将扩展函数的使用范围缩小到特定的泛型类型。如果你想和会员一样,你也需要在那里缩小范围。如果你看一下扩展函数是什么(静态方法调用),你可能在实现类中也需要同样的结构。以下示例有效,但并未缩小层次结构:

    open class usr<T>: File<T>() {
      operator fun div(a: local.Companion) = local<usr<T>>()
      operator fun div(a: bin.Companion) = bin<usr<T>>()
      companion object
    }
    

    所以 /...anything/usr/local 可能会起作用,如果您允许 usr...anything 之下。 要回答您的一般问题“扩展函数是否总是可以转换为成员函数?”这取决于。一旦使用泛型,肯定不是 1:1。也许这会导致一个新的类层次结构并最终删除您的泛型信息;-)但这也可能不会很好。

    但是,因为我们写了很多...这里是以前的解决方案(第二部分仍然写“不编译!”,但现在适应了“不得编译!”;-)

    您可能已经认识到,您的类型T 无法推断。但是您已经考虑过使用T : usr&lt;localhost_&gt;&gt;,为什么不首先省略泛型类型信息?

    如果你在任何地方删除它,你的代码仍然可以按预期工作,老实说,你甚至没有使用任何对泛型有益的东西;-)

    如果你在那里,也可以省略扩展函数,因为你不再需要它们了,如果你想缩小可以调用的范围(例如应该允许localhost_/usr/local/bin,但localhost_/usr/local/usr/local/bin不允许)那么泛型是可能不是要走的路。

    这是您的示例,没有泛型和扩展函数:

    abstract class File
    open class Y private constructor() {
        open class localhost_ {
            @JvmName("usr") operator fun div(a: usr.Companion) = usr()
            @JvmName("bin") operator fun div(a: bin.Companion) = bin()
            @JvmName("etc") operator fun div(a: etc.Companion) = etc()
            companion object {
                operator fun div(a: usr.Companion) = usr()
                operator fun div(a: bin.Companion) = bin()
                operator fun div(a: etc.Companion) = etc()
            }
        }
    
        // hierarchies are also built as class hierarchies
        open class bin: File() {
            operator fun div(a: sh.Companion) = sh()
            open class sh: File() { companion object }
            companion object }
        open class etc: File() {
            operator fun div(a: etc.vim.Companion) = etc.vim()
            open class vim: File() { companion object }
            companion object }
        open class usr: File() {
            operator fun div(a: usr.bin.Companion) = usr.bin()
            operator fun div(a: usr.local.Companion) = usr.local()
            open class bin: File() {
                operator fun div(a: usr.bin.vim.Companion) = usr.bin.vim()
                open class vim: File() { companion object }
                companion object }
            open class local : File() {
                operator fun div(a: local.bin.Companion) = local.bin()
                open class bin : File() { companion object }
                companion object }
            companion object }
    
        companion object { fun uri(path: Any) = println(path) }
    }
    

    但是,调用它确实看起来不太好(因为您随后需要导入适当的类型......您仍然可以将叶子放在类层次结构之外,但这真的不一样 ;-)(现在 vim 是一片叶子,但如果你以后有一个文件夹 vim 怎么办?)

    Y.uri(localhost_)
    Y.uri(localhost_)
    Y.uri(localhost_ / bin)
    Y.uri(localhost_ / bin / bin.sh)
    Y.uri(localhost_ / etc)
    Y.uri(localhost_ / etc / etc.vim)
    Y.uri(localhost_ / usr)
    Y.uri(localhost_ / usr / usr.bin / usr.bin.vim)
    Y.uri(localhost_ / usr / usr.local)
    Y.uri(localhost_ / usr / usr.local / usr.local.bin)
    

    但同样,由于您没有使用除法作为除法,您可能仍然最好省略它(因为您甚至没有获得漂亮/有用的代码完成),例如通过使用:

    Y.uri("localhost_/usr/local/bin")
    

    或使用具体类型和StringBuilder 连接您的路径:

    abstract class File {
      val uri = StringBuilder()
    
      inline fun <reified T> append() : T {
        val clazz = T::class
        uri.append(clazz.simpleName).append("/")
        return clazz.java.newInstance()
      }
    }
    open class usr: File() {
      fun local() = append<local>()
      fun bin() = append<bin>()
    }
    

    你可以这样调用:

    Y.uri(localhost().usr().local().bin())
    // or simply localhost().usr().local().bin().uri
    

    您甚至可以通过这种方式获得有用的代码完成。

    这里是一个例子,我还是不推荐,它在后面混合了运算符和一个具体化的函数:

    open class usr: File() {
      operator fun div(a : local.Companion) = append<local>()
      operator fun div(a : bin.Companion) = append<bin>()
      companion object
    }
    

    请注意,您也可以将 refied 类型与您的运算符混合,但您需要一个 inline 函数。我没有进一步详细说明;-) 正如您自己指出的那样,您甚至可以使用属性...确实有很多事情您应该考虑而不是滥用泛型和除法;-)

    【讨论】:

    • 感谢您详细周到的回复。我对您的解决方案很感兴趣,但是,在阅读了几次之后,我无法看到它是如何解决问题的。可能我不够聪明。你写道,“你为什么不首先忽略泛型类型信息?如果你在任何地方删除它,你的代码仍然可以像你期望的那样工作”。这种说法根本不正确(试试看)。主函数必须按描述工作。
    • 我对使用函数调用持开放态度,但同样,主函数必须按描述工作。这是使用您的语法的规范。编译:localhost().bin()localhost().bin().sh()localhost().usr().bin()。不编译:localhost().usr().bin().sh().
    • 作为参考,你可以在这里找到我的解决方案:github.com/breandan/yuri/blob/master/src/main/kotlin/Y.kt
    • 我很确定我测试了你所有的主要条目。稍后会看看它。也许我错过了一份副本;-)
    • 在验证您的解决方案时,重要的是所有“编译!”语句编译并且没有“不编译!”语句编译。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-01-15
    • 1970-01-01
    • 2023-03-28
    • 1970-01-01
    • 2021-02-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多