Class Extends bug, using inside a stack ?

Monkey Forums/Monkey Programming/Class Extends bug, using inside a stack ?

GC-Martijn(Posted 2015) [#1]
H!

I'm able to push new objects to a stack, but they don't find the methods.
Its maybe not as strict as it needs to be ?

Class Scene
	Field sprites:Stack<Sprite> = New Stack<Sprite>()

	Method Init:Void()
		sprites.Push(New World())  ' <--- WORKS, but maybe its better that its not working'
		sprites.Push(New Player()) ' <--- WORKS'

		For Local sp:Sprite = Eachin sprites
			sp.Load() ' <--- DON't WORK Error : Identifier 'Load' not found.
		Next
	End
End

Class Sprite
 ' sprite things AND no Load() method, don't need it here)
 	Method MoveTo:Void(_mx:Float, _my:Float)
	End
End

Class Player Extends JSONLoader
 ' player things '
	Method Load:Void()
                ' other player 'init' stuff
		LoadAtlas("data.json")
	End
	Method MoveTo:Void(_mx:Float, _my:Float)
		Super.MoveTo(_mx,_my) ' WORKS '
	End
End
Class World Extends JSONLoader
 ' world things '
	Method Load:Void()
		' other 'world' init  stuff
		LoadAtlas("data.json")
	End
End

Class JSONLoader Extends Sprite
	Method LoadAtlas:Void(inpFile:String,imgHandler:Int=0)
	End
	Method LoadOtherBla:Void()
	End
End


Not using the stack and it works, but I want to add all the 'sprites' inside a 'list' to manage them all.


MikeHart(Posted 2015) [#2]
Did you use STRICT?

Normally you have to cast them to their original type to have access to their own methods. The class Sprite has no Load method, that is why it isn't working.


GC-Martijn(Posted 2015) [#3]
Yes using strict.
dint know casting was possible in monkey. Going to check that


Samah(Posted 2015) [#4]
To be fair, there's nothing in the online documentation regarding casting.


muddy_shoes(Posted 2015) [#5]
While learning about casting is a good thing, casting is really not the way to resolve your issue based on the code you posted. If you're storing a set of "Sprites" and then believe you should be able to iterate over that set and call the Load method on them then, despite what your code comment says, you do need to have the Load method defined on Sprite. Your code implicitly states that loading is something that Sprites do.


ImmutableOctet(SKNG)(Posted 2015) [#6]
Adding to what muddy_shoes said, you could always make 'Sprite' an abstract class, or make 'Load' abstract. The former case being to make sure objects of that type can not be created (So, you could provide a "blank" implementation, and inheriting classes could override that). Or, if 'Load' is abstract, you wouldn't have it explicitly implemented in 'Sprite', but inheriting classes like 'Player' and 'JSONLoader' would need to either be abstract, or implement that method.


GC-Martijn(Posted 2015) [#7]
@Samah
Field sprites:Stack<Sprite> = New Stack<Sprite>()
sprites.Push(New Player())
sprites.Push(New World())
For Local sp:Sprite = Eachin sprites
			If Player(sp)
				Local ItsAPlayer:Player = Player(sp)
				ItsAPlayer.Load()
			End
			If World(sp)
				Local ItsAWorld:World = World(sp)
				ItsAWorld.Load()
			End
		Next


Player and World have extends Sprite
But maybe (not tested) this is working if they don't
Field sprites:Stack<Object> = New Stack<Object>()
sprites.Push(New Player())
sprites.Push(New World())
For Local unknownObject:Object = Eachin sprites
			If Player(unknownObject)
				Local ItsAPlayer:Player = Player(unknownObject)
				ItsAPlayer.Load()
			End
			If World(unknownObject)
				Local ItsAWorld:World = World(unknownObject)
				ItsAWorld.Load()
			End
		Next


The reason for this setup was because I want clean Classes, without things I don't need in it.
I will try to translate "Or, if 'Load' is abstract, you wouldn't have it explicitly implemented in 'Sprite', but inheriting classes like 'Player' and 'JSONLoader' would need to either be abstract, or implement that method." Into code.
Don't know exactly how.


GC-Martijn(Posted 2015) [#8]
Oke if this was not hard at all, I can live this this setup.
Class Sprite Abstract
	Method Load:Void() Abstract
.....
sprites.Push(New Player())
sprites.Push(New World())
For Local sp:Sprite = Eachin sprites
  sp.Load()
Next


So now everything is oke, and everyone is happy with my code style ;)


Samah(Posted 2015) [#9]
I think you need to rethink your class hierarchy.

This is what you currently have:
Sprite
	JSONLoader
		World
		Player

This states that all JSONLoaders are Sprites. What if I want to make a JSONLoader that isn't a Sprite? You need to think about what concepts your classes represent, and how best to write it in English (or any spoken language).

Here's how I would logically explain the kind of thing you want:
1. All sprites should have a position, texture, etc.
2. The world and player have a position and texture.
3. Some objects need to be able to load JSON files.

From 1 and 2 you can immediately see that World and Player should inherit the properties of Sprite.
As for 3, it's quite possible that there are parts of your game that should load JSON files that DON'T need a position and texture. However, you still want World and Player to be able to load themselves. This is where you could use an interface to represent that World/Sprite "know how to load a JSON file".

Class structure:
Sprite
	World
	Player


And now, apply an interface to World and Player:
' interface represents "the class knows how to load itself"
Interface JSONLoader
	Method Load:Void()
End

Class Sprite
	' sprite properties like position and texture
End

Class World Extends Sprite Implements JSONLoader
	Method Load:Void()
		' load the world
	End
End

Class Player Extends Sprite Implements JSONLoader
	Method Load:Void()
		' load the player
	End
End


And now:
Field sprites:Stack<Sprite> = New Stack<Sprite>()
sprites.Push(New Player())
sprites.Push(New World())
For Local sp:Sprite = Eachin sprites
	If JSONLoader(sp)
		JSONLoader(sp).Load()
	End
Next


Read the documentation for more info on interfaces.

Note: Realistically you'd probably keep a separate list of things to load and add your World/Player to that so that you don't need to cast.


GC-Martijn(Posted 2015) [#10]
I fun part is, that a month ago I started with Intefaces but it din't work like I thought is has to (poor documentation)
I searched the forum and found some examples, but now it makes sense the way you explain it (using my code example)

That being said, and i'm asking this before I did test it because i'm not at home right now ;(

Using
Interface JSONLoader
	Method Load:Void()
End

Destroys in this example my JSONLoader class, so I guess its like this.

Interface IJSONLoader
	Method Load:Void(path:String)
End

Class JSONLoader
 ' things
      Method Load:Void(path:String)
		' here we go
                
	End
End

Class World Extends Sprite Implements IJSONLoader
        Field theActualJSONLoader:JSONLoader = New JSONLoader()
	Method Load:Void(path:String)
		' load the world
                theActualJSONLoader.Load(path)
	End
End

Field sprites:Stack<Sprite> = New Stack<Sprite>()
sprites.Push(New Player())
sprites.Push(New World())
For Local sp:Sprite = Eachin sprites
	If IJSONLoader(sp)
		IJSONLoader(sp).Load("data.json")
	End
Next


Because this is of course not possible

Interface JSONLoader
        Method Load:Void(path:String)
		' here we go
	End
End

Class World Extends Sprite Implements JSONLoader
	Method Load:Void(path:String)
		' load the world
	End
End

Field sprites:Stack<Sprite> = New Stack<Sprite>()
sprites.Push(New Player())
sprites.Push(New World())
For Local sp:Sprite = Eachin sprites
	If JSONLoader(sp)
		JSONLoader(sp).Load("data.json")
	End
Next