Dave Chen
Dave Chen

Reputation: 10975

Multiple types in array for initialization

I'm trying to create a lot of objects, but using new ObjectName(...) over and over is not what I'm looking for.

new Obj("String", 0, true);
new Obj("Diff", 9, true);
new Obj("Changed", 2, false);
...

I would like something similar to:

massCreateObj({
    {"String",  0, true },
    {"Diff",    9, true },
    {"Changed", 2, false}
    ...
});

I want to do this because it will be easier to manage for multiple objects, such as:

massCreateObjs({
    "Obj" => {
        {"str", 0, false},
        {"sss", 1, true}
    },
    "Obj2" => {
        {false, "different"},
        {true, "diff"}
    }
});

Otherwise, I would have to flatten this:

new Obj("str", 0, false);
new Obj("sss", 1, true);
new Obj2(false, "different");
new Obj2(true, "diff");

This is just simply not scalable. With an array, I can easily see which objects are being created, and there isn't text repeated (the new object name).

Is there a way I can use Hashmaps, Arrays, or tuples to accomplish this? (Open to other solutions too)

I've taken a look at hashmaps but it's essentially only a K->V setup.

I've also looked at wrappers, but then, it's back to where I've started. If I create a class/interface, I still need to use new class name or whatever, which is what I'm avoiding in the first place.

Upvotes: 0

Views: 65

Answers (6)

Dave Chen
Dave Chen

Reputation: 10975

Here's my solution. I know this isn't codereview, but is this OK?

import java.lang.reflect.Constructor;
import java.util.Array;

public class MassCreate {
    private Array<Obj> objs = new Array<Obj>();

    public MassCreate(Object[][][] components) {
        Constructor<?> construct = null;
        try {
            for (int i = 0; i < components.length; i++)
                for (int i2 = 0; i2 < components[i].length; i2++)
                    if (i2 == 0) {
                        Class<?>[] classes = new Class[components[i][i2].length - 1];
                        for (int i3 = 1; i3 < components[i][i2].length; i3++)
                            classes[i3 - 1] = (Class<?>) components[i][i2][i3];
                        construct = ((Class<?>) components[i][i2][0])
                                .getConstructor(classes);
                    } else
                        objs.add((Obj) construct
                                .newInstance(components[i][i2]));
        } catch (Exception e) {}
    }

    public Obj[] getObjs() {
        Obj[] export = new Obj[objs.size];
        for (int i = 0; i < objs.size; i++)
            export[i] = objs.get(i);
        return export;
    }
}

Now for the fun part, here is where I can spawn new objects using an Object[][][].

Object[][][] components = {
        {
            {Obj.class, String.class, int.class, boolean.class},
            {"test 1", 0, true},
            {"test 2", 3, false}
        },
        {
            {Obj2.class, boolean.class, String.class},
            {true, "hello"},
            {false, "diff"}
        }
};

new MassCreate(components).getObjs(); //Obj2 extends Obj

Here's what it means:

{Obj.class, String.class, int.class, boolean.class}

The first class indicates what to make, and the rest indicate which constructor to use. So for this, the constructor for Obj.class would be:

 public Obj(String str, int num, boolean bool);

The following items in the first level array are objects to be, so:

{"test 1", 0, true},
{"test 2", 3, false}

Would invoke the above constructor twice with these values.

What do you guys think?

Upvotes: 0

Patricia Shanahan
Patricia Shanahan

Reputation: 26185

Rather than specifying the objects in Java, read their parameters from a file. that file can be in any format you like, and can parse. Write Java code to call from the constructor for the object containing the array that reads the file and generates the array from it.

As a variation on this, write a separate program that reads and parses the file, and generates a Java program from it. That way, you could literally copy over initializers like Color.BLUE.

Upvotes: 1

If you want to create the array inline, you can always do

Obj[] objects = new Obj[] {
    new Obj("String", 0, true),
    new Obj("Diff", 9, true),
    new Obj("Changed", 2, false)
};

There's no way to get around using new (or some wrapper factory function that calls it) if you're trying to create a new object.

On a broader scale, by the way, your use case sounds like what you really want is immutable data structure, and you should consider Guava's ImmutableList instead of a bare array.

Upvotes: 0

Deepak Luitel
Deepak Luitel

Reputation: 11

Make an inner class called "Node" if the input data follow specific types e.g. str, int, boolean, str, int, bool and create each Node object and put it in HashMap if object has names or in HashTable incase no names.

Upvotes: 0

k_g
k_g

Reputation: 4464

You can use Reflections, but you'd have to define your own function as follows:

public static <T> Object[] multipleConstructor(Class<T> type,
        Object[]... initargs) {
    ArrayList<T> list = new ArrayList<>();
    @SuppressWarnings("unchecked")
    Constructor<T>[] ctors = (Constructor<T>[]) type.getConstructors();
    for (int i = 0; i < initargs.length; i++) {
        for (Constructor<T> c : ctors) {
            try {
                list.add(c.newInstance(initargs[i]));
                break;
            } catch (Throwable t) {
                continue;
            }
        }
    }
    return list.toArray();
}

Upvotes: 1

iFytil
iFytil

Reputation: 459

If you want to hard code the parameters in your code but want to avoid repeating the "new" keyword, then a simple loop will do.

String[] args1 = {"hello", "bye", "potatoe"};
int[] args2 = {5,2,7};
boolean[] args3 = {true,false,true};

Obj[] objects = new Obj[3];

for (int i = 0; i < 3; i++) {
    objects[i] = new Obj(args1[i], args2[i], args3[i]);
}

Not that this is good code, but the idea can be applied in better ways :)

Upvotes: 1

Related Questions