如何创建一个内部类,其中只有外部类可以访问构造函数,而其余的随处可见?

时间:2019-01-16 16:02:30

标签: kotlin

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

我已经尝试过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'中是私有的”。

3 个答案:

答案 0 :(得分:4)

您可以做什么:

  • 引入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 :(得分:2)

不幸的是,无法从外部实例访问private内部Kotlin成员:

  

private意味着仅在此类内可见
   Kotlin reference / Visibility modifiers

但是,Java的可见性修饰符并不局限于此:

  当且仅当

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

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

唯一的解决方法(如果要保留当前结构,是 if )将是用Java重写该类,或以较少的可见性(例如internal公开此构造函数)。 )

在Kotlin论坛上有一个关于discussion的信息-似乎这是JVM的局限性,并且仅在Java中有效,因为编译器会生成适当的synthetic访问器。

答案 2 :(得分:0)

您可以制作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)
}