I'm trying to create classes from an external definition stored in a JSON file. I can create a class easily with a message like:
name := 'SomeNewClass'
Object subclass: name asSymbol
instanceVariableNames: ''
classVariableNames: ''
package: 'SomeNew-Package'.
This works fine the first but if I run my code again it fails as the class already exists. I've been looking but I can't figure out how to check if a class exists and to remove it if it does?
Your code shouldn't fail. The problem you see must have a different cause, let me explain why.
In (every) Smalltalk classes can be redefined for the purpouse of modifying them. Typically you will start with a simple approach on the lines of
Object subclass: #Volcano
instanceVariableNames: ''
classVariableNames: ''
package: 'SomeNew-Package'
add some methods and tests and then realize you need an instance variable or two
Object subclass: #Volcano
instanceVariableNames: 'location active'
classVariableNames: ''
package: 'SomeNew-Package'
In doing this, the very same class object Volcano will remain the same and your instances (if any is around) will acquire two new slots (one for location and the other for active).
More generally, the capability of redefining a class includes all operations you might need for evolving your model:
Add/Remove/Rename an instance variable
Add/Remove/Rename a class variable
Add/Remove/Rename a class instance variable
Change the superclass
Rename the class
Move the class to another package
None of these operations will interpret that the class already exists and cannot be redefined. On the contrary, there is a lot of infrastructure at your disposal to enable all of them in a meaningful way, which not only will reshape your class but its instances and subinstances too.
Therefore, your particular problem needs another diagnostic. Tell us what "failure" you get so we can better understand what's happening.
Trivial comment: A dot is missing in your code snippet for separating the two statements. It should read
name := 'SomeNewClass'. "<- dot added"
Object subclass: name asSymbol
instanceVariableNames: ''
classVariableNames: ''
package: 'SomeNew-Package'. "<- final dot is ok but not required"
BTW, you could have written this in a single statement:
Object subclass: #SomeNewClass
instanceVariableNames: ''
classVariableNames: ''
package: 'SomeNew-Package'
To see if a global (class) exists, try Smalltalk includesKey: aSymbol. To remove a global, try Smalltalk removeKey: aSymbol. (Note that there may be another way to get to the system dictionary. The above code is from memory and hasn't been tried.)
Related
I created a class named Die (singular of Dice) and set an instance variable side.
Object subclass: #Die
instanceVariableNames: 'side'
classVariableNames: ''
package: 'MyPackage'
I initialized the side variable to 6 by default:
initialize
side := 6.
However I would like the ability to change the instance variable after the object has been created, like so:
myDie := Die new.
myDie side: 10. " it doesn't to work "
It seems to me that instance variables can only be changed by class methods. Is there a way to change it like above?
Also, I have a method roll that returns a random number between 1 and the specified side. If I create two dice and want to roll two dice at the same time, or maybe three dice at the same time, should I create a different class, something like papercup feed that with the number of dices and implement the roll method on the papercup instead?
Thank you.
You are on a right track. When you execute
myDie side: 10.
You send a message side: to myDie which is an instance of a class Die. To "respond" to this message, the class die (or its superclasses) should implement a side: method. Ideally, this method is going to look like this:
side: aNumber
side := aNumber
You see, pharo takes encapsulation very seriously, so instance variables can be directly accessed only by the object itself, and not by anyone from the outside. This is why you always have to create accessors, which is a good practice followed to a various extent by many programming languages.
Regarding your second question: it's a good idea to have a Papercup class, but if you don't need extensive features, the exact task can be achieved by the following code:
papercup := OrderedCollection new.
papercup
add: die1;
add: die2;
add: die3.
papercup collect: #roll
This way you will roll all the 3 dies and return a collection for 3 numbers (collect: is the same as map() in many programming languages). I provided a shorthand version, whereas the full way to write a collect is: papercup collect: [ :die | die roll] when you specify a complete block (closure) and send the roll message to each die.
I am using newest Delphi 10.3.3
I have several main classes, which are extended from same parent class and also some, let's call it, Reflection classes, which has also same parent. I want instance of main class to have link to corresponding reflection instance, but I run to this issue (I simplified it to this example):
Main classes:
TMainClass = class
Link: TReflectionClass;
end;
TCarMainClass = class(TMainClass)
Link: TCarReflectionClass;
end;
Reflection classes:
TReflectionClass = class;
TCarReflectionClass = class(TReflectionClass);
The problem lies in Link field. I want Link field in TCarMainClass to be directly defined as TCarReflectionClass to avoid type-casting on lot of pieces of code (and also risking some errors), however if I define class like this, TCarMainClass.Link just hides TMainClass.Link - it is defined as different field with same name. That is not good because it consumes extra memory and mainly I can't access that field from parent class, which I want (as pointer to generic instance).
Of course I can solve this by making field private of common type and define property setter/getter which handles re-typing in each class. But that is lot of extra code for these classes and also overhead on each get/set because of calling getter/setter methods.
My question is - is there some easy hack I missed, to tell compiler I want field in child class to occupy same memory location as certain field in parent class?
Thanks
and mainly I can't access that field from parent class
While it's true that parent class can't access the descendant's field, there is nothing preventing you from synchronizing the 2.
procedure TCarMainClass.SetLink(const Value : TCarReflectionClass);
begin
FLink := Value;
TMainClass(Self).FLink := Value;
end;
Now, if you absolutely require no additionnal memory use and no setters, the only remaining option (that I can think of at this time) is as GolezTrol suggested, generics.
TMainClass<T : TReflectionClass> = class
Link: T;
end;
TCarMainClass = class(TMainClass<TCarReflectionClass>)
end;
But that would probably break your design though, because TCarMainClass would be incompatible with TMainClass<TReflectionClass>. (if you want to understand why, search for the terms covariance/contravariance).
I know there are various capabilities in Java with reflection.
For example:
Class<?> clazz = Class.forName("java.util.Date");
Object ins = clazz.newInstance();
I wonder if I could pass class dynamicaly in some method declaration in <> tags (or there is other way to do it if it must be fixed). I would like to change that class declaration dynamicaly; because I would like to write generic method for all types of classes.
In there, I have this:
List<Country>
Can I write it something diffrent with reflection? For example can it be somehow be achieved to pass class as parameter (or how else should be this done):
List<ins>
? I would appreciate examples.
This cannot be done because generics are a compile time feature. Once code is compiled, the only place where generics are exists are at method signatures, and they are only used for compiling new code.
When working with reflection, you are basicly working with raw types, and need to code according to that, that means, you can cast the returned result of newInstance() to the list type your need, for example:
List<Country> ins = (List<Country>)clazz.newInstance();
This is a safe operation to do, because you know at that point its empty, and isn't passed to any outside code.
I don't think this is possible. Generics in Java are implemented in a way that prohibits runtime access.
Generics are there so that the compiler can verify correct typing, but are no longer present at runtime (this is called "type erasure"). Reflection deals with the runtime representation of types only. As far as I know the only case where reflection has to deal with generics is to find out "fixed" type parameters of sub-classes, e.g. when you have class Bar<T> and class Foo extends Bar<String>, you can find out that the T of Bar is fixed to String in Foo using reflection. However, this is information found in the class file, too. Except that, reflection can only see or create raw-types.
How can I validate a parameter as a class object? For example, if I want to validate aparameter as string I could write param1 isString. Is there something like isClass?
As others have pointed out, isKindOf: and isMemberOf: are your friends when you're trying to figure this kind of thing out, but calling those methods is usually a kind of code smell. There are almost always better ways to do this, which I'll break into two categories:
Implement the method on all relevant classes, doing the right thing on each. For example, if I were writing a video game, then, rather than testing what kind of object I'm getting it and telling it what to do next, I can instead just implement a performNextStep function on all game objects, then leave it to each object to figure out what it ought to do.
Test for functionality, not class membership. Rather than checking whether something isKindOf: a class, instead check whether it respondsTo: aMethod, and call it if it does. This also means that classes that have acquired a valid method, but are not in the initial hierarchy you were anticipating, can still be passed around—doubly important if you're using Traits, where there may not be a single class hierarchy to test with.
You can test whether your parameter inherits from Class or Metaclass:
String isKindOf: Class orOf: Metaclass => true
"If you don't want Metaclasses, simply use isKindOf: Class"
String class isKindOf: Class orOf: Metaclass => true
'foo' isKindOf: Class orOf: Metaclass => false
However, it might be preferable to implement the operation you want to perform on Class (and any other relevant objects) itself, so you can just perform someOp without actually validating your inputs.
If your input is a String and you want to get the Class with the according name (if it exists), you can use:
Smalltalk classNamed: 'String'
I am using classes and static methods to 'scope' functions in a namespace, similar to C#. However, every time I add a new method to a class, at first it is not found. I have to restart the MATLAB environment (2007a) for the new methods to be recognised.
Surely there is an 'update' or 'refresh' type command that I can use so that I do not have to restart the MATLAB environment each time I add a function?
Issuing this call to CLEAR should do it:
clear classes
One unfortunate side effect of this is that it also effectively issues a clear all, which clears all of the variables in the workspace as well (however, this would happen anyway when you close and restart MATLAB). This clearing of the workspace actually serves a purpose, since it will remove any variables of the same type as the old version of your class, which potentially wouldn't work correctly with the new version of your class.
The function REHASH may work, but I doubt it (I think it deals more with file paths than class definitions).
Clearing instances of your class should work.
Suppose that you have an instance of "MyClass" in your base workspace:
foo = MyClass;
Now, suppose you edit MyClass and add new static method "bar":
foo.bar(); % Will cause error, as foo is instance of previous "MyClass"
However, "clear"-ing foo will remove the reference to the previous class:
clear('foo');
foo = MyClass;
foo.bar(); % this should now work.
This should be fine if you only have one or two instances of the class in your base workspace. If you have many instances of the class in your base workspace, then you may want to write a script to clear them:
varList = whos;
for iVar = 1:numel(varList)
if isequal( 'MyClass', varList(iVar).class )
clear( varlist(iVar).name );
end
end
clear('varList');
clear('MyClass');
If you have instances of the class in more locations, you may wish to extend the script as appropriate.
The last call to clear the class name might only be necessary if you are making modifications to classes in an inheritance hierarchy.
try "clear classname"