I was creating a very simple kotlin program and saw a weird behavior for parent class.
The code is:
fun makeSalt(name:String) = Spice(name, "non-spicy")
fun main(args: Array<String>) {
var salt : Spice = Spice("salt", "non-spicy")
println("Salt heat = ${salt.heat}")
val spicelist = listOf<Spice>(
Spice("salt", "non-spicy"),
Spice("turmeric", "mild"),
Spice("Pepper", "hot"),
Spice("Chilli", "hot"),
Spice("Sugar", "non-spicy")
)
val mildSpices = spicelist.filter{it.heat <=5}
val salt2 = makeSalt("rock salt")
val bhoot : SubSpice = SubSpice("bhoot", "hot")
}
open class Spice(open var name:String, open var spiciness:String = "mild" ){
var heat : Int = 5
get() = when (spiciness){
"mild"->5
"hot"->10
"non-spicy"->1
else -> 0
}
init{
if(spiciness === null) {println("spiciness is null")}
else println("Spiciness of ${name} = ${spiciness}; heat = ${heat}")
}
}
class SubSpice(override var name:String, override var spiciness:String = "hot") : Spice(name, spiciness){
}
When I execute this program, the output is:
Spiciness of salt = non-spicy; heat = 1
Salt heat = 1
Spiciness of salt = non-spicy; heat = 1
Spiciness of turmeric = mild; heat = 5
Spiciness of Pepper = hot; heat = 10
Spiciness of Chilli = hot; heat = 10
Spiciness of Sugar = non-spicy; heat = 1
Spiciness of rock salt = non-spicy; heat = 1
spiciness is null
As you can see, when I created an object of the sub-class, the variable spiciness of parent class becomes null. Can someone explain why is that? I expected it to be null since it also has default parameter of "mild"
You are you using open var when you're not overriding any getter/setter methods.
You're introducing weird initialization conflict since Spice.init (parent class constructor) is invoked before SubSpice.init and by overriding the fields they are no longer initialized alongside parent constructor - instead they will be available once Subspice is constructed.
Remove open keyword from variables in parent class and override var in child constructor, that way fields will be properly initialized in Spice class and its init block should run successfully.
To add extra information to Pawel's answer:
If you are overridding properties using open, the init block in the base class will run before the derived properties have been initialized.
See docs: https://kotlinlang.org/docs/reference/classes.html#derived-class-initialization-order
So if we change the code like Pawel suggested, we end up with something like this:
fun main(args: Array<String>) {
Spice("salt", "non-spicy")
Spice(name="spice")
SubSpice(name = "bhoot", spiciness = "hot")
SubSpice(name="subspice")
}
open class Spice(var name:String, var spiciness : String = "mild" ) {
var heat : Int = 5
get() = when (spiciness){
"mild"->5
"hot"->10
"non-spicy"->1
else -> 0
}
init{
if(spiciness === null) {println("spiciness is null")}
else println("Spiciness of ${name} = ${spiciness}; heat = ${heat}")
}
}
class SubSpice(name: String, spiciness : String = "hot") : Spice(name, spiciness) {
}
Which outputs:
Spiciness of salt = non-spicy; heat = 1
Spiciness of spice = mild; heat = 5
Spiciness of bhoot = hot; heat = 10
Spiciness of subspice = hot; heat = 10
Related
I am working through the tutorial on android developer central. One of the challenges is to extend the tutorial dice roller to include 2 dice. I tried creating a 2nd instance of my dice with
var dice2 = Dice(6) using the same class definition that I did for my first die. However, both return the same value. So I tried creating a 2nd class definition for Dice2 and created an instance of that with `var dice2 = Dice2(6). But I get the same result. Below is the full code.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val rollButton: Button = findViewById(R.id.button)
rollButton.setOnClickListener { rollDice() }
//roll the dice onLoad
rollDice()
}
private fun rollDice() {
//create two 6 sided die and rolls them
val dice = Dice(6)
val diceRoll = dice.roll()
val dice2 = Dice2(6)
val diceRoll2 = dice2.roll()
//assign the ImageViews called imageView and imageView2 to the variables diceImage and diceImage2
val diceImage: ImageView = findViewById(R.id.imageView)
val diceImage2: ImageView = findViewById(R.id.imageView2)
//update the die graphic based on the outcome of the dice roll
//assigns the resource id to a variable called drawableResource
val drawableResource = when(diceRoll) {
1 -> R.drawable.dice_1
2 -> R.drawable.dice_2
3 -> R.drawable.dice_3
4 -> R.drawable.dice_4
5 -> R.drawable.dice_5
else -> R.drawable.dice_6
}
/* do the same for the 2nd die */
val drawableResource2 = when(diceRoll2) {
1 -> R.drawable.dice_1
2 -> R.drawable.dice_2
3 -> R.drawable.dice_3
4 -> R.drawable.dice_4
5 -> R.drawable.dice_5
else -> R.drawable.dice_6
}
/*Update the die image source based on the variable
convert the die roll results from int to string and update the content description for
the die image
*/
diceImage.setImageResource(drawableResource)
diceImage.contentDescription = diceRoll.toString()
diceImage2.setImageResource(drawableResource)
diceImage2.contentDescription = diceRoll2.toString()
}
}
//create a class for an object called Dice & defines a roll function with random number
class Dice(private val numSides: Int) {
fun roll(): Int {
return (1..numSides).random()
}
}
class Dice2(private val numSides: Int) {
fun roll(): Int {
return (1..numSides).random()
}
}
Any help is greatly appreciated.
It seems you made a mistake in this line:
diceImage2.setImageResource(drawableResource)
It should be:
diceImage2.setImageResource(drawableResource2)
Try diceImage2.setImageResource(drawableResource2) instead of diceImage2.setImageResource(drawableResource). But better:
you definitely don't need Dice2; use val dice2 = Dice(6) as you had originally, or even just val diceRoll2 = dice.roll() again.
you'd avoid even a chance to make that mistake if you made a function
fun showDiceResult(viewId: Int, rollResult: Int) {
// get view by id
// set image resource
// set content description
}
and then called it twice
showDiceResult(R.id.imageView, diceRoll)
showDiceResult(R.id.imageView2, diceRoll2)
In next code I'm trying to write DSL, which allowed some constructions for hands of some robot.
It should have syntax below, which I should use in outer context for RobotBuilder class:
robot {
hands {
plastik width 3
load = light - medium
}
head {
...
}
etc...
}
The problem is in string load = light - medium. It's do nothing, but Robot created. What I do? Just overload minus operator for Int. For me when Kotlin compiler see expression of = - it should use overloaded operator. What I am doing wrong?
Should I create special class like LoadClassBuilder with only one attribute of type LoadClass and then overload minus operator in HandsBuilder for - and change types for load, light and medium or problem in outer context of my DSL?
class HandsBuilder {
var material: Material = Metal(0)
var minLoad: LoadClass = LoadClass.Medium
var maxLoad: LoadClass = LoadClass.Medium
val plastik: Plastik = Plastik(0)
val metal: Metal = Metal(0)
infix fun Metal.width(width: Int) {
material = Metal(width)
}
infix fun Plastik.width(width: Int) {
material = Plastik(width)
}
var load: Int = 0
val light: Int = 1
val medium: Int = 2
operator fun Int.minus(other: Int): Int {
println("minus call")
minLoad = LoadClass.values()[this]
println(this)
maxLoad = LoadClass.values()[other]
println(other)
return 1
}
fun build() = Hands(material, minLoad, maxLoad)
}
Update 1:
fun hands(init: HandsBuilder.() -> Unit) {
handsParamInRobotBuilderClass = HandsBuilder().apply(init).build()
}
In the following code snippet i have created 4 instances of CompanionClass and want to know how many
instances i have created , so have tried many ways but didn't work. Here i used companion object class
to keep track of objects but i know it has no connection with objects because keepTrackOfObjects() is
static. Can anyone help me solve this, please?
class CompanionClass
{
companion object{
var numberOfObjects = 0
fun keepTrackOfObjects() = println("number of created objects currently is: +
${++numberOfObjects}")
}
}
fun main()
{
val obj1 = CompanionClass()
CompanionClass.keepTrackOfObjects()
val obj2 = CompanionClass()`enter code here`
val obj3 = CompanionClass()
val obj4 = CompanionClass()
CompanionClass.keepTrackOfObjects()
}
// output is
// 1
// 2
You can use the init block to count the elements you created:
class CompanionClass {
companion object {
var counter: Int = 0
fun counter(): Int {
return counter
}
}
init {
counter++
}
}
You can test the behavior in this kotlin playground.
The problem is with your companion Object,
var numberOfObjects = 0
will initialise the variable every time you create a new instance. So create this variable in a separate class where keep all static content and use it from there. then you companion object will be like this.
companion object{
fun keepTrackOfObjects() = println("number of created objects currently is: +
${++numberOfObjects}")
}
To achieve this, you should increment your numberOfObject in a init statement.. Get a look at the code below
class User (var firstName:String,var lastName:String){
companion object {
var numberofinstance:Int = 0
fun getNumberOfInstance() = println("Nombre d'instance = $numberofinstance")
}
init {numberofinstance++}
}
Now you can use it in your main function
fun main(){
val user1=User("bob","marley")
val user2=User("bonkey","reynard")
val user3=User("jobbs","steve")
val user4=User("bill","naim")
val user5=User("jeff","romain")
User.getNumberOfInstance()
}
Output : Number of instance = 5
I'm brand new to Kotlin. I wanted to try the create-react-kotlin-app since I'm a react developer currently and I want to explore Kotlin.
I'm having a tough time instaniating my variables. I'm trying to do this:
fun main(args: Array<String>) {
val rootDiv = document.getElementById("root")
val gm : GameUiProps
gm.numPlayers = 2
gm.cardArray = arrayOf("Card to be dealt", "Cards to be Dealt")
gm.playerArray = arrayOf("Player 1", "Player 2")
RBuilder.render(rootDiv) {
GameUi(gm)
}
}
My gm variable isn't being initialized and I can't figure how how to do it.
I need to initiazle my GameUi component with props, but I can't figure out how to do that.
So GameUiProps comes from my component
interface GameUiProps : RProps {
var numPlayers: Int
var playerArray: Array<String>
var cardArray: Array<String>
}
class GameUi(props: GameUiProps) : RComponent<GameUiProps, RState>(props) {
private fun RBuilder.createHands() {
var numPlayers = props.numPlayers
val handArray: ArrayList<Unit> = ArrayList()
while (numPlayers > 0) {
handArray.add(
handUi(
props.playerArray[numPlayers],
props.cardArray[numPlayers]
)
)
numPlayers--
}
}
override fun RBuilder.render() {
div {
h1("sweet") {
+"RKT"
}
div {
createHands()
}
}
}
}
Your GameUiProps is just an interface, so you cannot instantiate it directly.
What you have (val gm: GameUiProps) is just field declaration without assigning anything to it. You obviously cannot access nor write any properties to it.
You need to either create a simple implementation with constructor, or an anonymous object.
Class that declares all interface fields in the constructor:
class GameUiPropsImpl(
override var numPlayers: Int,
override var playerArray: Array<String>,
override var cardArray: Array<String>) : GameUiProps
then in your fun main:
val gm = GameUiPropsImpl(2, arrayOf("Player 1", "Player 2"), arrayOf("Card to be dealt", "Cards to be Dealt"))
Or using an anonymous class implementing the interface:
val gm = object: GameUiProps{
override var numPlayers = 2
override var playerArray = arrayOf("Player 1", "Player 2")
override var cardArray = arrayOf("Card to be dealt", "Cards to be Dealt")
}
It has been a while, but for anyone who might need this, you just need to assign the variable gm to an object of type GameUiProps. Below is what I would have done.
val gm = object : GameUiProps{
override var numPlayers = 2
override var cardArray = arrayOf("Card to be dealt", "Cards to be Dealt")
override var playerArray = arrayOf("Player 1", "Player 2")
}
Hope it helps!
Short example:
class MyClass {
val someName = "want this value"
val someOther = SomeOther().apply{ someName = someName }
// other stuff below
}
SomeOther will apply the value of its own someName to someName, so value application makes no difference (x=x).
Q: How can I access external someName ("want this value") being inside apply?
UPDATE
I have further doubts related to suggestion to use this.someName=someName, below 2 code snippets, the first one works as expected, surprisingly the second fails with similar behavior as described.
First
fun main(args: Array<String>) {
class SomeOther {
var someName: String? = null
}
val someName = "want this value"
print(SomeOther().apply { this.someName = someName }.someName) // works!
}
Second
class SomeOther {
var someName: String? = null
}
class MyClass {
val someName = "want this value"
val someOther = SomeOther().apply { this.someName = someName }
fun go() = print(someOther.someName)
}
fun main(args: Array<String>) = MyClass().go() // prints null
Q: Where's the difference?
You could use the also-function instead. It is equivalent to apply, except it will bind your object to it instead of this:
val someName = "want this value"
val someOther = SomeOther().also { it.someName = someName }
The also-function was added to Kotlin 1.1 specifially for when you do not want to shadow this from the outer scope.
using this reference expression as below:
val someOther = SomeOther().apply { someName = this#MyClass.someName }
// reference to the outer class ---^
AND the T.apply function is a convenient way to applying Builder Design Pattern, in this way you never need using this or additional parameter at all, for example:
val foo = Foo().apply {
//v--- no need using `this` or any addition parameters
foo = "bar"
fuzz = "buzz"
}
class Foo {
lateinit var foo: String;
lateinit var fuzz: String
}
Edit
you can assuming apply(lambda) which will apply an anonymous class of Function2<T,ARG,T> instance, then you know why immediately?
in your first approach it looks like as below:
val lambda: Function2<SomeOther, String, SomeOther> = { thisRef, arg ->
thisRef.someName = arg;
// ^--- parameter is used in lambda
thisRef
}
val someName = lambda(SomeOther(), "want this value").someName
println(someName)
in your second approach it looks like as below:
class MyClass {
val lambda: Function2<SomeOther, MyClass, SomeOther> = { thisRef, arg ->
// the parameter `arg` is never used in lambda ---^
thisRef.someName = thisRef.someName
// ^--- it use thisRef's someName rather than arg's
thisRef
}
val someOther = lambda(SomeOther(), this)
}
You can access out of apply like that
class SomeOther {
var someName: String? = null
}
class MyClass {
val someName = "want this value"
val someOther = SomeOther().apply { this.someName = this#SomeOther.someName }
fun go() = print(someOther.someName)
}
Try this:
val someName = "want this value"
val otherName = SomeOther().apply { this.someName = someName }
// internal someName ---^ ^
// external someName ---^
print(otherName.someName) // >>> want this name