Casting question

Monkey Forums/Monkey Programming/Casting question

Grey Alien(Posted 2013) [#1]
Hi all, just checking that the following isn't supposed to work. I'm pretty sure it shouldn't work and I tested it to confirm that, but I needed a sanity check. Thx!

Let's say you have a class called A and you make a class called B extended from A.

Then you do this:

Local newB:B = B(new A)

newB ends up as null because the case failed because the code can't "upgrade" A into B right?

Here's an example. The print statement prints 1 because newB is null.

Strict

Import mojo

Function Main:Int()

	Local newB:B = B(New A)
	Print Int(newB = null)
	
	Return 0
End

Class A
	Field x:Int
End Class

Class B Extends A
	Field y:Int
End Class


Basically what I'm actually trying to do is create instances by cloning a template.

Normally I'd just use a prototype instance and clone that but the template is extended from another class and I need to clone values from the base class and the extended class. I hoped a simple cast could be applied to the base class instance before I copied the extended class's values to the instance, but realised that would probably fail (hence my test above).

So I figure I've either got to use a Factory Class which I pass into the base class when I clone it or use generics somehow.


Grey Alien(Posted 2013) [#2]
OK here's my Factory Class example. Am I doing this right or is it horrible? Note that the cast to B now succeeds:

Strict

Import mojo

Function Main:Int()

	Local newB:B = New B 'template
	Local newInstance:B = B(newB.Clone())
	 
	Print Int(newInstance=Null)
	
	Return 0
End

Class A
    Method Clone:A( f:Factory )
    	Local clone:A = f.CreateInstance()
    	'copy fields
    	Return clone
	End Method
End

Class B Extends A
    Method Clone:A( f:Factory = Null )
	If f=Null Then f = New BFactory
	Local b:B = B(Super.Clone(f))
    	'copy fields
	Return b
    End Method
End

Interface Factory
    Method CreateInstance:A()
End

Class AFactory Extends Factory

    Method CreateInstance:A()
        Return New A
    End
End

Class BFactory Implements Factory

    Method CreateInstance:A()
        Return New B
    End
End



Or maybe this where I'm using a different method called CloneB that returns B to avoid having to typecast outside of the clone method. Ideally I'd have just used a method called Clone:B( f:Factory = Null ) but the compiler won't let me override the bass class Clone method due to the different return type.

Strict

Import mojo

Function Main:Int()

	Local newB:B = New B 'template
	Local newInstance:B = newB.CloneB()
	 
	Print Int(newInstance=Null)
	
	Return 0
End

Class A
    Method Clone:A( f:Factory )
    	Local clone:A = f.CreateInstance()
    	'copy fields
    	Return clone
	End Method
End

Class B Extends A
    Method CloneB:B( f:Factory = Null )
	If f=Null Then f = New BFactory
	Local b:B = B(Super.Clone(f))
    	'copy fields
	Return b
    End Method
End

Interface Factory
    Method CreateInstance:A()
End

Class AFactory Extends Factory

    Method CreateInstance:A()
        Return New A
    End
End

Class BFactory Implements Factory

    Method CreateInstance:A()
        Return New B
    End
End




Goodlookinguy(Posted 2013) [#3]
newB ends up as null because the case failed because the code can't "upgrade" A into B right?


A is not B, but B is A. That's why you can cast a B instance up to A and from that down to B again, but cannot cast an A instance to B.

I believe that's the answer you're looking for, although to be honest, I wasn't sure what the question was.


Grey Alien(Posted 2013) [#4]
Yes that's the confirmation I needed. I was pretty sure that was the case but I saw some other code example (probably untested) on the forums using casting like that and got confused.

Still, the main issue now is, is my factory code good or horrible? How would you do it?


Goodlookinguy(Posted 2013) [#5]
I can't really comment on that question, as it's very opinionated. Whatever works, works. Don't get too worked up over something that's working. ;}


AdamRedwoods(Posted 2013) [#6]
does your question focus on this?
Local b:B = B(Super.Clone(f))

Yes, this is the way to do it.

It's too bad that monkey does not allow extending a base class method with a derived class.
Class A
  Method CloneMe:A()
    Return New A
  End
End

Class B Extends A
  Method CloneMe:B() ''Illegal in Monkey unless you return 'A', then cast
    Return New B
  End
End



NoOdle(Posted 2013) [#7]
Thought of an alternative approach, typed into browser so not tested, requires reflection.


Class BaseItem

	Method Init : BaseItem( copy : BaseItem ) Abstract


	Method Copy : BaseItem( copy : BaseItem )
		Local this : Object = GetClass( copy ).NewInstance()
		Return BaseItem( this ).Init( copy )
	End Method

	
End Class







Class ItemA Extends BaseItem
	
	Field x : int


	Method Init : BaseItem( copy : BaseItem )
	
		If copy <> Null
	
			Local this : ItemA = ItemA( copy )
		
			If this
				Self.x = this.x
				Return Self
			Endif
		
		Endif
		
		'// default init
		Self.x = 100
		Return Self
				
	End Method

End Class







Class ItemB Extends ItemA

	Field y : Int
	
	
	Method Init : BaseItem( copy : BaseItem )
	
		If copy <> Null
		
			Local this : ItemB = ItemB( copy )
			
			If this
				Self.x = this.x
				Self.y = this.y
				Return Self
			Endif
			
		Endif
		
		'// default init
		Self.x = 100
		Self.y = 150
		
		Return Self
		
	End Method
	
End Class



Grey Alien(Posted 2013) [#8]
Found this and will have a read: http://msdn.microsoft.com/en-us/library/orm-9780596527730-01-05.aspx


Grey Alien(Posted 2013) [#9]
@AdamRedwoods: Yeah, I tried doing it with method overloading but alas it was rejected so had to cast the return value instead.


Grey Alien(Posted 2013) [#10]
@NoOdle Interesting idea. Seems like it could work!