I am having some trouble with visibility modifiers...
# getting-started
j
I am having some trouble with visibility modifiers. Basically I want a public getter method with a private setter for subclasses of a parent class. The issue is if that parent class has a private variable it is not able to be set from the child classes so I have to have the parent have a public variable for the child classes to use it but now the var is public so the getter is useless. In Java this was possible with the package-private modifier which in Kotlin has been changed to module-private so do I really need to create a new module just to hide some
vars
?
e
private for subclassees
isn't that "protected"?
j
Yeah but it cannot be used on top level declarations
I can work around that in an annoying and messy way though
c
Yeah, I think you’re looking to use
protected
on the
set
method of it
Screen Shot 2022-05-24 at 10.48.26 AM.png
e
fyi, if you're just doing the default (get/set field) you can leave out the bodies,
Copy code
public var someValue: String = ""
    protected set
is equivalent to the definition in the previous image
j
the parent classes
someValue
can be modified by anyone though
oh I see, the set and get syntax is very strange. set and get are implied to belong to the field...because the syntax is underneath it? Not really clear on the scoping at play here
c
Java’s package-private doesn’t protect against that either, though. Anyone could create the same package and access all the package-private members. And if someone is really determined to change the property, they can eve. bypass
private
fields with reflection.
internal
is the closest analog Kotlin has to package-private, except that it actually does give strong guarantees around encapsulation in different modules It’s difficult to truly ensure that a field is never modified except by your own code, so the better route isn’t to forcibly hide those fields, bur rather provide sufficient documentation to let consumers know when they should and should not modify it, so they can make the decision for themselves. Using opt-in annotations on those APIs can help make sure that the library users are aware of the concerns
j
How does get() work? The docs claim that it is a property on any var but I cannot just paste a get() to random vars in main() {} it gives an error
I don't really care about that though because I control the package, I just don't want any consumers outside that package modifying it
c
Properties in Kotlin are not the same as Fields in java. Rather, a Kotlin property is more like a Java getter/setter function. The docs may clear it up for you https://kotlinlang.org/docs/properties.html#getters-and-setters
j
Where is the get syntax defined? It looks like it can be named anything according to the docs but the compiler only allows "get"
e
it is not indentation sensitive, but context sensitive.
get
and
set
immediately following a property declaration have special meaning, otherwise they don't. but by convention they are indented to visually mark them as belonging to the property
c
The property itself can be named whatever you want (
val stringRepresentation: String
is the property).
get()
and
set(value)
is the specific syntax for customizing the accessors of the property. Anyone accessing the property with
myClass.stringRepresentation
will not care whether the property internally uses the
get()
or
set(value)
accessors or not
e
(well, they also have meaning as annotation use-site targets, but that's in a different unambiguous context)
j
ok weird so get and set have to be named get and set and get has to come before set
actually the order doesn't even matter just the names
e
like many of Kotlin's keywords, they are soft keywords: they have special meaning in specific context only
c
With property accessors, you’re not defining your own functions, you’re using language keywords. The compiler creates the appropriate functions (and maybe a Field) for you in the Java bytecode using the name of the property they’re attached to. It’s a bit weird at first if you’re thinking of it like a Java Field, but if you get used to the fact that a property is shorthand syntax for get/set functions, it will make more sense. And Kotlin is literally creating standard bean-style functions based on the properties, see here how you cannot write a custom
getSomeValue()
method because the compiler is already creating a method with that name from the property
j
seems like there is a ton of soft and fuzzy logic in Kotlin
makes it really hard to read the docs, are there any other docs that explain things more thoroughly? Like what is actually happening
e
soft keywords are not unique to Kotlin; Scala has many, Python has a few, and Java is considering them
it is not "soft and fuzzy logic" by any means - it is 100% defined in the grammar
j
anyways it seems like what I want to do isn't possible in kotlin. I just wanted to make an enum or sealed class with a public getter private setter
e
"public getter private setter" is literally
Copy code
public var property: Type
    private set
it sounds like what you want isn't "private", but more like "protected" or "internal"?
j
yes, useable for children which are enums and sealed classes
e
then that is
protected
, not
private
.
j
protected doesn't work because they are top level members
so essentially I wouldn't even be using sealed classes or enums at this point, just deconstructing them and reconstructing them with open vars or something similar
e
you have a top-level mutable property? that is bad design, and cannot participate in the class hierarchy
but top-level
private
is really "file-private", so that does give you some options
j
I want a top level property that can be modified based on which type the class is
e
just to clarify. top-level means "outside of any class", e.g. at the file level. is this really what you mean?
j
reading the kotlin docs it looks like top level meant at the top level of the class ie
Copy code
class  Test {
   var topLevel

    class subClass {

     }
}
c
Copy code
class Foo {
  private val onlyVisibleInThisClass : String
  protected val alsoVisibleToSubclasses : String
  internal val visibleToEverythingInThisModule : String
  public val visibleToEverything : String	
}
e
where in the docs does it say that?
j
I cannot find it now but that is how it was appearing to work earlier when I tried to use this for sealed classes
c
is the issue perhaps with nested/inner classes vs inheritance? in your example subClass is NOT a subclass, it’s a nested class. It’s only a subclass if it inherits, as in:
Copy code
class subClass : Test()
j
I think in a sealed class they are all inheriting by some fuzzy implied logic
c
no. they are inheriting by what you specify as the parent class.
j
I don't think that's accurate
c
it is. sealed classes reflect an inheritance hierarchy.
j
There's some weird dual return type inheritence structure going on
c
no. implement with standard inheritance.
j
ok
so it sounds like I am not specifying it, it's being inherited
c
if you don’t specify the inheritance there is no inheritance relationship.
e
they are only inheriting if you declare them as inheriting. the structure is unrelated.
j
Copy code
sealed class SealedClass() {
    object Child
}
Child is a subclass of SealedClass
I didn't specify that, it was implied by the structure of the class
e
that is only a nested class. you need to state
Child : SealedClass()
to make it a child. it does not matter if it is nested or not.
c
A sealed class can have nested classes defined without them being subclasses. You have to explicitly extend the parent class for the class relationship to take effect
👀 1
c
Copy code
public abstract class BaseClass() {
    
}

public sealed class Child1 : BaseClass() {
    
}

public sealed class Child2 : BaseClass() {
}
e
exactly. it doesn't matter whether
Child1
and
Child2
are nested inside of
BaseClass
or not
c
yep. this is equivalent:
Copy code
public abstract class BaseClass() {
    public sealed class Child1 : BaseClass() {

    }

    public sealed class Child2 : BaseClass() {

    }
}
j
Ok I see the kotlin compiler is having trouble distinguishing this
e
it is not trouble - it is intentional, and the same as other languages
c
This behavior is also different from Java. In Kotlin, a class defined inside another is by default like a Java static inner class. You can use
inner class
keyword to make it like the default Java nested classes
e
inner classes are a type of nested class - but still not a child class
j
thanks
e
Java:
Copy code
public class Parent {
    static class Nested extends Parent {}
    class Inner extends Parent {}
}
without
extends Parent
, they are only nested, not children
j
yes I prefer the java syntax, it's much more clear what is happening there
kotlin just makes everything look like a return type
I really dislike that about kotlin, they try to fold all the naming and syntax conventions into a small numbers of things that have many different contextual meanings
it makes the code much less readable
c
Most newer languages are doing the same thing though. It makes things a lot nicer in a world with type inference https://elizarov.medium.com/types-are-moving-to-the-right-22c0ef31dd4a
Just takes some getting used to
Anytime you switch languages, it’s going to feel uncomfortable at first. But once you get used to Kotlin you’ll find it’s an objectively better-designed language than Java in most situations, being overall more concise and the intent of your code more clear
❤️ 4