- changed version to Unassigned
Apex code generation
Add code generation options for:
- Constructors
- Getters/setters - DONE
- Implement/override methods from base class/implemented interfaces - DONE
equals()
/hashCode()
- DONE- Unit test classes/methods/setup
Comments (25)
-
reporter -
reporter Issue
#99was marked as a duplicate of this issue. -
reporter Issue
#596was marked as a duplicate of this issue. -
reporter - edited description
- changed version to 2.0
-
reporter Partially delivered in IC2. Implement/override methods is now available.
-
reporter - edited description
-
reporter - changed component to Code Generation
-
Bump - I really miss generator for constructors and I was on verge of submitting enhancement number of times already.
Thanks!
-
reporter This will be coming relatively soon. I need to wrap up some LWC enhancements and then will be moving back to more refactorings, code inspections/intentions, and code generation.
-
Kindly reminder
-
reporter Piotr (and anyone else), is there a specific subset of this that you were looking for? The main one I'd heard about from people was implement/override so I knocked that one out. I'm happy to revisit high priority subsets of this.
-
I think the constructor generation is the main one on my mind - to be able to select class fields for a constructor.
-
reporter Gotcha. Anyone else have a favorite code generation aspect that's not yet implemented?
-
In addition to Piotr’s request when a constructor already exists add a field to the constructor(s) and assign it?
In the example below you would have an option in the context menu of the field to “add to Constructor(s)”
Ideally it would give you the method dialog with the ability to change the order of the current parameters and then open the refactor dialog to inspect the references? Not sure how this should be handled but I seem to remember this functionality existing in IntelliJ Idea for Java?
e.g.
public class TestClass { private String myField; public TestClass(){ System.debug('Some line in class'); } }
would go to
public class TestClass { private String myField; public TestClass(String myField){ System.debug('Some line in class'); this.myField = myField; } }
-
reporter Issue
#1888was marked as a duplicate of this issue. -
Account Deactivated Some additional improvements currently supported by Java that would be nice:
- Getter
- Setter
- Getter and Setter
- equals() and hashCode()
Thanks!
-
Hello @Scott Wells ,
Would it be possible to consider the comment above?
At least equals() and hashCode() would be really a good nice to have as it’s really a waste of time for custom objects:
Thanks
-
Account Deleted +1 for generate constructor, that’s the one I miss the most
-
Issue
#1286was marked as a duplicate of this issue. -
reporter Okay, I’m finally putting some significant effort into new Apex code generation capabilities. I’m starting with
equals()
andhashCode()
generation, but rest assured that I do plan to implement code generation for constructors and field getters/setters as well…and maybe even some code intentions to convert fields to properties and vice-versa.I thought I’d solicit some feedback from folks here about the generated implementations of
equals()
andhashCode()
. In Java, there are quite a few libraries to assist with those--Apache Commons, Guava, and even standard in more recent versions of the JDK--but in Apex (ignoring third-party libs), the only options areSystem.equals()
andSystem.hashCode()
. Those have significant limitations, though, as the former requires the first parameter to be non-null and the latter requires its one parameter to be non-null. Also,System.equals()
has the same behavior as the == operator in Apex, so equivalence checks don’t necessarily behave in the expected manner, specifically case-insensitive comparison ofString
values.Right now I’m generating code like the following:
public with sharing class CodeGenerationTest { private String firstName; private String lastName; private Date dob; private Integer age; private Boolean licensed; private Account account; public Boolean equals(Object o) { if (this == o) return true; if (!(o instanceof CodeGenerationTest)) return false; CodeGenerationTest that = (CodeGenerationTest) o; return firstName.equals(that.firstName) && lastName.equals(that.lastName) && System.equals(dob, that.dob) && (((age != null) && System.equals(age, that.age)) || ((that.age != null) && System.equals(that.age, age)) || (age == that.age)) && System.equals(licensed, that.licensed) && (((account != null) && System.equals(account, that.account)) || ((that.account != null) && System.equals(that.account, account)) || (account == that.account)); } public Integer hashCode() { Integer result = System.hashCode(firstName); result = (31 * result) + System.hashCode(lastName); result = (31 * result) + System.hashCode(dob); result = (31 * result) + (age != null ? System.hashCode(age) : 0); result = (31 * result) + System.hashCode(licensed); result = (31 * result) + (account != null ? System.hashCode(account) : 0); return result; } }
In the example above, I told the generator that
firstName
,lastName
,dob
, andlicensed
are always non-null, but other fields (this also works for properties) can be null. This all leads to a few special behaviors in the generated code:- When a field is of type
String
, it is checked for equivalence usingString.equals()
; otherwiseSystem.equals()
is used. It’s possible that there should be other type-specific exceptions, e.g., forId
. - When a field value can be assumed to be non-null, a direct comparison or call to
System.hashCode()
can be made with no need to check for a null value first. -
When a field value cannot be assumed to be non-null, a series of checks is performed to determine equivalence:
- Check the first operand as non-null and equivalent to the second operand using the type-appropriate check as described above.
- If not, check the second operand as non-null and equivalent to first operand using the type-appropriate check as described above.
- If not, check the operands as equivalent using the
==
operator. This would really only be used to check fornull == null
as the previous checks should have sussed out all non-null scenarios.
It’s also worth talking about this line for a moment:
if (!(o instanceof CodeGenerationTest)) return false;
In a Java implementation, this would be:
if ((o == null) || (getClass() != o.getClass())) return false;
but the latter condition isn’t possible in Apex. As a result, while the check I’ve added would allow subclass comparisons, a type assignability check needs to happen in some form given the very next line casts to the expected type.
As for the
hashCode()
implementation, it’s pretty much stock-standard with null checks as appropriate.So…is anyone aware of a better way to implement these generically in Apex assuming only the system/standard Apex types? I’m inclined to move forward with these implementations and see how it goes, but certainly if there are opportunities to improve the generated code for either/both of these methods before putting it into the wild, all the better. Thanks in advance for any thoughts you might have on this!
- When a field is of type
-
reporter I’ve also implemented getter and/or setter generation for the next build. I’ll be doing this in phases with constructor generation likely being the main focus of the second phase. I want to make sure that’s implemented properly as it’s something I use non-stop in Java myself including all of the nuances around field initialization, etc.
-
reporter Partially delivered in 2.2.8.0. More code generation capabilities coming in the next several builds.
-
reporter - edited description
-
reporter Second phase delivered in 2.2.8.1. The only remaining aspect would be unit test class/stub method generation.
-
reporter - changed status to resolved
Enhancements from
#2418were delivered in 2.2.8.2.With these three phases of work complete, I'm going to declare victory on this one even though I haven't yet implemented anything related to Apex unit test generation. Once I have my head wrapped around what Einstein GPT does/doesn't do in that regard (and the level of availability for that feature), I'll revisit test generation in the context of another enhancement request.
- Log in to comment