Init object from its parent's Builder class in java - java

I have a parent class like :
public abstract class ParentObject {
public abstract String[] fields();
public abstract String tableName();
}
And a child class like this :
public class MyObject extends ParentObject {
String id = "";
String name = "";
public MyObject(Map<String, Object> map){
this.id = map.get("id").toString();
this.name = map.get("name").toString();
}
#Override
public String[] fields() {
return new String[]{"id","name"};
}
#Override
public String tableName() {
return "testTable";
}
}
I want to create a Builder Class in ParentObject to be able to init all the child classes like :
MyObject object = new MyObject.Builder().getById("objectId").build();
I tried using generics but i could not find what i was looking for i need a Builder class like :
public static class Builder{
public Builder(){
}
public Builder getById(){
//some server codes here
return Builder.this;
}
public T build(){
return new T(map);
}
}
I need to know if i can use generics like this :
return new T();
If no, how can i do it ?

Basically you need something like the following:
public <T extends ParentObject> T build(Class<T> targetClass) throws IllegalAccessException, InstantiationException {
T t = targetClass.newInstance();
t.tableName(); // can access the method
return t;
}
You need to pass in the targetClass like build(MyObject.class).

Related

Java generics - iterating through list of uninstantiated classes extending from same parent and call parent method

I have a list of classes with extend a base class
public class Entity{
abstract String getTitle();
}
The child classes are
public class ChildEntityOne extends Entity{
public static final String TITLE= "ABCD";
#Override
public String getTitle() {
return TITLE;;
}
}
public class ChildEntityTwo extends Entity{
public static final String TITLE= "EFGH";
#Override
public String getTitle() {
return TITLE;;
}
}
public class ChildEntityThree extends Entity{
public static final String TITLE= "WXYZ";
#Override
public String getTitle() {
return TITLE;;
}
}
now i'm trying to pass a list of valid classes to a function
which creates an instance from one of the classes from list and returns it
List<?ClassesToChooseFrom?> list = new ArrayList()<>;
list.add(?ChildEntityOne?);
list.add(?ChildEntityTwo?);
Entity result = getInstantiatedClass(list,getKey(),getjsonData())
if(result instanceof ChildEntityOne){
//do something
}else if(result instanceof ChildEntityTwo){
//do somwthing
}
public ?InstantiatedClassObject? getInstantiatedClass(List<?ClassesToChooseFrom?> list,String key,String jsonData){
foreach(?Class? itemclass : list){
if(itemClass.getTitle().equals(key)){
return new GsonBuilder().create().fromJson(jsonData, itemClass);}
}
return null;
}
Ive tried
List<Class<? extends Entity>> classes = new ArrayList<>();
but unable to go further..
You got the beginning right: a list of subclasses of Entity is:
List<Class<? extends Entity>> list = new ArrayList<>();
list.add(ChildEntityOne.class);
list.add(ChildEntityTwo.class);
Then you just need to make getInstantiatedClass use the same types you pass to it:
public Entity getInstantiatedClass(List<Class<? extends Entity>> list, String key, String jsonData) {
for (Class<? extends Entity> itemclass : list) {
if (getTitle(itemClass).equals(key)) {
...
You could make that generic, if you don't want to/need to do anything special for the Entity class.
public <T> T getInstantiatedClass(List<Class<? extends T>> list, String key, String jsonData) {
for (Class<? extends T> itemclass : list) {
...
To extract the value of the static TITLE field from a child entity class you can use:
private String getTitle(Class<?> itemclass) {
try {
return (String) itemclass.getField("TITLE").get(null);
} catch (IllegalAccessException | NoSuchFieldException e) {
return "N/A";
}
}

How can i create new instance of class using field type on runtime

I have this class
public static class ExampleClass {
private ExampleObject exampleObject;
public ExampleObject getExampleObject() {
return exampleObject;
}
public void setExampleObject(ExampleObject exampleObject) {
this.exampleObject = exampleObject;
}
}
public class ExampleObject {
private String exampleProp;
public String getExampleProp() {
return exampleProp;
}
public void setExampleProp(String exampleProp) {
this.exampleProp = exampleProp;
}
}
I created a loop to get field types of ExampleClass. How can I create a new instance using field type on runtime? My field type is:
I try this
Class c = Class.forName(fieldType.getName());
var ins = c.newInstance();
But code throws exception like this
java.lang.NoSuchMethodException: com.example.Main$ExampleObject.<init>()
at java.base/java.lang.Class.getConstructor0(Class.java:3350)
at java.base/java.lang.Class.getDeclaredConstructor(Class.java:2554)
at com.example.CreatorFactory.getCreator(CreatorFactory.java:29)
at com.example.AutoFill.fill(AutoFill.java:15)
at com.example.AutoCreate.<init>(AutoCreate.java:15)
at com.example.AutoCreate.build(AutoCreate.java:23)
at com.example.Main.main(Main.java:7)

Issue With #JsonProperty on Method

I currently have my POJO class as such for deserializing a json source.
public class OpenBuilding extends Building {
#JsonProperty("BuildingPostCode")
#Override
public String getPostcode() {
return super.getPostcode();
}
}
Where the parent class is as such
public abstract class Buidling {
protected String postcode;
public String getPostcode() {
return this.postcode;
}
}
My issue is that the String postcode isn't getting mapped at all. It works when using the annotation on the field. However since it is an inherited field and I have other children of Building, which use different property names for the same data, I cannot have it implemented in that way.
For example:
public class DirectedBuilding extends Building {
#JsonProperty("Pseudo_PostCode")
#Override
public String getPostcode() {
return super.getPostcode();
}
}
Perhaps try defining a constructor with #JsonCreator.
class Parent {
private final String foo;
public Parent(final String foo) {
this.foo = foo;
}
public String getFoo() {
return foo;
}
}
class Child extends Parent {
#JsonCreator
public Child(#JsonProperty("foo") final String foo) {
super(foo);
}
#JsonProperty("foo")
public String getFoo() {
return super.getFoo();
}
}
public static void main(String[] args) throws Exception {
final ObjectMapper objectMapper = new ObjectMapper();
final Child toSerialize = new Child("fooValue");
// Serialize the object to JSON
final String json = objectMapper.writer()
.withDefaultPrettyPrinter()
.writeValueAsString(toSerialize);
// Prints { "foo" : "fooValue" }
System.out.println(json);
// Deserialize the JSON
final Child deserializedChild = objectMapper.readValue(json, Child.class);
// Prints fooValue
System.out.println(deserializedChild.getFoo());
}

Instantiating generic class and implementing generic interface

I have this class:
public DrawItem {
protected String getSeperator() {
return "";
}
.......
// some other methods
}
I've another class which extends DrawItem.
public DrawNumber extends DrawItem {
#Override
protected String getSeperator() {
return "-";
}
}
Now, in a generic class CombinationGenerator<E>, I'm trying to instantiate objects of DrawItem/DrawNumber. As instantiating a generic type is not possible in java (like new E(...)), I've created a Factory interface according to this answer.
public interface DrawItemFactory<E> {
E create(...);
}
Then in the CombinationGenerator<E> class,
public class CombinationGenerator<E> {
DrawItemFactory<E> factory;
public CombinationGenerator<E>(DrawItemFactory<E> factory) {
this.factory = factory;
}
public List<E> generate() {
......
list.add(factory.create(...));
......
}
}
And now the DrawNumber class implements DrawItemFactory<DrawItem> interface.
public DrawItem implements DrawItemFactory<DrawItem> {
protected String getSeperator() {
return "";
}
#Override
public DrawItem create(...) {
return new DrawItem(...);
}
.......
// some other methods
}
And I can create CombinationGenerator<DrawItem> class.
DrawItem drawItem = new DrawItem(...);
CombinationGenerator<DrawItem> generator = new CombinationGenerator<DrawItem>(drawItem);
List<DrawItem> combinations = generator.generate();
So far, everything is fine. But when I try to create a DrawNumber class like this,
public DrawNumber implements DrawItemFactory<DrawNumber> {
....
}
It gives me the following error:
The interface DrawItemFactory cannot be implemented more than once with different arguments: DrawItemFactory<DrawItem> and DrawItemFactory<DrawNumber>
I've tried this solution but I got the same error. Is there any other way to do this?
Instead of using all those factories you could do something like this:
public class CombinationGenerator<E> {
E instance;
public CombinationGenerator(Class<E> clazz) {
Constructor<?> con = clazz.getConstructor();
this.instance = (E) con.newInstance();
}
}
...
CombinationGenerator<DrawNumber> cg = new CombinationGenerator<DrawNumber>(DrawNumber.class);
According to #JB Nizet's comment, I've solved the problem by creating two separate factory classes like this:
public interface ItemFactory<E> {
E create(int[] values);
public static class DrawItemFactory implements ItemFactory<DrawItem> {
#Override
public DrawItem create(int[] values) {
return new DrawItem(values);
}
}
public static class DrawNumberFactory implements ItemFactory<DrawNumber> {
#Override
public DrawNumber create(int[] values) {
return new DrawNumber(values);
}
}
}
In the CombinationGenerator,
public class CombinationGenerator<E> {
ItemFactory<E> factory;
public CombinationGenerator<E>(ItemFactory<E> factory) {
this.factory = factory;
}
public List<E> generate() {
......
list.add(factory.create(...));
......
}
}
And instantiated CombinationGenerator like this:
DrawNumber drawNumber = new DrawNumber();
CombinationGenerator<DrawNumber> generator = new CombinationGenerator<DrawNumber>(new ItemFactory.DrawNumberFactory());
List<DrawNumber> combinations = generator.generate();

Is it possible to parcel a generic class?

I'm trying to create public class MyClass<T extends Parcelable> implements Parcelable. I'm having trouble implementing Parcelable. Is it possible to create a generic class that implements Parcelable? (Note that T is bounded so that it also must implement Parcelable).
I am running into trouble with the fact that the Parcelable interface requires a static variable: public static final Parcelable.Creator<MyParcelable> CREATOR. Thus I cannot do public static final Parcelable.Creator<MyClass<T>> CREATOR because MyParcelable<T> is nonstatic.
André
I had similar issues with implementing Parcelable on a class with a generic, the first issue was the same as what you were experiencing:
Thus I cannot do public static final Parcelable.Creator> CREATOR because MyParcelable is nonstatic.
The second was to read in a Parcelable object you need access to the ClassLoader which cannot be gotten from T due to type erasure.
The class below is an adaption of a class I am using in production which overcomes both issues. Note: I have not tested this class specifically, so let me know if you have any issues.
public class TestModel<T extends Parcelable> implements Parcelable {
private List<T> items;
private String someField;
public List<T> items() {
return items;
}
public void setItems(List<T> newValue) {
items = newValue;
}
public String someField() {
return someField;
}
public void setSomeField(String newValue) {
someField = newValue;
}
//region: Parcelable implementation
public TestModel(Parcel in) {
someField = in.readString();
int size = in.readInt();
if (size == 0) {
items = null;
}
else {
Class<?> type = (Class<?>) in.readSerializable();
items = new ArrayList<>(size);
in.readList(items, type.getClassLoader());
}
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(someField);
if (items == null || items.size() == 0)
dest.writeInt(0);
else {
dest.writeInt(items.size());
final Class<?> objectsType = items.get(0).getClass();
dest.writeSerializable(objectsType);
dest.writeList(items);
}
}
public static final Parcelable.Creator<TestModel> CREATOR = new Parcelable.Creator<TestModel>() {
public TestModel createFromParcel(Parcel in) {
return new TestModel(in);
}
public TestModel[] newArray(int size) {
return new TestModel[size];
}
};
//endregion
}
Write the generic data member class name to the parcel and then read it back in order to create its class loader. Example,
public class MyClass<T> implements Parcelable {
T data;
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(data.getClass().getName());
dest.writeParcelable((Parcelable) data, 0);
}
private MyClass(Parcel in) {
final String className = in.readString();
try {
data = in.readParcelable(Class.forName(className).getClassLoader());
} catch (ClassNotFoundException e) {
Log.e("readParcelable", className, e);
}
}
Yes you can. You just need to store the class name or class loader during the construction of your subclass object and then you can pass it during the read/write operation of the parcelable.
Step by step instructions:
Step 1. Store the class name that extends from your Generic class like this:
public abstract class GenericClass<T> implements Parcelable {
private String className;
Step 2. Any classes that extends from your generic class must specify the class name during its construction like this:
public class MyClass extends GenericClass<MyClass> {
public MyClass () {
super();
setClassName(MyClass.class.getName()); // Generic class setter method
}
Step 3. In your generic class, you can then read/write your class names to getClassLoader() like this:
public abstract class GenericClass<T> implements Parcelable {
private String className;
T myGenericObject;
protected MyClass (Parcel in) {
super(in);
this.className = in.readString();
ClassLoader classLoader;
try {
classLoader = Class.forName(this.className).getClassLoader();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
myGenericObject = in.readParcelable(classLoader);
//... Other class members can go here
}
#Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeString(className);
//... Other class members can go here
}
}
Based on answers above, have created extension functions for this.
fun <T : Parcelable> Parcel.writeGenericParcelable(data: T, flags: Int) {
writeString(data::class.java.name)
writeParcelable(data, flags)
}
fun <T : Parcelable> Parcel.readGenericParcelable(): T {
val className = readString()!!
val classNameLoader = Class.forName(className).classLoader
return readParcelable(classNameLoader)!!
}

Categories

Resources