【问题标题】:How to create an inner class where only the outer class can access the constructor, but the rest is visible everywhere?如何创建一个内部类,其中只有外部类可以访问构造函数,但其​​余部分随处可见?
【发布时间】:2019-01-16 16:02:29
【问题描述】:

我最初想创建一个可以在构造函数中中止实例化的类,但根据this link 我应该改用工厂类。但是现在我想阻止除了工厂类之外的任何人创建“内部”类的对象,同时让每个人都可以访问内部类的方法。

我已经尝试过this answer

import java.util.Date

object InnerFactory {

    class Inner private constructor(startDate: Date? = null, endDate: Date? = null) {
        fun getTimeDifference(): Long? {
            //calculates time difference but doesn't matter to this example
        }
    }

    fun createInnerObject(startDate: Date? = null, endDate: Date? = null): Inner? {
        if (startDate != null && endDate != null && !endDate.after(startDate)) {
            return null
        }

        return Inner(startDate, endDate)
    }

}

我会像下面这样使用它:

val date1 = Date(1547600000)
val date2 = Date(1547600600)
val inner = InnerFactory.createInnerObject(date1, date2) //should return an instance
val invalidInner = InnerFactory.createInnerObject(date2, date1) //should not return an instance because the "endDate" is before "startDate"
val difference = inner?.getTimeDifference()

当我将鼠标悬停在“createInnerObject”函数中对构造函数的使用上时,它说“无法访问'':它在'Inner'中是私有的”。

【问题讨论】:

  • 请注意:Kotlin 区分“内部”和“嵌套”类。您的 Inner 类实际上是一个嵌套类。一个实际的内部类是用inner 关键字定义的。不同之处在于内部类携带对外部类对象的引用。你可以在这里阅读更多信息:kotlinlang.org/docs/reference/nested-classes.html

标签: kotlin


【解决方案1】:

你能做什么:

  • 引入一个interface Inner,其中包含所有应该公开的必要功能
  • 创建所有类private 并实现该接口

示例:

object InnerFactory {

  interface Inner {
    fun getTimeDifference(): Long?
  }

  private class InnerImpl(startDate: Date? = null, endDate: Date? = null) : Inner {
    override fun getTimeDifference(): Long? = TODO("some implementation")
  }


  fun createInnerObject(startDate: Date? = null, endDate: Date? = null): Inner? {
    if (startDate != null && endDate != null && !endDate.after(startDate)) {
      return null
    }
    return InnerImpl(startDate, endDate) // InnerImpl accessible from here but not from outside of InnerFactory...
  }
}

现在您无法再从外部访问InnerImpl,但仍然可以使用所有必要的功能:

// the following all work as it deals with the interface
val inner = InnerFactory.createInnerObject(date1, date2) //should return an instance
val invalidInner = InnerFactory.createInnerObject(date2, date1) //should not return an instance because the "endDate" is before "startDate"
val difference = inner?.getTimeDifference()

// the following will not compile:
InnerImpl()

【讨论】:

  • 好主意! +1 唯一的缺点是您首先需要在接口中定义属性/函数,然后必须在 InnerImpl 中覆盖它,这有点多余。
  • 是的,那是真的……但我认为在这种结构中使用interface 是很常见的……
【解决方案2】:

很遗憾,Kotlin 内部类的 private 成员无法从外部实例访问:

private 表示仅在此类内部可见
Kotlin reference / Visibility modifiers

但是,Java 的可见性修饰符并没有这种限制:

当且仅当访问发生在包含成员或构造函数声明的顶级类型 (§7.6) 的主体内时,才允许访问。
Java Language Specification / §6 Names / §6.6 Access Control / §6.6.1 Determining Accessibility

这是我发现的仅有的(烦人的)案例之一,其中 Kotlin 的规则使常见的 Java 模式成为不可能。

唯一的解决方法(如果你想保留你当前的结构)是用 Java 重写这个类,或者以较少限制的可见性公开这个构造函数(例如internal。)

在 Kotlin 论坛上有一个关于此的 discussion - 似乎这是 JVM 的限制,并且它仅适用于 Java,因为编译器会生成适当的 synthetic 访问器。

【讨论】:

  • 好的。还是谢谢你。
【解决方案3】:

您可以制作 constructor protected。这样你只将它暴露给子类,在本例中为PrivateClass。然后,您将创建一个 PrivateClassnull 的实例,但将其返回为 InnerClass?

object InnerFactory {

    fun createInnerObject(startDate: Date? = null, endDate: Date? = null): Inner? {
        // return null here if conditions are not met
        return PrivateClass(startDate, endDate)
    }

    open class Inner protected constructor(val startDate: Date?, val endDate: Date?) {
        fun getTimeDifference(): Long? { /* ... */ }
    }

    private class PrivateClass(startDate: Date?, endDate: Date?): Inner(startDate, endDate)
}

【讨论】:

  • 请注意,您不会从 Inner 隐藏该构造函数,然后...您仍然可以调用类似 val myFakedInner = object : Inner(null, null) {} 的东西,然后它将构造一个有效的 Inner-object 重用您想要的功能首先隐藏(如果我正确理解了该要求)...仍然取决于用例(并且如果持有vals 是可以的)这也是一个有效的变体...但是您可能想要设置vals 到 private 至少...
  • @Roland 确定你可以从 Inner 继承。我什至在我的回答中说过“这样你只能将它暴露给子类......”,但你不能直接实例化它。这是我方法的弱点。 :D
  • 真的。由于“在这种情况下PrivateClass”,我只是想我会提出这个问题,以免被误解..
  • @Roland 当然,事情 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-31
  • 1970-01-01
  • 2011-08-11
  • 2014-09-09
  • 1970-01-01
  • 2013-05-31
相关资源
最近更新 更多