Abstract Classes and Methods

Primary tabs

I have been looking into using abstract classes and methods. In other languages, if a superclass is defined as abstract, subclasses inheriting from the superclass will not compile until all abstract methods have been implemented in the subclass.
I have created the following classes to try this in Cache:
Class User.Music.AbstractClass [ Abstract ]
{

             Method testabs(inp As %String) [ Abstract ]{}

}

Class User.Music.AbstractTest Extends (User.Music.AbstractClass, %Persistent)

{    

           Method testabs(inp1 As %Integer!!,"Input:",$g(inp,"undefined"}

}

When I compile User.Music.AbstractTest it errors because the datatype of parameter inp1 doesn't match the superclass. On changing the parameter to %String it compiles ok (this makes sense to me).

However, if I comment out the implemented method  testabs() from AbstractTest it compiles successfully and the object can be instantiated. But according to my understanding, now that testabs() has not been implemented it should either error in compile or when instantiated.

Also, if I add another parameter in addition to the parameters listed, this also seems to be ok. 

Have I misunderstood how abstracted classes/methods work? How do others use abstracted classes/methods?

Many thanks.

  • 0
  • 0
  • 112
  • 8

Replies

Hi Ken

In ObjectScript an abstract class simply means that you can not instantiate it.   You MUST subclass it and instantiate the subclass.

The concept you understand is supported in Objectscript but it requires that you declare the method itself as abstract.

The two (abstract class keyword and abstract method keyword) work independently of each other.

Does this explain what you are seeing better?

Hi Jenna,

Thanks for your response.

Fully understand your response but unless I am doing something wrong, Cache does not seem to throw an error/warning if the subclass does not implement the abstract methods defined in the superclass.

My understanding is that inherited abstract methods in subclasses must be implemented (even if the implemented method is empty) to standardise class interfaces but in my code this does not seem to be being enforced.

Thanks again,

Ken

You can easily add your own compile-time check via method generators. Here's an example which checks that all abastract methods are implemented.

Class Test.AbstractChecker
{

ClassMethod Check() As %Status [ CodeMode = objectgenerator, ForceGenerate ]
{
    #Dim sc As %Status = $$$OK
    
    // Get class name from %compiledclass object which is an instance of a currently compiled class
    Set class = %compiledclass.Name

    // Iterate over class methods.
    // You can also use %class object to iterate
    Set method=$$$comMemberNext(class, $$$cCLASSmethod, "")
    While method'="" {
        
        // Get mthod abstract state
        Set abstract = $$$comMemberKeyGet(class, $$$cCLASSmethod, method, $$$cMETHabstract)
        
        // Quit iteration when we find any abstract compiled method
        If abstract {
            set origin = $$$comMemberKeyGet(class, $$$cCLASSmethod, method, $$$cMETHorigin)
            Set sc = $$$ERROR($$$GeneralError, $$$FormatText("Abstract method %1 in class %2 not implemented (origins from %3)", method, class, origin))
            Quit
        }
        
        // Get next method
        Set method=$$$comMemberNext(class, $$$cCLASSmethod, method)        
    }
    Quit sc
}

}

After adding this class to inheritance I get an error on compilation:

Eduard,

Many thanks for the code it does what I am looking for. Do you know if there are plans for the compiler to check for the implementation of abstract methods instead of having to inherit code to do this?

A couple of things I have noticed:

  • If the method is inherited in a Persistent class, it won't compile.
  • It looks as though it checks for the name of the abstract method but not the number of parameters the method requires.

But otherwise that is what I am looking for, I just have to remember to add the AbstractChecker to the inheritance list.

Thanks again.

If the method is inherited in a Persistent class, it won't compile.  

You can easily modify the method to ignore abstract method if they are system (start with %) or originate from system classes (start with %).

It looks as though it checks for the name of the abstract method but not the number of parameters the method requires.

You can modify checker to check whatever you need.

Also, if I add another parameter in addition to the parameters listed, this also seems to be ok.  

It is, inherited method can accept more arguments than a parent method.

Do you know if there are plans for the compiler to check for the implementation of abstract methods instead of having to inherit code to do this?

Please file a WRC issue.

Thanks again for the code, it does exactly what I am looking for.  However, I have amended the code to list all of the abstract methods required to be implemented for the subclass in the error message.

Class User.Abstract.AbstractChecker
{
ClassMethod Check() As %Status [ CodeMode = objectgenerator, ForceGenerate ] {
    k absCheck
    #Dim sc As %Status = $$$OK
    
    // Get class name from %compiledclass object which is an instance of a currently compiled class
    Set class = %compiledclass.Name
    
    ; get the abstract methods
    set methodCnt=..FindAbstractMethods(class,.methods)
    
    ; check whether the abstract methods have been implemented
    set methodCnt=$order(methods(""),-1)
    for i=1:1:methodCnt {
        set method=methods(i)
           set origin = $$$comMemberKeyGet(class, $$$cCLASSmethod, method, $$$cMETHorigin)
           set errtext=$$$FormatText("%1 [abstract] (origin %2)", method,origin)
           ; build array of error messages
           set absCheck("AbsNotImp",$order(absCheck("AbsNotImp",""),-1)+1)=errtext
    }
    
    ; if there are error messages build the error string
    set str=""
    if $data(absCheck("AbsNotImp")) {
        set ind="",ind=$order(absCheck("AbsNotImp",ind))
        while ind {
            set $piece(str,", ",ind)="("_ind_") "_$get(absCheck("AbsNotImp",ind))
            set ind=$order(absCheck("AbsNotImp",ind))
        }
        set str="In class '"_class_"' the following "_$order(absCheck("AbsNotImp",""),-1)_" abstract classes are to be implemented: "_str
        set sc=$$$ERROR($$$GeneralError,str)
    }
    quit $get(sc)
}

ClassMethod FindAbstractMethods(class As %String, ByRef methods As %String) As %Integer {
    kill methods
    // Iterate over class methods.
    // You can also use %class object to iterate
    Set method=$$$comMemberNext(class, $$$cCLASSmethod, "")
       
    While method'="" {
        // Get method abstract state
        Set abstract = $$$comMemberKeyGet(class, $$$cCLASSmethod, method, $$$cMETHabstract)
        if abstract{
            set methods($order(methods(""),-1)+1)=method
        }
        Set method=$$$comMemberNext(class, $$$cCLASSmethod, method)
    }    
    quit +$order(methods(""),-1)
}
}

I'd use $lb instead of local as you don't store anything in subscripts anyway.

HI, Coming from Java, we expect cache to behave same way.  However they are two different languages and I learnt that over time.
Both are beautiful languages .