byte-buddy
byte-buddy copied to clipboard
Cannot invoke public constructor
This fails with "Exception in thread "main" java.lang.IllegalStateException: Cannot invoke public java.util.HashSet() on private final java.util.Set org.foo.bytebuddyctor.Entity2$Setter$Tracking$ByteBuddy$Subclass._modified$Fields$Tracker".
What am I doing wrong?
package org.foo.bytebuddyctor;
import static org.foo.bytebuddyctor.Constants.MODIFIED_FIELDS_TRACKER_FIELD_NAME;
import static org.foo.bytebuddyctor.Constants.MODIFIED_FIELDS_TRACKER_SUBCLASS_NAMING_STRATEGY;
import static org.foo.bytebuddyctor.Utils.getModifiedFieldsTracker;
import java.util.HashSet;
import java.util.Set;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.modifier.FieldManifestation;
import net.bytebuddy.description.modifier.SynchronizationState;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.Transformer;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
import net.bytebuddy.implementation.MethodCall;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.SuperMethodCall;
import net.bytebuddy.matcher.ElementMatchers;
// This is copied from the answer given to the question in
// https://github.com/raphw/byte-buddy/issues/1634, but it blows up with:
// Exception in thread "main" java.lang.IllegalStateException: Cannot invoke public
// java.util.HashSet() on private final java.util.Set
// org.foo.bytebuddyctor.Entity2$Setter$Tracking$ByteBuddy$Subclass._modified$Fields$Tracker
public class AddConstructorSecondAttempt {
public static void main(String[] args)
throws InstantiationException, IllegalAccessException, NoSuchMethodException {
Class<?> setterTrackingEntityClass =
new ByteBuddy()
.with(MODIFIED_FIELDS_TRACKER_SUBCLASS_NAMING_STRATEGY)
.subclass(Entity2.class, ConstructorStrategy.Default.NO_CONSTRUCTORS)
.defineField(
MODIFIED_FIELDS_TRACKER_FIELD_NAME,
TypeDescription.Generic.Builder.parameterizedType(Set.class, String.class).build(),
Visibility.PRIVATE,
FieldManifestation.FINAL)
.defineConstructor()
.intercept(
MethodCall.invoke(Entity2.class.getConstructor())
.onSuper()
.andThen(
MethodCall.invoke(HashSet.class.getConstructor())
.onField(MODIFIED_FIELDS_TRACKER_FIELD_NAME)))
.method(ElementMatchers.isSetter())
.intercept(
MethodDelegation.to(SetterInterceptor.class).andThen(SuperMethodCall.INSTANCE))
.transform(Transformer.ForMethod.withModifiers(SynchronizationState.SYNCHRONIZED))
.make()
.load(Entity2.class.getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
.getLoaded();
Entity2 entity = (Entity2) setterTrackingEntityClass.newInstance();
entity.setFoo("foo");
Set<String> modifiedFieldsTracker = getModifiedFieldsTracker(entity);
System.out.println(modifiedFieldsTracker.iterator().next());
}
}
You want to use setsField, not onField. Also, you likely want to make the constructor public in defineConstructor.
How do I specify the field that it sets in setsField, when that field is being dynamically added in the same builder chain?
Create a latent field description and supply it.
This works:
.defineConstructor(Visibility.PUBLIC)
.intercept(
MethodCall.invoke(getConstructor(entityClass))
.onSuper()
.andThen(
FieldAccessor.ofField(MODIFIED_FIELDS_TRACKER_FIELD_NAME)
.setsValue(new HashSet<String>())))