jcodemodel icon indicating copy to clipboard operation
jcodemodel copied to clipboard

java 16 record ?

Open glelouet opened this issue 3 years ago • 12 comments

are they already present ? If not, are they to be implemented ? if yes, are you working on it or can I work on it ?

glelouet avatar Apr 15 '21 11:04 glelouet

No plans on my side - feel free :)

phax avatar Apr 15 '21 13:04 phax

ok so.

  • add JDefinedClass.record(String name) that creates a JRecord.
  • add class JRecord that extends AbstractJClass.It's not an AbstractJContainer because we can't add class inside. However some code is duplicated ( :( ) from AbstractJContainer or JDefinedClass
  • JRecord contains a linkedMap<String, JFieldVar> of its base methods that are used for the constructor. it's linked because the order defines the creation call. The fields should be considered private final.
  • JRecord also has the method method(String name) and staticMethod(String name) both return a JMethod.
  • JRecord also has the method construct() that creates a constructor, also creates a JMethod
  • for each field specified, the record should have a method with that field name, that returns that field value.

Use Case :

// simple call that should be used most of the time
var record = cm.class("RecordTest").record("Person").withParam(String.class, "name").withParam(String.class, "address");
//more complex as we add a new constructor
var cons2 = record.construct();
var namep = cons2.param(String.class, "name");
cons2.body().add(JExpr.invoke("this").arg(namep).arg(Jexpr.null));

should produce

class RecordTest {
  public record Person(String name, String address){
    public Person(String name) {
      this(name, null);
    }
  }
}

glelouet avatar Apr 15 '21 13:04 glelouet

There also is the base code to be added to the constructor, example

public Person {
  Object.requireNonNull(name);
}

I think this should be a JBlock accessible with _init() in the Jrecord ?

glelouet avatar Apr 15 '21 13:04 glelouet

okay so I think I was wrong, I can create a record as a class so maybe no need to rewrite the whole thing.

The goal is therefore to add the type to the existing EClassType, and then… I guess the following code is a good start ?


import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public record Person(String name, String address) {

	public Person {
		Objects.requireNonNull(name);
	}

	public Person(String name) {
		this(name, null);
	}

	public String familyName() {
		return Stream.of(name.split(" ")).skip(1).collect(Collectors.joining(" "));
	}

	public String firstName() {
		return name.split(" ")[0];
	}

	// singleton

	private static Person nobody = new Person("nobody");

	public static Person nobody() {
		return nobody;
	}

}

It compiles on my computer so being able to describe it programatically seems a good start ?

glelouet avatar Apr 15 '21 14:04 glelouet

I must admit I haven't look at the official specs yet, and just adding to EClassType will not suffice I fear. Are the record types the final specification or are they still in "draft" mode?

phax avatar Apr 15 '21 14:04 phax

They are final for java 16. preview in java 15 which I used. I also thought that adding an EclassType would not be enough, because the fields are generated from the "method call" declaration . But at the same time, it allows static field so maybe the easiest way to do so is to not recreate a new class (especially since it uses code from abstractJcontainer and JDefinedClass)

glelouet avatar Apr 15 '21 14:04 glelouet

https://openjdk.java.net/jeps/395

glelouet avatar Apr 15 '21 14:04 glelouet

ok so I think these are the required steps :

  1. get all the links needed, and use case to evaluate the functionality and its importance. Is that really useful ? we assume it is.
  2. create one or more class to represent what we want to achieve. The "Person" I provided before is an example (just a start).
  3. Define the code we want to add to generate those class. Typically the class JRecord, its methods, etc.
  4. create a test that generates the classes of 2. and implement the code.
  5. define another case to manage when the projected JDK version is <14 OR <16 AND no --enable-preview . In that case, the records are created as final classes with their final fields and a cannonic constructor if not present already, also getters if not present already.

glelouet avatar Apr 16 '21 09:04 glelouet

The example test should provide an executable class, either with a main() or by implementing Runnable, so that it can actually be ran with the in-memory compiler and return a value that would be tested with Assert.assertEqual.

This test should also provide the same result when called as a java12 and therefore the test class should make both calls. (with record as a feature and with record as final immutable)

IMO the enableRecord flag should be present in the JCodemodel, default false untill JCM uses java 16 (in maven)

glelouet avatar Apr 16 '21 11:04 glelouet

https://github.com/glelouet/JCM-test-records/blob/main/src/main/java/com/helger/jcodemodel/target/record/Person.java showcasing the features of records. Not all, eg missing sealed etc.

glelouet avatar Apr 16 '21 15:04 glelouet

Usage of records : records manipulation in JCM would allow to automatically generate immutable class.

In my case, it would be useful as the key type of a cached remote server resource modeling. Example if the remote server proposes a path clients_by_region_id_and_id/42/25 (the client of region 42 and id 25) then the built API should have a cache for the path parameters (region, id) to the compiled resource type eg private final HashMap<KEYTYPE, WeakReference<Holder<RESOURCETYPE>>> cache_clients_by_region_and_id therefore a record would alleviate the creation of such a KEYTYPE class. Even the RESOURCETYPE would gain from the change, allowing to never have the data modified by user (the client API is used by several users). The KEYTYPE is only managed by the API access , typically behind a corresponding method call that creates the KEYTYPE transparently.

https://github.com/guiguilechat/JCELechat/blob/master/model/esi/JCESI-compiled/src/generated/java/fr/guiguilechat/jcelechat/model/jcesi/compiler/compiled/connected/Characters.java#L200 Here is an example where I have a cache that creates its own key . In that case, both the K_0_int_Integer class (key) and R_get_characters_character_id_calendar (resource representation) would benefit from being created as records.

Later, because java16 is not here yet :P

glelouet avatar Apr 16 '21 15:04 glelouet

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Jul 21 '21 01:07 stale[bot]