Created
July 17, 2021 02:23
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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