I originally wanted to create a class that can abort instantiation in constructor, but according to this link I should instead use a Factory class. But now I want to prevent anyone except the factory class from creating an object of class "Inner" while giving access to the methods of the inner class to everyone.
I have already tried 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)
}
}
I would use it like the following:
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()
It says "cannot access '<init>': it is private in 'Inner'" when hovering over my usage of the constructor in the "createInnerObject" function.
What you could do:
interface Inner
with all the necessary functions that should be exposedprivate
and implement that interfaceSample:
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...
}
}
Now you can't access InnerImpl
from outside anymore, but still have all the necessary functions available:
// 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()
Unfortunately, private
members of Kotlin inner classes are not accessible from the outer instance:
private
means visible inside this class only
Kotlin reference / Visibility modifiers
However, Java is not this restrictive with its visibility modifiers:
access is permitted if and only if it occurs within the body of the top level type (§7.6) that encloses the declaration of the member or constructor.
Java Language Specification / §6 Names / §6.6 Access Control / §6.6.1 Determining Accessibility
This is one of the only (annoying) cases I have found where Kotlin's rules make a common Java pattern impossible.
The only workarounds (if you want to keep your current structure) would be to rewrite this class in Java, or to expose this constructor with a less restrictive visibility (e.g. internal
.)
There was a discussion about this on the Kotlin forums - it seems that this is a JVM limitation, and that it only works in Java because the compiler generates appropriate synthetic
accessors.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With