Reputation: 311
I know that Kotlin generates getters and setters internally if we don't provide it but then the variable behaves just like a public variable in java which is considered bad in java programming, but not in Kotlin why?
Upvotes: 3
Views: 1184
Reputation: 46170
TL;DR: It's not the same thing. You aren't exposing a field in Kotlin; you're exposing a property. The property has a, implicitly or explicitly, getter and, if mutable, a setter. Accessing the property goes through the getter and setter. Essentially, Kotlin forces you to follow best practices in this context.
Exposing a field as public is generally considered bad practice in Java because it breaks encapsulation, makes the code less flexible (for future changes), and makes it more difficult to ensure data is valid and consistent. But Kotlin doesn't really have fields. A property may have a backing field, but it's only usable from within that property's accessors (i.e., the getter and setter).
The following Kotlin class:
class Person(var name: String)
// or
class Person(name: String) {
var name: String = name
}
Is equivalent to the following Java class:
import org.jetbrains.annotations.NotNull;
public final class Person {
private @NotNull String name;
public Person(@NotNull String name) {
// -- compiler intrinsic ensures 'name' is not null --
this.name = name;
}
public final @NotNull String getName() { return name; }
public final void setName(@NotNull String name) {
// -- compiler intrinsic ensures 'name' is not null --
this.name = name;
}
}
In Java, the private String name
is a field. But in Kotlin, the var name: String
is a
property. A Kotlin property, at least when non-private, always has a getter and, if mutable, a setter. Code reading from
and writing to the property go through the getter and setter, respectively. In other words,
the following Kotlin code:
val person = Person("Steve")
person.name = person.name + " Buscemi"
Is equivalent to the following Java code:
final Person person = new Person("Steve");
person.setName(person.getName() + " Buscemi");
So, you aren't exposing a field in Kotlin; you're exposing a property, which "guards" its state with a getter and, if mutable, a setter. You can add or remove custom getters and setters without changing other code that uses the property. Essentially, Kotlin properties force you to use best practices with regards to getters and setters.
To see an example of a custom setter, let's say you don't want to allow blank names for a person.
You would modify the Person
class to look something like this:
class Person(name: String) {
/*
* You cannot define a custom getter or setter for a property defined
* in the primary constructor. So, you have to move the property into
* the class body. Make sure to keep a parameter in the constructor if
* you still want to allow initializing the property during construction.
*/
var name: String = ""
set(value) {
require(name.isNotBlank()) { "blank name" }
field = value
}
init {
// invokes the setter, which the inline initialization above bypasses
this.name = name
}
}
Note: See https://discuss.kotlinlang.org/t/why-is-the-setter-of-a-property-not-called-on-construction/14488
That would be behaviorally equivalent to the following Java class:
import org.jetbrains.annotations.NotNull;
public final class Person {
private @NotNull String name = "";
public Person(@NotNull String name) {
// -- compiler intrinsic ensures 'name' is not null --
setName(name);
}
public final @NotNull String getName() { return name; }
public final void setName(@NotNull String name) {
// -- compiler intrinsic ensures 'name' is not null --
if (name.isBlank()) {
throw new IllegalArgumentException("blank name");
}
this.name = name;
}
}
Though if you're dealing with a data class, which should typically be at least shallowly
immutable, then such data validation would simply be in the init
block.
data class Person(val name: String) {
init {
require(name.isNotBlank()) { "blank name" }
}
}
Note you can also have the setter be less visible than the property itself. For example:
class Person(name: String) {
var name: String = name
private set
}
Upvotes: 1
Reputation: 322
Similarly to C#, Kotlin uses the concept of properties which means you can do something like
var name: String = “John”
Giving you a private field and public getters/setters.
Java does not have this and the fields are exposed if not defined as private or protected.
Upvotes: 1