Skip to content

Instantly share code, notes, and snippets.

@bfu4
Created July 17, 2021 02:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bfu4/30425ade533efc95edc864bd53d92945 to your computer and use it in GitHub Desktop.
Save bfu4/30425ade533efc95edc864bd53d92945 to your computer and use it in GitHub Desktop.
instance building utilizing generics (and bloat) to try to make any instance of a class as possible
/**
* Base abstraction of a pair.
*
* @param <K> key
* @param <V> value
*/
public interface Entry<K, V> {
/**
* Get key.
* @return key
*/
K getKey();
/**
* Get the value.
* @return value
*/
V getValue();
}
import org.jetbrains.annotations.NotNull;
import java.beans.ConstructorProperties;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Arrays;
/**
* The instance builder may create an instance of an object.
* One of the original known feats to this builder is <a href="https://openjdk.java.net/jeps/218">JEP-218</a>.
* This is bypassed utilizing the method {@link InstanceBuilder#allPrimitivesOf(Class[])}, however,
* will translate all box type classes into primitive classes.
* @param <T> builder type constraints.
* @author bfu4
* @since v1.0
*/
final class InstanceBuilder<T> {
/**
* Integer box type association.
*/
private static final Association INTEGER = new Association(Integer.class, int.class);
/**
* Long box type association.
*/
private static final Association LONG = new Association(Long.class, long.class);
/**
* Boolean box type association.
*/
private static final Association BOOLEAN = new Association(Boolean.class, boolean.class);
/**
* Short box type association.
*/
private static final Association SHORT = new Association(Short.class, short.class);
/**
* Float box type association.
*/
private static final Association FLOAT = new Association(Float.class, float.class);
/**
* Double box type association.
*/
private static final Association DOUBLE = new Association(Double.class, double.class);
/**
* Byte box type association.
*/
private static final Association BYTE = new Association(Byte.class, byte.class);
/**
* All box type associations.
*/
private static final ArrayList<Association> BOXED_TYPE_ASSOCIATIONS = new ArrayList<>(Arrays.asList(INTEGER, LONG, BOOLEAN, SHORT, FLOAT, DOUBLE, BYTE));
/**
* Type of instance to build.
*/
private final Class<T> type;
/**
* Constructor.
*/
private Constructor<T> constructor;
/**
* The arguments.
*/
private Object[] args;
/**
* Create a typed builder using an existing builder.
* @param type type
*/
protected InstanceBuilder(@NotNull final Class<T> type) {
this.type = type;
}
/**
* Add arguments to the instantiation call.
* @param args arguments
* @return builder
*/
public InstanceBuilder<T> withArgs(final Object... args) {
this.args = args;
this.constructor = getConstructorOf(type, args);
return this;
}
/**
* Build an instance.
* @return instance
* @throws RuntimeException if an error has occurred
*/
public T build() throws RuntimeException {
if (this.constructor == null) {
throw new RuntimeException("constructor is null");
}
Constructor<T> constructor = this.constructor;
try {
return constructor.newInstance(this.args);
// Catch all exceptions.
} catch (Throwable t) {
throw new RuntimeException("could not instantiate class!");
}
}
/**
* Get an accessible constructor of the provided type with arguments.
* @param type type
* @param args args
* @return constructor
*/
private Constructor<T> getConstructorOf(final Class<T> type, final Object... args) {
Class<?>[] types = typesOf(args);
Constructor<T> constructor;
try {
constructor = type.getDeclaredConstructor(types);
} catch (NoSuchMethodException ex) {
types = allPrimitivesOf(types);
// Try again with primitives.
try {
constructor = type.getDeclaredConstructor(types);
} catch (NoSuchMethodException e) {
return null;
}
}
constructor.setAccessible(true);
return constructor;
}
/**
* Get the types of parameters.
* @param args parameters
* @return types
*/
private Class<?>[] typesOf(final Object... args) {
if (args == null) {
return null;
}
return Arrays.stream(args)
.map(Object::getClass)
.toArray(Class[]::new);
}
/**
* Translate a list of classes into the primitive class types if needed.
* @param types types
* @return list of classes with the boxed types translated to primitives
*/
private Class<?>[] allPrimitivesOf(final Class<?>... types) {
if (types == null) {
return null;
}
return Arrays
.stream(types)
.map(InstanceBuilder::getPrimitiveOf)
.toArray(Class[]::new);
}
/**
* Get the primitive class of a specified class.
* @param clazz class to get a primitive type of.
* @return primitive type, otherwise itself
*/
private static Class<?> getPrimitiveOf(final Class<?> clazz) {
Association association = BOXED_TYPE_ASSOCIATIONS
.stream()
.filter(assoc -> assoc.getKey().equals(clazz))
.findAny()
.orElse(null);
if (association != null) {
return association.associate;
}
return clazz;
}
/**
* An association. Similar to a pair.
*/
private static final class Association implements Entry<Class<?>, Class<?>> {
/**
* Key.
*/
private final Class<?> association;
/**
* Value.
*/
private final Class<?> associate;
/**
* Create an association (pair).
* @param key key
* @param value value
*/
@ConstructorProperties({"key", "value"})
Association(final Class<?> key, final Class<?> value) {
this.association = key;
this.associate = value;
}
/**
* Get the key.
* @return association, or key.
*/
@Override
public Class<?> getKey() {
return association;
}
/**
* Get the value.
* @return associate, or value.
*/
@Override
public Class<?> getValue() {
return associate;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment