Here is a simple example of a class with some code (properties) inside the bracket
class Person(firstName: String) {
....
}
Now here is an example of a function with some code (parameters) inside the bracket
fun double(x: Int) {
...
}
I know this is a fundamental question but I am quite confused as a beginner.
You pass parameters to functions and constructors, and classes have properties.
The constructor of the Person class in your example has a single parameter, and so does the double function. In this case, the firstName parameter is not a property!
To make it a property, you have to declare it so:
class Person(firstName: String) {
val firstName : String = firstName
}
Kotlin allows this to be shorter, which makes the firstName parameter serve as a property:
class Person(val firstName: String)
First, your firstName also is a parameter rather than a property in Person class.
// v-- a parameter declared on primary constructor
class Person(firstName: String)
you can access a parameter declared on primary constructor in init block or property/field declaration, for example:
class Person(firstName: String){
val first:String
init{ first=firstName }
}
class Person(firstName: String){
val first:String = firstName
}
class Person(firstName: String){
#JvmField val first:String = firstName
}
to make the firstName to a property you can using keyword val or var, for example:
// v--- immutable property
class Person(val firstName: String)
// v--- mutable property
class Person(var firstName: String)
a Kotlin property will generate getter/setter(?) and a backing field(?) to java byte code. Take an example of a mutable property to java byte code as below:
public final class Person{
private String firstName; // backing field
// v--- parameter
public Person(String firstName){ this.firstName = firstName; }
//getter
public final String getFirstName(){ return firstName; }
//setter
public final String setFirstName(String firstName){ this.firstName= firstName; }
}
a parameter only visible in function scope/constructor scope except parameter declared on primary constructor.
Note: a parameter is immutable like as java effective-final variables/parameters, so you can't reassign a parameter at all in Kotlin, for example:
fun foo(bar:String){
bar = "baz"
// ^--- compile error
}
properties and parameters are different thinks:
parameters : when we declare any function :
fun sum(a: Int, b: Int): Int {
return a + b
}
Function having two Int parameters with Int return type:
Properties and Fields:
Declaring Properties
Classes in Kotlin can have properties. These can be declared as mutable, using the var keyword or read-only using the val keyword.
class Address {
var name: String = ...
var street: String = ...
var city: String = ...
var state: String? = ...
var zip: String = ...
}
To use a property, we simply refer to it by name, as if it were a field in Java:
fun copyAddress(address: Address): Address {
val result = Address() // there's no 'new' keyword in Kotlin
result.name = address.name // accessors are called
result.street = address.street
// ...
return result
}
The full syntax for declaring a property is:
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
Related
I have a variable foo:String declared in Kotlin and in constructor I want to pass variables with same name as foo:String but I don't know how to make both variables different from each other as in C# we use this.foo for class variables
I am expecting:
class Product
{
lateinit var productName:String
constructor(productName:String)
{
this.productName = productName
}
}
Please guide me how to do it in Kotlin.
That entire class can be written as just
class Product(var productName: String)
You use this like you used in C#
This sample shows how to do this for primary constructor
class Product{
private val name : String
constructor(name : String) {
this.name = name
}
}
And this is sample for second constructor:
class Product (){
private var name : String = ""
constructor(name : String) : this() {
this.name = name
}
}
My use case:
I have a large number of POJO models that are different types of requests for a third-party API. All of them have several common fields and a couple unique ones.
I was hoping to build something that conceptually looks like this
class RequestBase(
val commonField1: String,
val commonField2: String,
...
val commonFieldX: String
)
class RequestA(
val uniqueFieldA: String
): RequestBase()
class RequestB(
val uniqueFieldB: String
): RequestBase()
fun main() {
val requestA = RequestA(
commonField1 = "1",
commonField2 = "2",
...
uniqueFieldA = "A"
)
}
I can of course override the common fields in every child request and then pass them to the parent constructor, but this ends up producing a lot of boilerplate code and bloats the model. Are there any options I can explore here?
Notice that what you are doing in the parentheses that follow a class declaration is not "declaring what properties this class has", but "declaring the parameters of this class' primary constructor". The former is just something you can do "along the way", by adding var or val.
Each class can have its own primary constructor that take any number and types of parameters that it likes, regardless of what class its superclass is. Therefore, it is not unreasonable to have to specify all the parameters of the constructor:
open class RequestBase(
val commonField1: String,
val commonField2: String,
...
val commonFieldX: String
)
class RequestA(
// notice that the parameters for the inherited properties don't have the
// "val" prefix, because you are not declaring them in the subclass again.
// These are just constructor parameters.
commonField1: String,
commonField2: String,
...
commonFieldX: String,
val uniqueFieldA: String,
): RequestBase(
commonField1,
commonField2,
...
commonFieldX,
)
If you find this unpleasant, there are a bunch of ways to work around this.
One way is to use composition and delegation - create an interface having the common properties. The specific requests' primary constructors will take a RequestBase and their unique properties, and implement the interface by delegating to the RequestBase:
interface Request {
val commonField1: String
val commonField2: String
val commonFieldX: String
}
open class RequestBase(
override val commonField1: String,
override val commonField2: String,
override val commonFieldX: String
): Request
class RequestA(
val requestBase: RequestBase,
val uniqueField: String
): Request by requestBase
This allows you to access someRequestA.commonFieldX directly, without doing someRequestA.requestBase.commonFieldX, but to create a RequestA, you need to create a RequestBase first:
RequestA(
RequestBase(...),
uniqueField = ...
)
Another way is to change your properties to vars, give them default values, and move them out of the constructor parameters:
open class RequestBase {
var commonField1: String = ""
var commonField2: String = ""
var commonFieldX: String = ""
}
class RequestA: RequestBase() {
var uniqueField: String = ""
}
Then to create an instance of RequestA, you would just call its parameterless constructor, and do an apply { ... } block:
RequestA().apply {
commonField1 = "foo"
commonField2 = "bar"
commonFieldX = "baz"
uniqueField = "boo"
}
The downside of this is of course that the properties are all mutable, and you have to think of a default value for every property. You might have to change some properties to nullable because of this, which might not be desirable.
You can't do it with constructors of base class. Without constructors it's possible:
open class RequestBase {
lateinit var commonField1: String
lateinit var commonField2: String
...
lateinit var commonFieldX: String
}
class RequestA(
val uniqueFieldA: String
): RequestBase()
class RequestB(
val uniqueFieldB: String
): RequestBase()
fun main() {
val requestA = RequestA(
uniqueFieldA = "A"
).apply {
commonField1 = "1"
commonField2 = "2"
...
commonFieldX = "X"
}
}
class Greeter(name: String) {
fun greet() {
println("Hello, $name")
}
}
fun main(args: Array<String>) {
Greeter(args[0]).greet()
}
for above program I got this error
Unresolved reference: name
but when I add var or val
class Greeter(var name: String) {
or
class Greeter(val name: String) {
then program works fine, so why I need to add var or val to name, what is default type for constructor parameter val or var and why program gives me error when I not mention var or val
To use your value in the constructor like class Greeter(name: String), you can use init{}
class Greeter(name: String) {
var string:name = ""
init{
this.name = name
}
fun greet() {
println("Hello, $name")
}
}
or If you use val or var in the constructor it is more like class level variable and can be accessed anywhere inside the class
class Greeter(var name:String){
fun greet() {
println("Hello, $name")
}
}
The variable name can be used directly in the class then.
We can also give default values for the variables in both cases.
Adding val or var makes the parameter a property and can be accessed in the whole class.
Without this, it is only accessible inside init{}
The question is not making any sense, But the problem you are facing does make sense. In your case, the approach you are using is,
Wrong-Way:
// here name is just a dependency/value which will be used by the Greeter
// but since it is not assigned to any class members,
// it will not be accessible for member methods
class Greeter(name: String) {
fun greet(){} // can not access the 'name' value
}
Right-Way:
// here name is passed as a parameter but it is also made a class member
// with the same name, this class member will immutable as it is declared as 'val'
class Greeter(val name: String) {
fun greet(){} // can access the 'name' value
}
You can also replace val with var to make the name a mutable class member.
Just learning Kotlin In the first code down below there is the val keyword right in the other code there is not,
what is the different here if the val and var is omitted?
class Person(val firstName: String, val lastName: String) {
}
class Person(firstName: String, lastName: String) {
}
If val or var is omitted then they won't be properties, but parameters passed to constructor. You won't be able to work with them outside of constructor.
If you omit val or var in in a constructor, then the only places that can access these parameters are initialization statements that are evaluated at construction time. See https://kotlinlang.org/docs/reference/classes.html
This is useful when you want to do something with a value before storing it. In Java you would put that code a constructor body
class Person(firstName: String, lastName: String) {
// directly in val / var declarations
val firstName = firstName.capitalize()
val lastName = lastName
// in init blocks
val fullName: String
init {
fullName = "$firstName $lastName"
}
// secondary constructors can only see their own parameters
// and nothing else can access those
constructor(fullName: String) : this("", fullName)
}
But it also works for delegation using by
interface Named {
fun getName(): String
}
class Human(private val fname: String, private val lname: String) : Named {
override fun getName() = "$fname + $lname" // functions need val since
// value is resolved after construction
}
class Person2(firstName: String, lastName: String) : Named by Human(firstName, lastName)
class Person3(human: Human) : Named by human {
constructor(firstName: String, lastName: String): this(Human(firstName, lastName))
}
Or in property delegation
class Person4(firstName: String, lastName: String) {
val fullName: String by lazy { "$firstName $lastName" }
}
Note: the closure is captured at initialization time, the values are therefore still available when lazy evaluates eventually.
I'm little confused how kotlin is managing the property name and the primary constructor parameter name. If I wrote the same property name and the parameter name then kotlin compiler gives an error.
class Student(name : String, roll : Int){
val name: String
init {
name = "Asif"
}
}
It gives this error.
> Error:(9, 5) Kotlin: Property must be initialized or be abstract
> Error:(12, 9) Kotlin: Val cannot be reassigned
But when I change the name of the property val name : String or the changing the name of the parameter of the primary constructor name : String then the code will work and compile.
This will work or compile fine.
class Student(pName : String, roll : Int){
val name: String
init {
name = "Asif"
}
}
What is the reason behind this? Why we can't have the same primary constructor's parameter name and the property name?
Primary constructor parameters are available in property initializers and initializer blocks (this is what makes the primary constructor special).
In your init block, name refers to the constructor parameter, which as all other function parameters, cannot be reassigned. This is the second error. The first one is for the same reason, now your property isn't initialized anywhere.
If you want to initialize your property, you can still refer to it as this.name:
class Student(name : String, roll : Int){
val name: String
init {
this.name = "Asif"
}
}
Init block provides parameters from the default constructor. To assign name field of your object, you have to explicitly use this.name:
class Student(name : String, roll : Int){
val name: String
init {
this.name = "Asif"
}
}
But more kotlin-way is to declare field with default value directly in the constructor:
class Student(val name : String = "DefaultName", roll : Int){ }
The answer was provided by others but to clarify look at these versions:
(a)
class Student1(name : String, roll : Int){
val name: String
init {
this.name = "Asif"
}
}
(b)
class Student2(name : String, roll : Int){
val name: String = "Asif"
}
(c)
class Student3(val name : String = "DefaultName", roll : Int)
Classes Student1 and Student2 are totally equivalent but Student3 is not: If you run the below code:
val s = Student1("Nick", 2)
println(s.name)
val s2 = Student2("Nick", 2)
println(s2.name)
val s3 = Student3("Nick", 2)
println(s3.name)
you will see:
Asif
Asif
Nick
Kotlin provides concise and easy way like below:
class Student(var name : String, roll : Int){
init {
name = "Asif"
}
}
Copied: In fact, for declaring properties and initializing them from the primary constructor, Kotlin has a concise syntax. Refer here