扩展功能可以始终转换为成员吗?

时间:2018-07-30 02:52:40

标签: kotlin

我有一些扩展功能,我想进入成员功能。但是我不确定如何做到这一点,尤其是对于嵌套的通用类型链。

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
}

1 个答案:

答案 0 :(得分: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
}

因此,如果您允许usr...anything下,那么/...anything/usr/local可能会起作用。 要回答您的一般性问题“扩展功能能否始终转换为成员功能?”这取决于。当使用泛型时,肯定不会1:1。也许这会导致一个新的类层次结构,并最终删除您的泛型信息;-)但这也可能最终效果不佳。

但是,正如我们写的很多...这是以前的解决方案(第二部分中仍然写有“不编译!”,但现在改编为“必须不编译!” ;-)) >

您可能已经认识到,无法推断出您的类型T。但是,正如您已经考虑过使用T : usr<localhost_>>一样,为什么不首先省略泛型类型信息呢?

如果将其删除,您的代码仍然可以按预期运行并且说实话,您甚至都没有使用过任何对泛型有用的东西;-)

如果您在那里,还可以省略扩展功能,因为您将不再需要扩展功能,并且如果您想缩小可以调用的范围(例如,应允许使用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) }
}

但是,调用它确实看起来并不好(因为您随后需要导入适当的类型...您仍然可以将叶子放在类层次结构之外,但这实际上是不相同的;-)(现在是{{1 }是一片叶子,但是如果以后有文件夹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 } 函数。我没有进一步详细说明;-) 正如您自己指出的那样,您甚至可以使用属性...实际上,您应该考虑很多事情,而不是滥用泛型和除法;-)