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
Related
I have 2 secondary constructors for a data class.
data class node(var type: String):parentNode(){
constructor(type: String, value: aNode) : this(type)
constructor(type: String, value: bNode) : this(type)
}
I want to return a value from a function which is node(type:String, value:aNode).
fun getNode(): node{
val aNode = getAnode
val type = "Bank"
val return_val = node(type,aNode)
return (return_val)}
a = getNode()
Now 'a' has only the 'type' but not 'aNode'.
Any idea on what am i missing here?
This is because value is not a property of node class. It is just a constructor argument. You need to put it as a property first and then initialize it from the constructor.
data class node(var type: String): parentNode() {
var value: parentNode? = null // Assuming aNode and bNode inherit from parentNode
constructor(type: String, value: aNode) : this(type) {
this.value = value
}
constructor(type: String, value: bNode) : this(type) {
this.value = value
}
}
Now you will be able to access this value using a.value. If the node class is instantiated using the primary constructor, a.value will be null.
Also, you might want to add private set to this value property so that it cannot be modified from outside. You can do the same with the type property (make it a val). Most of the times you would want to use val properties in a data class instead of vars.
(And it is recommended to follow Kotlin's naming conventions while creating variables, classes, functions, etc.)
Edit: As #gidds suggested, you can also include the value property in the primary constructor with a default value null and get rid of those secondary constructors.
data class node(val type: String, val value: parentNode? = null)
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.
class Animal {
val name: String
constructor(name: String){
this.name = name // initialized via constructor
}
}
For the above class in Kotlin I am able to initialize a val property via secondary constructor but the same is not allowed for Data classes
data class User(val name: String, val postalCode: Int) {
val email: String
constructor( email: String): this("", 1){
this.email = email // error: value can not be reassigned
}
}
What I can't understand is, where is the email property is initialized already as I haven't declared any initializes?
If your class has a primary constructor, you have to initialize all of its properties "in the primary constructor" - either by directly initializing them at their declaration:
val email = "foo#bar.com"
Or in an initializer block:
val email: String
init {
email = "foo#bar.com"
}
The compiler forces you to forward all secondary constructor calls to the to the primary constructor, and since the primary constructor already has to initialize all properties inside the class (otherwise calling it would construct a partially initialized instance, like in your code example), it wouldn't make sense to also initialize them in the body of the secondary constructor, especially for a val which cannot be reassigned.
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>]
I have this code:
class AnyUsernamePersistentNodePath(override val value: String) : AnyPersistenceNodePath {
override val key = "username"
}
and
interface AnyPersistenceNodePath {
val key: String
val value: String
}
So far, so good. Now I want parameter value in the constructor to be named username, instead of value. But, obviously, keep overriding interface's property value. Is that possible in Kotlin?
I know that I can do:
class AnyUsernamePersistentNodePath(val username: String) : AnyPersistenceNodePath {
override val key = "username"
override val value = username
}
but I'd like to avoid it.
You can do what you want simply by removing val from the constructor parameter, so that it is a parameter and not a member. Your final class would be:
class AnyUsernamePersistentNodePath(username: String) : AnyPersistenceNodePath {
override val key = "username"
override val value = username
}
You cannot otherwise change the name of something that you are truly overriding. But you can pass in the value to be assigned to a member later during construction, as my slightly modified version of your code shows.