13

I was playing with Apex and stumbled upon this.

Created an Abstract Class and made the constructor as private, so that noone can create an instance of it. This works just fine during compilation. it stops me from using new AbstractClass(). Which is awesome.

Then I thought can I use JSON Deserialize to create an instance of the abstract class with private constructor, and I was able to.

public  Abstract class  MyAccountWrapper {

    public  String abc;

    private  MyAccountWrapper(){
        System.debug(abc);
        System.debug('Constructor called');
    }



}

Test:

MyAccountWrapper myc = (MyAccountWrapper)JSON.deserialize('{"abc":"JAR"}',MyAccountWrapper.class);

System.debug(myc.abc); // Prints "JAR"

I also noted that Constructor was not called, thus the private bit of constructor didn't came into the picture during deserialization.

Is it SF bug or working as designed?

Also, When I try that as Interface instead of Abstract class, I get an exception.

Error on line 15, column 1: System.TypeException: Cannot deserialize JSON as abstract type: MyAccountWrapper
Class.System.JSON.deserialize: line 15, column 1

My understanding is Abstract class and Interfaces can't be instantiated. Which does not hold true for Abstract class.

Can someone shed some light?

  • This is interesting. At least I can say from Java perspective, an abstract class can never be instantiated. Even if in this scenario let's say the constructor was never called, wonder how did the deserialization work at runtime. – Jayant Das Feb 13 at 14:22
  • 3
    try JSON.deserialize('{"abc":"JAR"}',WeirdInvalidClassname.class);, worked for me 2 months ago, wasted 1 day on the typo – kurunve Feb 13 at 14:29
13

Edit: This is now explicitly blocked in a critical update that prevents one from deserializing a JSON object into an abstract class. See also @ca_peterson's answer, Tweet, and GitHub comment, and the Release Notes/Critical Update.

Future Readers: If there is not a link to the Critical Update/Release Notes announcing this feature, please feel free to comment or edit a link in to this answer (see [4]: at the bottom of this post in edit mode).


Original Answer

abstract is a compiler-enforced limitation. The runtime does not explicitly check for abstract or read-only properties for performance reasons. JSON.deserialize is a common trick we've used for ages to mock an sObject with read-only fields, etc. Just be aware that if you do use this trick, be very careful, as you'll run into exceptions if you try to call an abstract method. Interfaces can't be deserialized because they're not "real" classes in the same sense. Technically, this is "working as designed," but you should avoid abusing this feature any more than necessary.

  • Runtime actually does check this enforcement. If you attempt MyAccountWrapper a = new MyAccountWrapper(), you won't be able to initialize the class. It seems it's just a special behavior how JSON.deserialize works. – Jayant Das Feb 13 at 14:25
  • 2
    @JayantDas The compiler stops you from writing that line of code, not the runtime. Your code will never run because of the compilation error. – sfdcfox Feb 13 at 14:27
  • Well, I was testing this in anonymous window. Unless JSON.deserialize is designed to work that way, ideally it should have been detected at compile time as well. – Jayant Das Feb 13 at 14:30
  • 2
    @JayantDas I just messaged Chris on Twitter, too. He always has the most delightful insights. – sfdcfox Feb 13 at 14:41
  • 1
    Great. This will be interesting to hear about. I had twitter opened, was just wondering whom to tweet :) @PranayJaiswal very interesting find! – Jayant Das Feb 13 at 14:42
5

Salesforce bug. We're fixing this in Spring '20 via the "Restrict Reflective Access to Non-Global Controller Constructors in Packages" critical update.

As noted in the comments to @sfdxfox's answer, similar issues exist with Type.newInstance, which this critical update also resolves, as well as removing legacy versioned behavior with these tricks that was... strange.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.