Kotlin Access Modifiers & Constructors – also see Java comparison of top-level modifiers
fun main() {
// Public by default, also private, protected and internal
// Can't use protected at the top level
// Can have multiple (top-level) classes in one file, unlike Java. Hence you can have private classes which are only accessible from the same file
val person = Person()
println(person)
// Internal means accessible from within the same module. You can have more than one module in a project.
// In Kotlin, classes can't see private members belonging to inner classes
// When compiling, the JVM compiles private -> package-private, internal -> public, but Kotlin enforces its visibility rules so it won't be broken
// However, when mixing Java and Kotlin may result in Java being able to access stuff in Kotlin it shouldn't e.g. internal should only access in same module, but ANY Java code can access it as is compiled to public. Look out for this - typically the name will be very long and ugly when trying to access from Java. Probably shouldn't be doing!
// All classes are public and final by default. Have constructor created by default.
val emp = Employee("John")
println(emp.firstName)
val emp1 = Employee1("Jack")
println(emp1.firstName)
val emp2 = Employee2("Jane", false)
println("${emp2.firstName} ${emp2.fullTime}")
val emp2x = Employee2("Jill") // Default fullTime applied
println("${emp2x.firstName} ${emp2x.fullTime}")
val emp2a = Employee2a("Jane", false)
println("${emp2a.firstName} ${emp2a.fullTime}")
val emp2ax = Employee2a("Jill") // Default fullTime applied
println("${emp2ax.firstName} ${emp2ax.fullTime}")
emp2ax.fullTime = false // Demonstrate setter
// emp2x.firstName = "Joan" // Not allowed as firstName is a val
println(Demo().dummy)
val emp3 = Employee3("Jim", false)
// println(emp3.firstName) // Invalid. firstName is private, so how do we get or set? In Java we could allocate getters and setters and would allow us access. In Kotlin, default getters and setters are blocked in private - can't be done.
// Therefore no point in designating properties private if needing to be changed. Fine to have public properties - members outside will not be able to change them.
val emp4 = Employee4("Jill") // Default fullTime applied
emp4.fullTime // Calls custom getter
emp4.fullTime = false // Calls custom setter
}
private class Person {
}
// This is equivalent to the Employee Java class in this module. **The long way**
class Employee constructor(firstName: String){ // Declare constructor on same line as class - this is the primary constructor.
val firstName: String // Declaration
init { // NOT a constructor. This block runs when the class is first instantiated. Can have multiple, run in order.
this.firstName = firstName
}
}
//The Kotlin way
class Employee1 (val firstName: String) // Declaration inline with constructor
// If you wanted to declare access modifier for constructor, you would need 'constructor' keyword:
// class Employee2 protected constructor(val firstName: String)
// This is equivalent to the Employee2 Java class in this module. **The long way**
class Employee2 (val firstName: String) {
var fullTime: Boolean = true // Cannot be null. Only need this if using the variable outside the constructor body
constructor(firstName: String, fullTime: Boolean): this(firstName){
this.fullTime = fullTime
} // Secondary constructor. Calls the primary and feeds in firstName. Cannot declare properties in secondary constructor
}
//The Kotlin way
class Employee2a (val firstName: String, var fullTime: Boolean = true) // No need for multiple constructors as default is declared
//Demo of class without primary constructor, but with secondary. Because you can run expression in secondaries but not primaries
class Demo {
val dummy: String
constructor() {
dummy = "hello"
}
}
// Up until now all properties have been (default) public. Here is an Employee class with private properties:
class Employee3 (private val firstName: String, private var fullTime: Boolean = true) // Cannot access e.g. emp.firstName. Reason it worked before is Kotlin creates default getters and setters (see equivalent Employee3 Java class). Likely no need to do this as all properties are public & final by default and have to go through the getters and setters to access them.
// To customise the getters/setters (accessors) you cannot declare the property within the primary constructor, but in the class:
class Employee4 (val firstName: String, fullTime: Boolean = true) { // Not declaring the property, just using it
var fullTime = fullTime // Must declare getter/setter (accessor) immediately after declaring the property
get() {
println("We are getting fullTime")
return field // The only time we ever use the 'field' identifier - refers to a backing field in custom accessor
}
set(value) {
println("Running the custom set")
field = value
}
}
public class JavaEmployee {
public final String firstName;
public JavaEmployee (String firstName) {
this.firstName = firstName;
}
}
public class JavaEmployee2 {
public final String firstName;
private final boolean fullTime;
public JavaEmployee2(String firstName) { // Alternative constructor which applies default fullTime
this.firstName = firstName;
this.fullTime = true;
}
public JavaEmployee2(String firstName, boolean fullTime) {
this.firstName = firstName;
this.fullTime = fullTime;
}
}
public class JavaEmployee3 {
public String firstName;
private boolean fullTime;
public JavaEmployee3(String firstName) { // Alternative constructor which applies default fullTime
this.firstName = firstName;
this.fullTime = true;
}
public JavaEmployee3(String firstName, boolean fullTime) {
this.firstName = firstName;
this.fullTime = fullTime;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public boolean isFullTime() {
return fullTime;
}
public void setFullTime(boolean fullTime) {
this.fullTime = fullTime;
}
}