如何创建一个内部类,其中只有外部类可以访问构造函数,但其余的在任何地方都可见?



我最初想创建一个可以在构造函数中中止实例化的类,但根据此链接,我应该改用工厂类。但是现在我想阻止除工厂类之外的任何人创建类"Inner"的对象,同时向所有人提供对内部类方法的访问权限。

我已经尝试过这个答案。

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()

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

你可以做什么:

  • 引入具有应公开的所有必要功能的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()

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

private表示仅在
此类中可见Kotlin 引用/可见性修饰符

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

且仅当访问发生在包含成员或构造函数声明的顶级类型 (§7.6) 的主体中时,才允许访问。
Java 语言规范/§6 名称/§6.6访问控制/§6.6.1 确定可访问性

这是我发现的唯一(令人讨厌的)案例之一,其中 Kotlin 的规则使通用的 Java 模式变得不可能。

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

在 Kotlin 论坛上对此进行了讨论 - 这似乎是 JVM 的限制,并且它仅适用于 Java,因为编译器会生成适当的synthetic访问器。

你可以做constructorprotected。这样,您只将其公开给子类,在本例中为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)
}

最新更新