I have a class that needs to be passed the type of an element, so I can later check if another object has either the type or is a subclass of this type and add it to an internal collection.
I have an initialize:
method that gets called from the new:
ctor of my class:
initialize: aType
elements := OrderedCollection new.
type := aType class.
Now I have a method that gets passed a value and should check if the types are compatible:
add: anElement
type isNil ifTrue: [ elements add:anElement. ^self. ].
(anElement isMemberOf: type)
ifTrue: [elements add:anElement.]
ifFalse: [ ^ 'Not supported!' ].
This works if I want to check for a concrete type:
|myClass|
myClass:= MyClass new: '123'.
cc add: '5.4'. "Works"
cc add: 123. "Fails correctly."
Now, to check if it's a derived type I modified the add:
method:
add: anElement
type isNil ifTrue: [ elements add:anElement. ^self. ].
(anElement isKindOf: type)
ifTrue: [elements add:anElement.]
ifFalse: [ ^ 'Not supported!' ].
However, this doesn't work:
|myClass|
myClass:= MyClass new: 5 asNumber.
myClass add: 5.4. "Fails, although Float is a sub type of Number"
I suspect that my initial method of determining the type of an object (aType class
) is wrong, but I can't find a better, or more explicit way of determining the type. Basically, I'm looking for something like typeOf(MyObject)
in C#. This is part of an exercise, so please excuse the contrived example :)
As I mentioned in a comment to your question, the problem is that 5 asNumber
is 5
which is an instance of SmallInteger
, not an instance of Number
. Thus, when you initialize:
your class with 5
what you get in the ivar type
is SmallInteger
. And then, when you add: 5.4
, the check becomes 5.4 isKindOf: SmallInteger
, which naturally fails.
I think that the problem originates in the way you have chosen to initialize the instance. A simpler approach would be to explicitly set the target type
with a class, not an instance. Something on the lines of
initialize: aClass
elements := OrderedCollection new.
type := aClass
Then, your example will be something like
|myClass|
myClass:= MyClass new initialize: Number.
myClass add: 5.4.
which would accept 5.4
as an element because it is a Float
, which isKindOf: Number
.
Now let me add another remark. The usual semantics of new:
is different from the one you used. The argument of new:
is usually an Integer
and such and integer expresses the desired size of the new instance. For example, you say Array new: 3
when you want an Array
with 3
entries, etc. It is not expected for new:
to receive other kind of parameter for the construction of the object. I'm not saying it's forbidden, just that is not the usual naming convention. In your case I would suggest a method for instance creation such as
MyClass class >> on: aClass
^self new initialize: aClass
and your code would look like
| sequence |
sequence := MyClass on: Number.
sequence add: 5. "ok, 5 isKindOf: Number"
sequence add: 4.5. "ok, 5.4 isKindOf: Number"
sequence add: 'hello world' "fail, not a Number"